Arka plan ve kullanım senaryosu
Solana işlem gönderiminde slot ilerlemesi, lider yerleşimi ve ağ yolu tıkanıklığı, lidere en hızlı ulaşan rotanın hangisi olduğunu sürekli değiştirir. Bu durum herhangi bir RPC sağlayıcısına ya da gönderim hizmetine özgü değildir; Solana'nın yürütme modelinin yapısal bir özelliğidir.
ERPC'nin SWQoS endpoint'i, işlemleri liderlerin Stake-weighted Quality of Service (SWQoS) temelinde tahsis ettiği öncelikli şeride enjekte eden bir gönderim yolu sağlar. Bu öncelikli bant genişliği, önceliksiz şeridin yaklaşık 5 katıdır ve priority fee değerlendirmesinden önce uygulanır.
Bu nedenle SWQoS endpoint'i işlem gönderimi için önemli bir seçenektir, ancak üretimde tek bir endpoint her zaman en hızlısı değildir. Aynı slot içinde bile geçici yol farklılıkları veya yük dengesizliği, başka bir hızlı endpoint'in öne geçmesine olanak tanıyabilir.
Bu özellikler göz önüne alındığında, tek bir rotaya güvenmek yerine aynı işlemi paralel olarak birden çok hızlı yola gönderip ilk işlenen sonucu kabul etmek şeklinde bir operasyonel desen etkili olabilir.
Ancak aynı işlem birden fazla endpoint'e gönderildiğinde, ek bir kontrol olmadan bunun yalnızca bir kez yürütüleceğini garanti edemezsiniz. Bu kontrol olmadan yapılan fan-out, istenmeyen çift yürütmeye veya bozuk retry kontrolüne yol açabilir.
Solana bunun için Durable Nonce mekanizmasını sağlar. Durable Nonce kullanmak, aynı imzalanmış işlemi birden çok rota üzerinden gönderirken on-chain yürütmeyi tek seferle sınırlamanıza olanak tanır.
Bu sayfa, Durable Nonce ile işlem gönderimini varsayarak, ERPC'nin SWQoS endpoint'ini diğer hızlı RPC endpoint'leriyle birleştiren fan-out işlemlerinin nasıl uygulanacağını açıklar.
Kapsam ve ön koşullar
Bu kılavuz, web3.js ile bir Durable Nonce hesabı oluşturmayı ve onu işlem gönderimi ile fan-out işlemleri için kullanmayı kapsar.
Anlaşılması gereken ön koşullar:
- Durable Nonce işlemleri için nonce değerini
recentBlockhash olarak kullanın ve nonceAdvance öğesini ilk talimat olarak yerleştirin
nonceAdvance bir kez yürütüldüğünde, sonraki talimatlar başarısız olsa bile nonce tüketilebilir. Aynı rawTx'i olduğu gibi yeniden gönderemezsiniz.
- Nonce hesabı oluşturma tek seferlik bir kurulumdur ve genellikle yeniden kullanılır
Adım 1: Nonce authority ve bağlantıyı hazırlayın
Nonce authority, nonce'u ilerletebilen bir Keypair'dir.
import {
Connection,
Keypair,
SystemProgram,
NONCE_ACCOUNT_LENGTH,
} from '@solana/web3.js'
const connection = new Connection('https://<primary-rpc-endpoint>', 'confirmed')
const nonceAuthority = Keypair.fromSecretKey(/* secret key */)
import {
Connection,
Keypair,
SystemProgram,
NONCE_ACCOUNT_LENGTH,
} from '@solana/web3.js'
const connection = new Connection('https://<primary-rpc-endpoint>', 'confirmed')
const nonceAuthority = Keypair.fromSecretKey(/* secret key */)
- Nonce authority,
nonceAdvance işlemini yürütebilir
- Fee payer olabilir ya da ayrı bir keypair olabilir
Adım 2: Bir nonce hesabı Keypair'i oluşturun
Bir nonce hesabı bir System Account'tur.
const nonceAccount = Keypair.generate()
const nonceAccount = Keypair.generate()
Bu Keypair şunlar için kullanılır:
- Nonce değerini tutmak (
recentBlockhash yerine geçer)
- Yalnızca oluşturma anında imzalamak
- Sonrasında güvenli depolama; günlük gönderimler için gerekli değildir
Adım 3: Rent-exempt minimum tutarı hesaplayın
Nonce hesapları rent-exempt olmalıdır. Değerleri sabit kodlamayın; bunları RPC'den getirin.
const lamports =
await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH)
const lamports =
await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH)
Adım 4: Nonce hesabını oluşturun ve başlatın
Nonce hesabını tek bir işlemde createAccount + nonceInitialize ile oluşturun.
import { Transaction } from '@solana/web3.js'
const tx = new Transaction()
tx.add(
SystemProgram.createAccount({
fromPubkey: nonceAuthority.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
)
// fee payer is the nonce authority
tx.feePayer = nonceAuthority.publicKey
// use a normal blockhash for initialization
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('confirmed')
tx.recentBlockhash = blockhash
// sign with both the new account and the authority
tx.sign(nonceAccount, nonceAuthority)
const signature = await connection.sendRawTransaction(tx.serialize())
await connection.confirmTransaction(
{ signature, blockhash, lastValidBlockHeight },
'confirmed',
)
import { Transaction } from '@solana/web3.js'
const tx = new Transaction()
tx.add(
SystemProgram.createAccount({
fromPubkey: nonceAuthority.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
)
// fee payer is the nonce authority
tx.feePayer = nonceAuthority.publicKey
// use a normal blockhash for initialization
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('confirmed')
tx.recentBlockhash = blockhash
// sign with both the new account and the authority
tx.sign(nonceAccount, nonceAuthority)
const signature = await connection.sendRawTransaction(tx.serialize())
await connection.confirmTransaction(
{ signature, blockhash, lastValidBlockHeight },
'confirmed',
)
Sonraki gönderimler için bu nonceAccount.publicKey değerini kullanın.
Adım 5: Her gönderimden önce nonce'u getirin
Her işlem için mevcut nonce değerini getirin.
import { NonceAccount } from '@solana/web3.js'
const { value, context } = await connection.getAccountInfoAndContext(
nonceAccount.publicKey,
'confirmed',
)
if (!value) {
throw new Error('Nonce account not found')
}
const nonce = NonceAccount.fromAccountData(value.data).nonce
const minContextSlot = context.slot
import { NonceAccount } from '@solana/web3.js'
const { value, context } = await connection.getAccountInfoAndContext(
nonceAccount.publicKey,
'confirmed',
)
if (!value) {
throw new Error('Nonce account not found')
}
const nonce = NonceAccount.fromAccountData(value.data).nonce
const minContextSlot = context.slot
nonce değerini recentBlockhash olarak kullanın
context.slot onaylamada kullanılır
Adım 6: Durable Nonce işlemini oluşturun
Durable Nonce işlemleri aşağıdaki koşulları sağlamalıdır:
recentBlockhash = nonce
- İlk talimat
nonceAdvance'tir
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const instructions = [
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
// add your real instructions after this
]
const message = new TransactionMessage({
payerKey: nonceAuthority.publicKey,
recentBlockhash: nonce,
instructions,
}).compileToV0Message()
const tx = new VersionedTransaction(message)
tx.sign([nonceAuthority /* + other signers */])
const rawTx = tx.serialize()
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const instructions = [
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: nonceAuthority.publicKey,
}),
// add your real instructions after this
]
const message = new TransactionMessage({
payerKey: nonceAuthority.publicKey,
recentBlockhash: nonce,
instructions,
}).compileToV0Message()
const tx = new VersionedTransaction(message)
tx.sign([nonceAuthority /* + other signers */])
const rawTx = tx.serialize()
Notlar:
- ComputeBudget talimatları kullanıyorsanız, bunlar
nonceAdvance'tan sonra gelmelidir
nonceAdvance ilk sırada değilse, işlem reddedilir
Adım 7: Birden fazla RPC'ye fan out yapın
Aynı rawTx'i eşzamanlı olarak gönderin.
const endpoints = [
'https://swqos-fra2.erpc.global?api-key=YOUR_API_KEY',
'https://swqos-ams1.erpc.global?api-key=YOUR_API_KEY',
'https://<backup-rpc-1>',
]
const results = await Promise.allSettled(
endpoints.map((url) =>
new Connection(url, 'confirmed').sendRawTransaction(rawTx, {
skipPreflight: true,
minContextSlot,
}),
),
)
const success = results.find(
(r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled',
)
if (!success) {
throw new Error('All sends failed')
}
const signature = success.value
const endpoints = [
'https://swqos-fra2.erpc.global?api-key=YOUR_API_KEY',
'https://swqos-ams1.erpc.global?api-key=YOUR_API_KEY',
'https://<backup-rpc-1>',
]
const results = await Promise.allSettled(
endpoints.map((url) =>
new Connection(url, 'confirmed').sendRawTransaction(rawTx, {
skipPreflight: true,
minContextSlot,
}),
),
)
const success = results.find(
(r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled',
)
if (!success) {
throw new Error('All sends failed')
}
const signature = success.value
- İmza tüm endpoint'lerde aynıdır
- Başarılı bir gönderim bir onay değildir
Adım 8: Durable Nonce ile onaylayın
Durable Nonce ile onaylamaya nonce bilgisini dahil edin.
await connection.confirmTransaction(
{
signature,
nonceAccountPubkey: nonceAccount.publicKey,
nonceValue: nonce,
minContextSlot,
},
'confirmed',
)
await connection.confirmTransaction(
{
signature,
nonceAccountPubkey: nonceAccount.publicKey,
nonceValue: nonce,
minContextSlot,
},
'confirmed',
)
Onay başarılı olursa:
- Nonce ilerler
- Diğer RPC'lere gönderilen kopyalar
InvalidNonce döndürür
Sonraki gönderimler
- Onaydan sonra yeni bir nonce getirin
- Aynı rawTx'i veya nonce değerini yeniden kullanmayın
- Paralel iş akışları için ayrı nonce hesapları kullanın
Bir sonraki işlem için güncellenmiş nonce'u getirin ve işlemi aynı adımları kullanarak oluşturun.