SWQoS için Durable Nonce Fan-out
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
recentBlockhasholarak kullanın venonceAdvanceöğesini ilk talimat olarak yerleştirin nonceAdvancebir 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.
typescript
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,
nonceAdvanceiş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.
typescript
const nonceAccount = Keypair.generate()const nonceAccount = Keypair.generate()Bu Keypair şunlar için kullanılır:
- Nonce değerini tutmak (
recentBlockhashyerine 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.
typescript
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.
typescript
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.
typescript
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.slotimport { 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.slotnoncedeğerinirecentBlockhasholarak kullanıncontext.slotonaylamada 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
typescript
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 nonceAdvanceilk 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.
typescript
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.valueconst 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.
typescript
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
InvalidNoncedö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.






