Token'lar, bir işlem onaylandığı anda cüzdanınıza ulaşır. Alıcı tarafından herhangi bir işlem yapılması gerekmez. Solana, alıcının token hesap bakiyesini atomik olarak artırır ve gönderenin bakiyesini azaltır. Bu kılavuzda, token hesap bakiyenizi anlamak ve gelen ödemeleri izlemek için yararlı araçları ele alıyoruz.
Token bakiyesini sorgulama
Stablecoin bakiyenizi
getTokenAccountBalance RPC yöntemi
kullanarak kontrol edin:
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 addressconst [ata] = await findAssociatedTokenPda({mint: USDC_MINT,owner: walletAddress,tokenProgram: TOKEN_PROGRAM_ADDRESS});// Query balance via RPCconst { value } = await rpc.getTokenAccountBalance(ata).send();return {raw: BigInt(value.amount), // "1000000" -> 1000000nformatted: value.uiAmountString, // "1.00"decimals: value.decimals // 6};}
Gelen transferleri izleme
Gerçek zamanlı ödeme bildirimleri için token hesabınıza accountNotifications
RPC yöntemi kullanarak abone olun:
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();}
Burada RPC aboneliklerini ve Solana ağına websocket bağlantısı kullandığımızı unutmayın.
Her bildirim, token hesap verilerinin base64 kodlu bir dizesini içerir.
Baktığımız hesabın bir token hesabı olduğunu bildiğimiz için, verileri
@solana-program/token paketindeki getTokenCodec yöntemi kullanarak
çözebiliriz.
Üretim uygulamaları için daha sağlam bir akış çözümü düşünmeniz gerektiğini unutmayın. Bazı seçenekler şunlardır:
İşlem geçmişini ayrıştırma
Solana, bir hesabın işlem geçmişini almanıza
(getSignaturesForAddress) ve bir
işlemin ayrıntılarını almanıza
(getTransaction) olanak tanıyan RPC
yöntemlerine sahiptir. İşlem geçmişini ayrıştırmak için token hesabımız için son
imzaları alırız, ardından her işlemin öncesi/sonrası token bakiyelerini
getiririz. Her işlemden önce ve sonra ATA'mızın bakiyesini karşılaştırarak,
ödeme tutarını ve yönünü (gelen veya giden) belirleyebiliriz.
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 transactionconst accountKeys = tx.transaction.message.accountKeys;const ataIndex = accountKeys.findIndex((key) => key === ata);if (ataIndex === -1) continue;// Compare pre/post balances for our ATAconst 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;}
Karşı tarafı belirlemek için, işlemin token bakiyelerini ters yönde değişen başka bir hesap için tarayabilirsiniz—eğer fon aldıysanız, aynı miktarda azalan bir hesap arayın.
SPL token transferleri kullanıcılar arasındaki ödemelerden daha geniş bir alanda var olabileceğinden, bu yaklaşım ödeme olmayan bazı işlemleri de verebilir. Burada iyi bir alternatif Memo'ları kullanmaktır.
Ön/son bakiye ayrıştırmanın sınırlamaları
Yukarıdaki yaklaşım basit ödeme akışları için iyi çalışır. Ancak, ölçekte ödeme işleyen şirketler genellikle daha ayrıntılı ve gerçek zamanlı verilere ihtiyaç duyar:
- Talimat başına dökümü: Tek bir işlem birden fazla transfer içerebilir. Ön/son bakiyeler yalnızca net değişimi gösterir, bireysel transferleri göstermez.
- Çok taraflı işlemler: Karmaşık işlemler (takaslar, toplu ödemeler) birden fazla hesap içerir. Bakiye farkları fonların tam akışını ortaya çıkarmaz.
- Denetim gereksinimleri: Finansal uyumluluk genellikle yalnızca nihai bakiyeleri değil, tam transfer dizilerini yeniden oluşturmayı gerektirir.
Yüksek hacimleri işleyen üretim sistemleri için, bireysel transfer talimatlarını ayrıştıran ve işlem düzeyinde detay sağlayan özel indeksleme çözümlerini değerlendirin.
Ödemeleri memo'larla mutabık kılma
Gönderenler memo'lar (fatura kimlikleri, sipariş numaraları) eklediğinde,
bunları işlemin mesajından getTransaction RPC yöntemi ve jsonParsed
kodlaması kullanarak çıkarabilirsiniz:
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);}
Korumalar
Kaçınılması gereken birkaç hata modu:
-
Ön uca güvenmek. Bir ödeme sayfası "ödeme tamamlandı" diyor—ama işlem gerçekten gerçekleşti mi? Her zaman RPC'yi sorgulayarak sunucu tarafında doğrulayın. Ön uç onayları taklit edilebilir.
-
"Processed" durumuna göre hareket etmek. Solana işlemleri üç aşamadan geçer: processed → confirmed → finalized. "Processed" bir işlem fork'lar sırasında hala düşürülebilir. Siparişleri göndermeden önce "confirmed" (1-2 saniye) veya yüksek değerli işlemler için "finalized" (~13 saniye) durumunu bekleyin.
-
Mint'i göz ardı etmek. Herkes "USDC" adında bir token oluşturabilir. Token hesabının mint'inin, yalnızca token adı değil, gerçek stablecoin mint adresi ve token programıyla eşleştiğini doğrulayın.
-
Çift yerine getirme. Webhook'unuz tetiklenir, siparişi gönderirsiniz. Ağ aksaması olur, webhook tekrar tetiklenir. Şimdi iki kez gönderdiniz. İşlenmiş işlem imzalarını saklayın ve yerine getirmeden önce kontrol edin.
Is this page helpful?