Fan-out pour SWQoS

Contexte et scénario d'utilisation

Dans Solana, l'envoi de transactions, la progression des slots, le placement des leaders et la congestion du réseau changent constamment quelle route atteint le leader le plus rapidement. Ce n'est pas spécifique à tout fournisseur de RPC ou service d'envoi; c'est une caractéristique structurelle du modèle d'exécution de Solana.
Le endpoint SWQoS d'ERPC fournit un chemin d'envoi qui injecte les transactions dans la voie prioritaire allouée par les leaders en fonction de la qualité de service pondérée (SWQoS). Cette bande passante prioritaire est d'environ 5× celle de la voie non prioritaire et est appliquée avant l'évaluation des frais de priorité.
Pour cette raison, l'endpoint SWQoS est une option importante pour l'envoi de transactions, mais en production un seul paramètre n'est pas toujours le plus rapide. Même à l'intérieur d'une même slot, des différences de trajectoire transitoires ou un décalage de charge peuvent permettre à un autre endpoint rapide de conduire.
Compte tenu de ces caractéristiques, un schéma opérationnel d'envoi de la même transaction à plusieurs voies rapides en parallèle et d'acceptation du premier traité peut être efficace, plutôt que de compter sur une seule voie.
Cependant, lorsque la même transaction est envoyée à plusieurs paramètres, vous ne pouvez pas garantir qu'elle ne s'exécutera qu'une seule fois sans contrôle supplémentaire. Sans ce contrôle, le ventilateur peut mener à une double exécution involontaire ou à un contrôle de réessayer cassé.
Solana fournit la Nonce Durable comme mécanisme pour cela. Utiliser Durable Nonce vous permet d'envoyer la même transaction signée sur plusieurs routes tout en limitant l'exécution sur la chaîne à une seule fois.
Cette page explique comment mettre en œuvre des opérations de fan-out qui combinent l'endpoint SWQoS d'ERPC avec d'autres endpoints RPC rapides, en supposant l'envoi de transactions avec Durable Nonce.

Portée et conditions préalables

Ce guide couvre la création d'un compte Nonce Durable avec web3.js et son utilisation pour les opérations d'envoi de transactions et de fan-out.
Prérequis pour comprendre:
  • Pour les transactions de Nonce Durable, utilisez la valeur nonce comme recentBlockhash et lieu nonceAdvance comme première instruction
  • Une fois nonceAdvance exécute, le nonce peut être consommé même si les instructions plus tard échouent. Vous ne pouvez pas renvoyer le même Tx brut que-est.
  • La création de compte Nonce est une configuration unique et est généralement réutilisée

Étape 1: Préparer l'autorité et le lien non-ce

L'autorité de nonce est un Keypair qui peut faire avancer le nonce.
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 */)
  • L'autorité non-ce peut exécuter nonceAdvance
  • Il peut être le payeur de frais, ou une paire de clés séparée

Étape 2: Générer un compte non-ce Keypair

Un compte non-ce est un Compte système.
typescript
const nonceAccount = Keypair.generate()
Ce Keypair est utilisé pour:
  • Détention de la valeur non-ce (remplacement pour recentBlockhash)
  • Signature seulement au moment de la création
  • Stockage sûr après l'envoi; il n'est pas nécessaire pour les envois quotidiens

Étape 3: Calculer le minimum exempt de loyer

Les comptes nominaux doivent être exonérés de loyer. Ne pas coder les valeurs; les récupérer du RPC.
typescript
const lamports =
  await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH,
  )

Étape 4: Créer et initialiser le compte nonce

Créer le compte nonce avec createAccount + nonceInitialize dans une seule transaction.
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',
)
Utilisez ceci nonceAccount.publicKey pour les envois ultérieurs.

Étape 5: Récupérez le nonce avant chaque envoi

Pour chaque transaction, récupérer la valeur courante nonce.
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
  • Utilisation nonce comme recentBlockhash
  • context.slot est utilisé pour la confirmation

Étape 6: Construisez la transaction de Nonce Durable

Les transactions durables doivent satisfaire aux conditions suivantes:
  • recentBlockhash = nonce
  • La première instruction est nonceAdvance
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()
Remarques:
  • Si vous utilisez les instructions ComputeBudget, ils doivent venir après nonceAdvance
  • Si nonceAdvance n'est pas d'abord, la transaction sera rejetée

Étape 7: Éventail vers plusieurs RPC

Envoyez simultanément le même Tx brut.
typescript
const endpoints = [
  'https://<erpc-swqos-fra>',
  'https://<backup-rpc-1>',
  'https://<backup-rpc-2>',
]

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
  • La signature est identique entre les paramètres
  • Un envoi réussi est Pas une confirmation

Étape 8: Confirmer avec un bouton durable

Avec Durable Nonce, incluez les informations nonce dans la confirmation.
typescript
await connection.confirmTransaction(
  {
    signature,
    nonceAccountPubkey: nonceAccount.publicKey,
    nonceValue: nonce,
    minContextSlot,
  },
  'confirmed',
)
Si la confirmation réussit:
  • Les avances non-ce
  • Copies envoyées à d'autres RPC InvalidNonce

Envois suivants

  • Après confirmation, chercher un nouveau nonce
  • Ne pas réutiliser la même valeur bruteTx ou nonce
  • Pour les workflows parallèles, utilisez des comptes non-ce séparés
Pour la transaction suivante, récupérer le nonce mis à jour et construire la transaction en utilisant les mêmes étapes.