Her Solana işlemi yakın zamanlı bir blok hash'i içerir—işlemin "şimdi" oluşturulduğunu kanıtlayan yakın zamanlı bir ağ durumuna referans. Ağ, ~150 bloktan (~60-90 saniye) daha eski blok hash'ine sahip herhangi bir işlemi reddeder, böylece tekrar saldırılarını ve eski gönderimleri önler. Bu, gerçek zamanlı ödemeler için mükemmel çalışır. Ancak imzalama ve gönderim arasında boşluk gerektiren iş akışlarını bozar, örneğin:
| Senaryo | Standart işlemler neden başarısız olur |
|---|---|
| Hazine operasyonları | Tokyo'daki CFO imzalar, New York'taki kontrolör onaylar—90 saniye yeterli değil |
| Uyumluluk iş akışları | İşlemlerin yürütülmeden önce yasal/uyumluluk incelemesi gerekir |
| Soğuk depolama imzalama | Hava boşluklu makineler, imzalı işlemlerin manuel transferini gerektirir |
| Toplu hazırlık | Bordro veya ödemeleri mesai saatlerinde hazırlayın, gece yürütün |
| Çoklu imza koordinasyonu | Farklı zaman dilimlerindeki birden fazla onaylayıcı |
| Planlanmış ödemeler | Ödemeleri gelecekteki bir tarihte yürütülmek üzere planlayın |
Geleneksel finansta, imzalanmış bir çek 90 saniyede geçerliliğini yitirmez. Belirli blokzincir operasyonları da yitirmemeli. Dayanıklı nonce'lar bunu, yakın zamanlı blok hash'ini yalnızca kullandığınızda ilerleyen, depolanmış, kalıcı bir değerle değiştirerek çözer—size göndermek için hazır olana kadar geçerli kalan işlemler sunar.
Nasıl çalışır
Yakın zamanlı bir blockhash (~150 blok geçerli) yerine, bir blockhash yerine kullanılabilecek benzersiz bir değer saklayan özel bir hesap olan nonce hesabı kullanırsınız. Bu nonce'u kullanan her işlem, ilk talimat olarak onu "ilerletmelidir". Her nonce değeri yalnızca bir işlem için kullanılabilir.
Nonce hesabı, rent muafiyeti için ~0.0015 SOL maliyete sahiptir. Bir nonce hesabı = aynı anda bir bekleyen işlem. Paralel iş akışları için birden fazla nonce hesabı oluşturun.
Nonce hesabı oluşturma
Bir nonce hesabı oluşturmak, tek bir işlemde iki talimat gerektirir:
- System Program'dan
getCreateAccountInstructionkullanarak hesabı oluşturun getInitializeNonceAccountInstructionkullanarak nonce olarak başlatın
Keypair oluşturma
Nonce hesap adresi olarak kullanmak üzere yeni bir keypair oluşturun ve gerekli alanı ve rent'i hesaplayın.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();
Hesap oluşturma talimatı
Rent muafiyeti için yeterli lamport ile System Program'a ait hesabı oluşturun.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});
Nonce başlatma talimatı
Hesabı nonce hesabı olarak başlatın ve onu ilerletebilecek yetkiyi ayarlayın.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});const initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});
İşlem oluşturma
Her iki talimatı içeren bir işlem oluşturun.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();const createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});const initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});const { value: blockhash } = await rpc.getLatestBlockhash().send();const createNonceTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),(tx) =>appendTransactionMessageInstructions([createNonceAccountIx, initNonceIx],tx));
İmzalama ve gönderme
Nonce hesabını oluşturmak ve başlatmak için işlemi imzalayın ve gönderin.
Ertelenmiş işlem oluşturma
Yakın zamanlı bir blok hash'i yerine, nonce hesabının blockhash değerini
işlemin ömrü olarak kullanın.
Nonce'u getir
Nonce hesabından veriyi getirin. Nonce hesabındaki blockhash değerini işlemin
ömrü olarak kullanın.
{version: 1,state: 1,authority: 'HgjaL8artMtmntaQDVM2UBk3gppsYYERS4PkUhiaLZD1',blockhash: '5U7seXqfgZx1uh5DFhdH1vyBhr7XGRrKxBAnJJTbbUa',lamportsPerSignature: 5000n}
Transfer talimatı oluştur
Ödemeniz için talimat oluşturun. Bu örnek bir token transferini gösterir.
Kalıcı nonce ile işlem oluştur
setTransactionMessageLifetimeUsingDurableNonce kullanın, bu nonce'u blok
hash'i olarak ayarlar ve otomatik olarak nonce ilerleme talimatını başa ekler.
İşlemi imzala
İşlemi imzalayın. Artık standart bir blok hash'i yerine kalıcı nonce kullanıyor.
İşlemi saklama veya gönderme
İmzaladıktan sonra, işlemi depolama için kodlayın. Hazır olduğunuzda, ağa gönderin.
Depolama için kodlama
İmzalanmış işlemi base64 formatına kodlayın. Bu değeri veritabanınızda saklayın.
İşlemi gönderme
Hazır olduğunuzda imzalanmış işlemi gönderin. İşlem, nonce ilerletilene kadar geçerli kalır.
Demo
// Generate keypairs for sender and recipientconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();console.log("Sender Address:", sender.address);console.log("Recipient Address:", recipient.address);// Demo Setup: Create RPC connection, mint, and token accountsconst { rpc, rpcSubscriptions, mint } = await demoSetup(sender, recipient);// =============================================================================// Step 1: Create a Nonce Account// =============================================================================const nonceKeypair = await generateKeyPairSigner();console.log("\nNonce Account Address:", nonceKeypair.address);const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();// Instruction to create new account for the nonceconst createNonceAccountIx = getCreateAccountInstruction({payer: sender,newAccount: nonceKeypair,lamports: nonceRent,space: nonceSpace,programAddress: SYSTEM_PROGRAM_ADDRESS});// Instruction to initialize the nonce accountconst initNonceIx = getInitializeNonceAccountInstruction({nonceAccount: nonceKeypair.address,nonceAuthority: sender.address});// Build and send nonce account creation transactionconst { value: blockhash } = await rpc.getLatestBlockhash().send();const createNonceTx = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),(tx) =>appendTransactionMessageInstructions([createNonceAccountIx, initNonceIx],tx));const signedCreateNonceTx =await signTransactionMessageWithSigners(createNonceTx);assertIsTransactionWithBlockhashLifetime(signedCreateNonceTx);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedCreateNonceTx,{ commitment: "confirmed" });console.log("Nonce Account created.");// =============================================================================// Step 2: Token Payment with Durable Nonce// =============================================================================// Fetch current nonce value from the nonce accountconst { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);console.log("Nonce Account data:", nonceData);const [senderAta] = await findAssociatedTokenPda({mint: mint.address,owner: sender.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [recipientAta] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});console.log("\nMint Address:", mint.address);console.log("Sender Token Account:", senderAta);console.log("Recipient Token Account:", recipientAta);const transferInstruction = getTransferInstruction({source: senderAta,destination: recipientAta,authority: sender.address,amount: 250_000n // 0.25 tokens});// Create transaction message using durable nonce lifetime// setTransactionMessageLifetimeUsingDurableNonce automatically prepends// the AdvanceNonceAccount instructionconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) =>setTransactionMessageLifetimeUsingDurableNonce({nonce: nonceData.blockhash as string as Nonce,nonceAccountAddress: nonceKeypair.address,nonceAuthorityAddress: nonceData.authority},tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);assertIsTransactionWithDurableNonceLifetime(signedTransaction);const transactionSignature = getSignatureFromTransaction(signedTransaction);// Encode the transaction to base64, optionally save and send at a later timeconst base64EncodedTransaction =getBase64EncodedWireTransaction(signedTransaction);console.log("\nBase64 Encoded Transaction:", base64EncodedTransaction);// Send the encoded transaction, blockhash does not expireawait rpc.sendTransaction(base64EncodedTransaction, {encoding: "base64",skipPreflight: true}).send();console.log("\n=== Token Payment with Durable Nonce Complete ===");console.log("Transaction Signature:", transactionSignature);// =============================================================================// Demo Setup Helper Function// =============================================================================
Bekleyen bir işlemi geçersiz kılma
Her nonce hesabı blockhash yalnızca bir kez kullanılabilir. Bekleyen bir
işlemi geçersiz kılmak veya nonce hesabını yeniden kullanıma hazırlamak için
manuel olarak ilerletin:
import { getAdvanceNonceAccountInstruction } from "@solana-program/system";// Submit this instruction (with a regular blockhash) to invalidate any pending transactiongetAdvanceNonceAccountInstruction({nonceAccount: nonceAddress,nonceAuthority});
Bu, yeni bir nonce değeri oluşturur ve eski değerle imzalanmış herhangi bir işlemi kalıcı olarak geçersiz kılar.
Çok taraflı onay iş akışı
Ek imzalar eklemek için işlemi deserialize edin, ardından depolama veya gönderim için tekrar serialize edin:
import {getBase64Decoder,getTransactionDecoder,getBase64EncodedWireTransaction,partiallySignTransaction} from "@solana/kit";// Deserialize the stored transactionconst txBytes = getBase64Decoder().decode(serializedString);const partiallySignedTx = getTransactionDecoder().decode(txBytes);// Each approver adds their signatureconst fullySignedTx = await partiallySignTransaction([newSigner],partiallySignedTx);// Serialize again for storage or submissionconst serialized = getBase64EncodedWireTransaction(fullySignedTx);
İşlem serileştirilebilir, saklanabilir ve onaylayanlar arasında aktarılabilir. Gerekli tüm imzalar toplandığında, ağa gönderin.
Üretim ortamı için dikkat edilmesi gerekenler
Nonce hesabı yönetimi:
- Paralel işlem hazırlığı için bir nonce hesabı havuzu oluşturun
- Hangi nonce'ların "kullanımda" olduğunu takip edin (bekleyen imzalı işlemleri olan)
- İşlemler gönderildikten veya iptal edildikten sonra nonce geri dönüşümünü uygulayın
Güvenlik:
- Nonce yetkisi, işlemlerin geçersiz kılınıp kılınamayacağını kontrol eder. Ek kontrol ve görev ayrımı için nonce yetkisini işlem imzalayanlardan ayırmayı düşünün
- Serileştirilmiş işlem baytlarına sahip olan herkes bunu ağa gönderebilir
İlgili kaynaklar
Is this page helpful?