Strumenti di verifica

I token arrivano nel tuo wallet nel momento in cui una transazione viene confermata. Non è richiesta alcuna azione da parte del destinatario. Solana incrementa atomicamente il saldo del token account del destinatario e decrementa il saldo del mittente. In questa guida, copriamo alcuni strumenti utili per comprendere il saldo del tuo token account e monitorare i pagamenti in entrata.

Interrogare il saldo dei token

Controlla il tuo saldo in stablecoin utilizzando il metodo RPC getTokenAccountBalance:

import { createSolanaRpc, address, type Address } from "@solana/kit";
import {
findAssociatedTokenPda,
TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
async function getBalance(walletAddress: Address) {
// Derive the token account address
const [ata] = await findAssociatedTokenPda({
mint: USDC_MINT,
owner: walletAddress,
tokenProgram: TOKEN_PROGRAM_ADDRESS
});
// Query balance via RPC
const { value } = await rpc.getTokenAccountBalance(ata).send();
return {
raw: BigInt(value.amount), // "1000000" -> 1000000n
formatted: value.uiAmountString, // "1.00"
decimals: value.decimals // 6
};
}

Monitorare i trasferimenti in entrata

Iscriviti al tuo token account per ricevere notifiche di pagamento in tempo reale utilizzando il metodo RPC accountNotifications:

const rpcSubscriptions = createSolanaRpcSubscriptions(
"wss://api.mainnet-beta.solana.com"
);
async function watchPayments(
tokenAccountAddress: Address,
onPayment: (amount: bigint) => void
) {
const abortController = new AbortController();
const subscription = await rpcSubscriptions
.accountNotifications(tokenAccountAddress, {
commitment: "confirmed",
encoding: "base64"
})
.subscribe({ abortSignal: abortController.signal });
let previousBalance = 0n;
for await (const notification of subscription) {
const [data] = notification.value.data;
const dataBytes = getBase64Codec().encode(data);
const token = getTokenCodec().decode(dataBytes);
if (token.amount > previousBalance) {
const received = token.amount - previousBalance;
onPayment(received);
}
previousBalance = token.amount;
}
abortController.abort();
}

Nota che qui stiamo utilizzando le RPC Subscriptions e una connessione websocket alla rete Solana.

Ogni notifica contiene una stringa codificata in base64 dei dati del token account. Poiché sappiamo che l'account che stiamo osservando è un token account, possiamo decodificare i dati utilizzando il metodo getTokenCodec dal package @solana-program/token.

Nota che per le applicazioni in produzione, dovresti considerare una soluzione di streaming più robusta. Alcune opzioni includono:

Analizzare la cronologia delle transazioni

Solana dispone di metodi RPC che ti consentono di recuperare la cronologia delle transazioni di un account (getSignaturesForAddress) e ottenere i dettagli di una transazione (getTransaction). Per analizzare la cronologia delle transazioni, recuperiamo le firme recenti per il nostro token account, quindi recuperiamo i saldi dei token pre/post di ciascuna transazione. Confrontando il saldo del nostro ATA prima e dopo ogni transazione, possiamo determinare l'importo del pagamento e la direzione (in entrata vs in uscita).

async function getRecentPayments(
tokenAccountAddress: Address,
limit = 100
): Promise<Payment[]> {
const signatures = await rpc
.getSignaturesForAddress(tokenAccountAddress, { limit })
.send();
const payments: ParsedPayment[] = [];
for (const sig of signatures) {
const tx = await rpc
.getTransaction(sig.signature, { maxSupportedTransactionVersion: 0 })
.send();
if (!tx?.meta?.preTokenBalances || !tx?.meta?.postTokenBalances) continue;
// Find our ATA's index in the transaction
const accountKeys = tx.transaction.message.accountKeys;
const ataIndex = accountKeys.findIndex((key) => key === ata);
if (ataIndex === -1) continue;
// Compare pre/post balances for our ATA
const pre = tx.meta.preTokenBalances.find(
(b) => b.accountIndex === ataIndex && b.mint === USDC_MINT
);
const post = tx.meta.postTokenBalances.find(
(b) => b.accountIndex === ataIndex && b.mint === USDC_MINT
);
const preAmount = BigInt(pre?.uiTokenAmount.amount ?? "0");
const postAmount = BigInt(post?.uiTokenAmount.amount ?? "0");
const diff = postAmount - preAmount;
if (diff !== 0n) {
payments.push({
signature: sig.signature,
timestamp: tx.blockTime,
amount: diff > 0n ? diff : -diff,
type: diff > 0n ? "incoming" : "outgoing"
});
}
}
return payments;
}

Per identificare la controparte, puoi scansionare i saldi dei token della transazione per trovare un altro account il cui saldo è cambiato nella direzione opposta—se hai ricevuto fondi, cerca un account il cui saldo è diminuito dello stesso importo.

Poiché i trasferimenti di token SPL possono esistere oltre i semplici pagamenti tra utenti, questo approccio potrebbe produrre alcune transazioni che non sono pagamenti. Una buona alternativa qui è utilizzare i memo.

Limitazioni dell'analisi dei saldi pre/post

L'approccio sopra descritto funziona bene per flussi di pagamento semplici. Tuttavia, le aziende che elaborano pagamenti su larga scala spesso necessitano di dati più granulari e in tempo reale:

  • Suddivisione per istruzione: Una singola transazione può contenere più trasferimenti. I saldi pre/post mostrano solo la variazione netta, non i singoli trasferimenti.
  • Transazioni multi-parte: Le transazioni complesse (swap, pagamenti batch) coinvolgono più account. Le differenze di saldo non rivelano il flusso completo dei fondi.
  • Requisiti di audit: La conformità finanziaria spesso richiede la ricostruzione delle sequenze di trasferimento esatte, non solo i saldi finali.

Per i sistemi di produzione che gestiscono volumi elevati, considera soluzioni di indicizzazione dedicate che analizzano le singole istruzioni di trasferimento e forniscono dettagli a livello di transazione.

Riconciliare i pagamenti con i memo

Quando i mittenti includono memo (ID fattura, numeri d'ordine), puoi estrarli dal messaggio della transazione utilizzando il metodo RPC getTransaction e la codifica jsonParsed:

function extractMemos(transaction): string | null {
const { instructions } = transaction.transaction.message;
let memos = "";
for (const instruction of instructions) {
if (instruction.program !== "spl-memo") continue;
memos += instruction.parsed + "; ";
}
return memos;
}
async function getTransactionMemo(
signature: Signature
): Promise<string | null> {
const tx = await rpc
.getTransaction(signature, {
maxSupportedTransactionVersion: 0,
encoding: "jsonParsed"
})
.send();
if (!tx) return null;
return extractMemos(tx);
}

Protezioni

Alcuni scenari di errore da evitare:

  • Fidarsi del frontend. Una pagina di checkout dice "pagamento completato"—ma la transazione è effettivamente arrivata? Verifica sempre lato server interrogando l'RPC. Le conferme frontend possono essere falsificate.

  • Agire sullo stato "processed". Le transazioni Solana attraversano tre fasi: processed → confirmed → finalized. Una transazione "processed" può ancora essere scartata durante i fork. Attendi "confirmed" (1-2 secondi) prima di spedire gli ordini, o "finalized" (~13 secondi) per transazioni di alto valore.

  • Ignorare il mint. Chiunque può creare un token chiamato "USDC". Verifica che il mint del token account corrisponda all'indirizzo del mint della stablecoin reale e al programma token, non solo al nome del token.

  • Doppio adempimento. Il tuo webhook si attiva, spedisci l'ordine. Interruzione di rete, il webhook si attiva di nuovo. Ora hai spedito due volte. Memorizza le firme delle transazioni elaborate e controlla prima di procedere all'adempimento.

Is this page helpful?

Indice

Modifica Pagina

Gestito da

© 2026 Solana Foundation.
Tutti i diritti riservati.
Rimani Connesso