Il pacchetto @solana-commerce/sdk fornisce hook React per costruire esperienze
di pagamento Solana personalizzate. Questi hook offrono il pieno controllo sui
trasferimenti di SOL e token SPL con gestione dello stato integrata, logica di
ripetizione automatica, gestione degli errori e helper per l'interfaccia utente.
Sotto il cofano, l'SDK utilizza TanStack Query per
il caching e la gestione dello stato,
@solana/kit per le primitive Solana, e si
integra perfettamente con @solana-commerce/connector per la connessione al
wallet.
Installazione
pnpm add @solana-commerce/sdk
Configurazione del Provider
ArcProvider
Il ArcProvider è il provider principale che inizializza il client RPC Solana,
gestisce la configurazione della rete e fornisce connettività blockchain a tutti
gli hook. Deve racchiudere tutti i componenti che utilizzano gli hook dell'SDK.
Props
config(ArcWebClientConfig) - Oggetto di configurazione per il client Arcchildren(ReactNode) - Componenti figli che avranno accesso agli hookqueryClient(QueryClient, opzionale) - Client TanStack Query personalizzato. Se non fornito, viene creata automaticamente un'istanza predefinita.
ArcWebClientConfig
Configurazione per il client Arc che controlla la connettività RPC e i livelli di commitment.
Campi Obbligatori
Il provider si integra automaticamente con @solana-commerce/connector tramite
l'hook useConnectorClient, quindi non è necessaria alcuna configurazione
esplicita del connettore quando utilizzato all'interno di un
ConnectorProvider.
Campi Opzionali
-
network('mainnet' | 'devnet' | 'testnet') - Rete Solana a cui connettersi. Predefinito:'mainnet'. -
rpcUrl(string) - URL dell'endpoint RPC personalizzato. Se non fornito, utilizza endpoint pubblici per la rete selezionata. -
commitment('processed' | 'confirmed' | 'finalized') - Livello di conferma delle transazioni. Predefinito:'confirmed'. -
debug(boolean) - Abilita il logging dettagliato nella console per il debug. Predefinito:false. -
autoConnect(boolean) - Si connette automaticamente al wallet quando il componente viene montato. Predefinito:true. -
storage(Storage) - Adattatore di archiviazione personalizzato per persistere le preferenze del portafoglio. Deve implementare:getItem(key: string): string | nullsetItem(key: string, value: string): voidremoveItem(key: string): void
Predefinito:
window.localStoragequando disponibile (solo browser). Utilizzalo per React Native (AsyncStorage) o archiviazione personalizzata compatibile con SSR.
Integrazione con ConnectorProvider
Il provider si integra con ConnectorProvider da @solana-commerce/connector.
Avvolgi sempre la tua app con ConnectorProvider prima di ArcProvider:
import { ConnectorProvider } from "@solana-commerce/connector";import { ArcProvider } from "@solana-commerce/sdk";function App() {return (<ConnectorProvider config={{ autoConnect: true }}><ArcProvider config={{ network: "mainnet", commitment: "confirmed" }}><YourApp /></ArcProvider></ConnectorProvider>);}
Hook Principali
useTransferSOL
Hook per trasferire SOL con logica di retry automatica, gestione dello stato e funzioni helper per l'interfaccia utente. Costruito su TanStack Query per il caching e la deduplicazione delle richieste.
Firma
function useTransferSOL(initialToInput?: string,initialAmountInput?: string): UseTransferSOLReturn;
Parametri
initialToInput(string, opzionale) - Valore iniziale per l'input dell'indirizzo del destinatario. Utile per precompilare i moduli.initialAmountInput(string, opzionale) - Valore iniziale per l'input dell'importo in SOL. Utile per precompilare i moduli.
Valore di Ritorno
interface UseTransferSOLReturn {// Core transfer functiontransferSOL: (options: TransferSOLOptions) => Promise<TransferSOLResult>;// StateisLoading: boolean;error: Error | null;data: TransferSOLResult | null;reset: () => void;// UI HelperstoInput: string;amountInput: string;setToInput: (value: string) => void;setAmountInput: (value: string) => void;handleToInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;handleAmountInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;handleSubmit: (event?: {preventDefault?: () => void;}) => Promise<TransferSOLResult | undefined>;transferFromInputs: () => Promise<TransferSOLResult | undefined>;}
Funzione Principale
transferSOL- Avvia un trasferimento di SOL. Restituisce una promise che si risolve quando la transazione viene confermata on-chain.
Proprietà di Stato
-
isLoading(boolean) -truementre la transazione è in elaborazione (firma, invio, conferma). Utilizzala per indicatori di caricamento e stati dei pulsanti. -
error(Error | null) - Oggetto errore se la transazione è fallita in qualsiasi fase.nullquando non c'è errore. Gli errori includono rifiuti del portafoglio, saldo insufficiente, errori di rete, ecc. -
data(TransferSOLResult | null) - Oggetto risultato quando la transazione ha successo. Contiene firma, indirizzi, importi e metadati della blockchain.nullprima del primo trasferimento riuscito. -
reset(() => void) - Resetta lo stato della mutation, cancellandoerroredata. Utile per flussi di retry o per resettare i moduli dopo il completamento.
Proprietà e Metodi Helper per l'UI
L'hook fornisce gestione dello stato integrata per gli input dei moduli:
-
toInput/setToInput- Stato controllato per il campo di input dell'indirizzo del destinatario -
amountInput/setAmountInput- Stato controllato per il campo di input dell'importo (in SOL, non lamport) -
handleToInputChange- Gestore onChange pre-associato per l'input del destinatario:<input onChange={handleToInputChange} /> -
handleAmountInputChange- Gestore onChange pre-associato per l'input dell'importo:<input onChange={handleAmountInputChange} /> -
transferFromInputs- Metodo di convenienza che trasferisce SOL utilizzando i valori correnti ditoInputeamountInput. Converte automaticamente l'importo da SOL a lamport. -
handleSubmit- Gestore di invio del form che chiamatransferFromInputs()e previene il comportamento predefinito del form. Da utilizzare con<form onSubmit={handleSubmit}>.
Opzioni
interface TransferSOLOptions {to: string | Address; // Recipient wallet addressamount: bigint; // Amount in lamports (1 SOL = 1,000,000,000 lamports)from?: string | Address; // Optional sender address (defaults to connected wallet)}
-
to(obbligatorio) - Indirizzo Solana del destinatario. Può essere una stringa o di tipoAddressda@solana/kit. -
amount(obbligatorio) - Importo del trasferimento in lamport (non SOL). Deve essere unbigint. UtilizzareBigInt()o la notazione letterale:1_000_000_000n= 1 SOL. -
from(facoltativo) - Indirizzo del mittente. Se non fornito, utilizza l'indirizzo del wallet connesso. Richiesto solo per casi d'uso avanzati (ad es., firma per un account diverso).
Risultato
Il risultato include i metadati della transazione, compresi i dettagli del trasferimento e la firma della transazione:
interface TransferSOLResult {signature: string; // Transaction signature (base58)amount: bigint; // Amount transferred in lamportsfrom: Address; // Sender addressto: Address; // Recipient addressblockTime?: number; // Unix timestamp when transaction was processedslot?: number; // Slot number where transaction was confirmed}
Architettura Interna
Transaction Builder: L'hook utilizza un transaction builder condiviso che:
- Recupera blockhash aggiornati per ogni transazione
- Costruisce messaggi di transazione ottimizzati con commissioni minime
- Firma le transazioni utilizzando il wallet connesso
- Invia e conferma le transazioni in un unico flusso
Invalidazione della Cache: Al completamento con successo del trasferimento, l'hook invalida automaticamente le cache di TanStack Query per:
- Saldo del mittente (indirizzo
from) - Saldo del destinatario (indirizzo
to)
Ciò garantisce che qualsiasi componente che visualizza i saldi (ad es., tramite
useArcClient) effettui automaticamente un nuovo recupero e si aggiorni senza
intervento manuale.
Conversione Precisa dell'Importo: Quando si utilizza transferFromInputs(),
l'importo viene convertito da SOL a lamport utilizzando aritmetica basata su
stringhe per evitare errori di precisione in virgola mobile. La conversione:
- Convalida il formato di input (rifiuta numeri negativi e non validi)
- Gestisce fino a 9 cifre decimali (1 lamport = 0,000000001 SOL)
- Tronca o aggiunge zeri ai valori frazionari secondo necessità
- Genera errori descrittivi per input non validi
useTransferToken
Come useTransferSOL, questo hook viene utilizzato per trasferire token SPL.
Oltre a gestire i trasferimenti, l'hook gestisce anche la creazione automatica
dell'Associated Token Account (ATA) quando necessario.
Firma
function useTransferToken(initialMintInput?: string,initialToInput?: string,initialAmountInput?: string): UseTransferTokenReturn;
Parametri
initialMintInput(string, facoltativo) - Indirizzo iniziale del mint del token. Utile per trasferimenti di token fissi.initialToInput(string, facoltativo) - Indirizzo iniziale del destinatario.initialAmountInput(string, facoltativo) - Importo iniziale nelle unità base del token (considerando i decimali).
Valore Restituito
interface UseTransferTokenReturn {// Core transfer functiontransferToken: (options: TransferTokenOptions) => Promise<TransferTokenResult>;// StateisLoading: boolean;error: Error | null;data: TransferTokenResult | null;reset: () => void;// UI HelpersmintInput: string;toInput: string;amountInput: string;setMintInput: (value: string) => void;setToInput: (value: string) => void;setAmountInput: (value: string) => void;handleMintInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;handleToInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;handleAmountInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;handleSubmit: (event?: {preventDefault?: () => void;}) => Promise<TransferTokenResult | undefined>;transferFromInputs: () => Promise<TransferTokenResult | undefined>;}
Il valore restituito è simile a useTransferSOL ma include uno stato
mintInput aggiuntivo per la selezione del token.
Opzioni
interface TransferTokenOptions {mint: string | Address; // Token mint addressto: string | Address; // Recipient wallet addressamount: bigint; // Amount in token's smallest unitfrom?: string | Address; // Optional sender (defaults to connected wallet)createAccountIfNeeded?: boolean; // Auto-create recipient's ATA (default: true)retryConfig?: TransferRetryConfig; // Optional retry configuration}
Campi Obbligatori
-
mint- Indirizzo del mint del token SPL. Ad esempio:- USDC:
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' - USDT:
'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'
- USDC:
-
to- Indirizzo del wallet del destinatario (non il suo token account). L'hook deriva automaticamente l'Associated Token Account corretto. -
amount- Importo del trasferimento nell'unità più piccola del token. Deve tenere conto dei decimali del token:- USDC (6 decimali):
1_000_000n= 1 USDC - Token wrapped SOL (9 decimali):
1_000_000_000n= 1 token
- USDC (6 decimali):
Campi Facoltativi
-
from- Indirizzo del wallet del mittente. Predefinito: wallet connesso. -
createAccountIfNeeded(predefinito:true) - Se il destinatario non ha un token account per questo mint, crealo automaticamente come parte della transazione. Quando èfalse, il trasferimento fallirà se l'account del destinatario non esiste.Nota: La creazione di un token account costa ~0,00203 SOL. Questo viene pagato dal mittente.
-
retryConfig- Configurazione per il tentativo automatico in caso di scadenza del blockhash. Vedi Configurazione Tentativi.
Risultato
Il risultato include i metadati della transazione inclusi i dettagli del trasferimento e la firma della transazione:
interface TransferTokenResult {signature: string; // Transaction signaturemint: Address; // Token mint addressamount: bigint; // Amount transferredfrom: Address; // Sender wallet addressto: Address; // Recipient wallet addressfromTokenAccount: Address; // Sender's token accounttoTokenAccount: Address; // Recipient's token accountcreatedAccount?: boolean; // Whether recipient's ATA was createdblockTime?: number; // Transaction timestampslot?: number; // Block slot number}
Configurazione Tentativi
L'hook include una logica sofisticata di tentativi per gestire la scadenza del blockhash, che si verifica comunemente durante la congestione della rete.
interface TransferRetryConfig {maxAttempts?: number; // Max retry attempts (default: 3)baseDelay?: number; // Base delay in ms (default: 1000)backoffMultiplier?: number; // Backoff multiplier (default: 1)}
-
maxAttempts- Numero massimo di tentativi di transazione. Ogni tentativo recupera un blockhash nuovo. Predefinito:3. -
baseDelay- Ritardo in millisecondi prima del primo tentativo. Predefinito:1000(1 secondo). -
backoffMultiplier- Moltiplicatore di backoff esponenziale. Ogni tentativo attendebaseDelay * (backoffMultiplier ^ attemptNumber)millisecondi.1= backoff lineare (1s, 1s, 1s)1.5= backoff esponenziale (1s, 1,5s, 2,25s)2= esponenziale aggressivo (1s, 2s, 4s)
Come Funzionano i Tentativi:
- Primo Tentativo: La transazione viene costruita con il blockhash corrente e inviata
- Scadenza Blockhash: Se il blockhash diventa obsoleto prima della conferma, Solana rifiuta la transazione
- Tentativo Automatico: L'hook rileva la scadenza, recupera un blockhash nuovo, ricostruisce la transazione e la invia nuovamente
- Backoff Esponenziale: Ogni tentativo attende più a lungo per evitare la congestione della rete
- Errore Finale: Dopo
maxAttempts, generaBlockhashExpirationErrorcon contesto
Quando i Tentativi Non Si Attivano:
- Gli errori non legati al blockhash (fondi insufficienti, account non validi, ecc.) generano immediatamente un'eccezione senza riprovare
- Solo gli errori di scadenza del blockhash attivano il meccanismo di tentativi
Architettura Interna
Gestione ATA:
- Deriva gli Associated Token Account in modo deterministico usando
findAssociatedTokenPda(Nota: al momento è supportato solo il Token Program) - Verifica se il mittente ha un token account (fallisce immediatamente se il mittente non possiede il token)
- Verifica se il destinatario ha un token account (lo crea se necessario e
createAccountIfNeeded: true) - I controlli degli account vengono eseguiti solo al primo tentativo per evitare chiamate RPC ridondanti durante i tentativi
Invalidazione della Cache: In caso di successo, invalida le cache di TanStack Query per:
- Saldo token del mittente per questo mint
- Saldo token del destinatario per questo mint
- Dati account correlati
Questo mantiene automaticamente sincronizzati tutti i componenti UI che mostrano i saldi.
useArcClient
Hook per accedere al client RPC Solana sottostante, allo stato del wallet e alla configurazione di rete. Si tratta di un hook di livello inferiore per casi d'uso avanzati che richiedono accesso diretto all'RPC.
Firma
function useArcClient(): ArcClientSnapshot;
Valore Restituito
interface ArcClientSnapshot {// Wallet Statewallet: {address: Address | null;signer: TransactionSigner | null;};// Network Configurationnetwork: {cluster: "mainnet" | "devnet" | "testnet";rpcUrl: string;};// Client Configurationconfig: ArcWebClientConfig;// Actionsselect: (walletName: string) => Promise<void>;disconnect: () => Promise<void>;selectAccount: (accountAddress: Address) => Promise<void>;}
Stato
Il ArcClientSnapshot estende il ArcWebClient che fornisce accesso a:
- stato del wallet (indirizzo, firmatario, wallet disponibili, funzionalità e stato del wallet)
- configurazione di rete (endpoint RPC, cluster Solana)
Casi d'Uso
Query RPC Dirette:
import { useArcClient } from "@solana-commerce/sdk";import { getSharedRpc } from "@solana-commerce/sdk/core/rpc-manager";import { address } from "@solana/kit";function AccountBalance() {const { network, wallet } = useArcClient();const [balance, setBalance] = useState<bigint | null>(null);useEffect(() => {if (!wallet.address) return;const rpc = getSharedRpc(network.rpcUrl);async function fetchBalance() {const result = await rpc.getBalance(wallet.address).send();setBalance(result);}fetchBalance();}, [wallet.address, network.rpcUrl]);if (!wallet.address) return <div>Connect wallet to see balance</div>;return <div>Balance: {(Number(balance) / 1e9).toFixed(4)} SOL</div>;}
Componenti Consapevoli della Rete:
function NetworkIndicator() {const { network } = useArcClient();return (<div><span>Network: {network.cluster}</span>{network.canAirdrop && <button onClick={handleAirdrop}>Airdrop</button>}</div>);}
Rendering Condizionale Basato sul Wallet:
function SendButton() {const { wallet } = useArcClient();const { transferSOL, isLoading } = useTransferSOL();if (!wallet.address) {return <div>Connect wallet to send SOL</div>;}return (<buttononClick={() =>transferSOL({to: "recipient-address",amount: BigInt(1_000_000_000)})}disabled={isLoading}>{isLoading ? "Sending..." : "Send 1 SOL"}</button>);}
Is this page helpful?