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 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.
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 */)
  • 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.
typescript
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.
typescript
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',
)
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.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
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()
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.
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.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',
)
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.