Jokainen Solana-transaktio sisältää viimeaikaisen lohkohashin – viittauksen viimeaikaiseen verkon tilaan, joka todistaa transaktion luodun "nyt". Verkko hylkää kaikki transaktiot, joiden lohkohash on vanhempi kuin ~150 lohkoa (~60–90 sekuntia), estäen uudelleentoistohyökkäykset ja vanhentuneet lähetykset. Tämä toimii täydellisesti reaaliaikaisissa maksuissa. Mutta se rikkoo työnkulut, jotka tarvitsevat väliä allekirjoituksen ja lähetyksen välillä, kuten:
| Skenaario | Miksi tavalliset transaktiot epäonnistuvat |
|---|---|
| Treasury-toiminnot | Talousjohtaja Tokiossa allekirjoittaa, controller New Yorkissa hyväksyy – 90 sekuntia ei riitä |
| Compliance-työnkulut | Transaktiot vaativat juridisen/compliance-tarkastuksen ennen suoritusta |
| Cold storage -allekirjoitus | Ilmaväliset koneet vaativat allekirjoitettujen transaktioiden manuaalisen siirron |
| Eräkäsittelyn valmistelu | Valmistele palkanlaskenta tai maksut työaikana, suorita yöllä |
| Multi-sig-koordinointi | Useita hyväksyjiä eri aikavyöhykkeillä |
| Ajoitetut maksut | Ajoita maksut suoritettavaksi tulevana päivänä |
Perinteisessä rahoituksessa allekirjoitettu shekki ei vanhene 90 sekunnissa. Tiettyjen lohkoketjuoperaatioiden ei pitäisi sekään. Kestävät noncet ratkaisevat tämän korvaamalla viimeaikaisen lohkohashin tallennetulla, pysyvällä arvolla, joka etenee vain kun käytät sitä – antaen sinulle transaktioita, jotka pysyvät voimassa kunnes olet valmis lähettämään ne.
Miten se toimii
Viimeaikaisen lohkohajautuksen sijaan (voimassa ~150 lohkoa) käytät nonce-tiliä, erityistä tiliä, joka tallentaa ainutlaatuisen arvon, jota voidaan käyttää lohkohajautuksen sijasta. Jokaisen tätä noncea käyttävän transaktion on "edistettävä" sitä ensimmäisenä käskynä. Jokaista nonce-arvoa voidaan käyttää vain yhdelle transaktiolle.
Nonce-tili maksaa ~0.0015 SOL vuokravapautusta varten. Yksi nonce-tili = yksi odottava transaktio kerrallaan. Rinnakkaisiin työnkulkuihin luo useita nonce-tilejä.
Nonce-tilin luominen
Nonce-tilin luominen vaatii kaksi käskyä yhdessä transaktiossa:
- Luo tili käyttäen
getCreateAccountInstructionSystem Programista - Alusta se nonceksi käyttäen
getInitializeNonceAccountInstruction
Luo avainpari
Luo uusi avainpari käytettäväksi nonce-tilin osoitteena ja laske vaadittu tila ja vuokra.
const nonceKeypair = await generateKeyPairSigner();const nonceSpace = BigInt(getNonceSize());const nonceRent = await rpc.getMinimumBalanceForRentExemption(nonceSpace).send();
Luo tilin käsky
Luo tili, jonka omistaa System Program, riittävällä määrällä lamporteja vuokravapautusta varten.
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});
Alusta nonce-käsky
Alusta tili nonce-tiliksi asettaen valtuutus, joka voi edistää sitä.
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});
Rakenna transaktio
Rakenna transaktio molemmilla käskyillä.
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));
Allekirjoita ja lähetä
Allekirjoita ja lähetä transaktio nonce-tilin luomiseksi ja alustamiseksi.
Lykätyn transaktion rakentaminen
Viimeaikaisen lohkohashin sijaan käytä nonce-tilin blockhash-arvoa transaktion
elinaikana.
Hae nonce
Hae data nonce-tililtä. Käytä nonce-tilin blockhash-arvoa transaktion
elinaikana.
{version: 1,state: 1,authority: 'HgjaL8artMtmntaQDVM2UBk3gppsYYERS4PkUhiaLZD1',blockhash: '5U7seXqfgZx1uh5DFhdH1vyBhr7XGRrKxBAnJJTbbUa',lamportsPerSignature: 5000n}
Luo siirtokäsky
Luo käsky maksuasi varten. Tämä esimerkki näyttää token-siirron.
Rakenna transaktio kestävällä noncella
Käytä setTransactionMessageLifetimeUsingDurableNonce-metodia, joka asettaa
noncen lohkohashiksi ja lisää automaattisesti nonce-edistyskäskyn alkuun.
Allekirjoita transaktio
Allekirjoita transaktio. Se käyttää nyt kestävää noncea tavallisen lohkohashin sijaan.
Tallenna tai lähetä transaktio
Allekirjoituksen jälkeen koodaa transaktio tallennusta varten. Kun olet valmis, lähetä se verkkoon.
Koodaa tallennusta varten
Koodaa allekirjoitettu transaktio base64-muotoon. Tallenna tämä arvo tietokantaasi.
Lähetä transaktio
Lähetä allekirjoitettu transaktio, kun olet valmis. Transaktio pysyy voimassa, kunnes nonce edistetään.
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// =============================================================================
Mitätöi odottava transaktio
Jokaista nonce-tiliä blockhash voidaan käyttää vain kerran. Mitätöidäksesi
odottavan transaktion tai valmistellaksesi nonce-tilin uudelleenkäyttöä varten,
edistä sitä manuaalisesti:
import { getAdvanceNonceAccountInstruction } from "@solana-program/system";// Submit this instruction (with a regular blockhash) to invalidate any pending transactiongetAdvanceNonceAccountInstruction({nonceAccount: nonceAddress,nonceAuthority});
Tämä luo uuden nonce-arvon, mikä tekee kaikista vanhalla arvolla allekirjoitetuista transaktioista pysyvästi virheellisiä.
Moniosapuolinen hyväksyntätyönkulku
Deserialisoi transaktio lisätäksesi ylimääräisiä allekirjoituksia, ja serialisoi sitten uudelleen tallennusta tai lähettämistä varten:
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);
Transaktio voidaan serialisoida, tallentaa ja välittää hyväksyjien välillä. Kun kaikki vaaditut allekirjoitukset on kerätty, lähetä verkkoon.
Tuotantoympäristön huomioitavaa
Nonce-tilin hallinta:
- Luo joukko nonce-tilejä rinnakkaista transaktiovalmistelua varten
- Seuraa, mitkä noncet ovat "käytössä" (niillä on odottavia allekirjoitettuja transaktioita)
- Toteuta noncen kierrätys sen jälkeen, kun transaktiot on lähetetty tai hylätty
Turvallisuus:
- Nonce-valtuutus määrittää, voidaanko transaktiot mitätöidä. Harkitse nonce-valtuutuksen erottamista transaktioiden allekirjoittajista lisävalvontaa ja tehtävien erottelua varten
- Kuka tahansa, jolla on sarjallistetut transaktiotavut, voi lähettää sen verkkoon
Aiheeseen liittyvät resurssit
Is this page helpful?