Rand Protocol
Documentation
Concepts fondamentaux

Frais

Ce qu'une transaction paie à la chaîne en RAND - les planchers fixes par action, les prix du gas et de l'octet qu'un appel paie sur la chain 20, la destination des frais, pourquoi le gas est ici un prix et non une borne sur l'exécution, et ce que l'expéditeur paie à la place avec sa propre machine.

Utilisez cette page pour fixer les frais d’une transaction, estimer le coût d’un déploiement ou d’un appel, et comprendre ce que touche un validator. Les frais sont des planchers, exprimés en RAND. Toute action autre qu’un appel paie un montant fixe. Sur la chain 20, un appel paie la limite de gas que déclare sa preuve et ses octets, à des prix qui varient selon le remplissage des blocs et s’arrêtent à un plafond.

Les planchers

Les frais sont en unités de 10⁻⁹ RAND. Les constantes se trouvent dans randprotocol_core::gas :

transactionplancheraujourd’hui
tout bundle : un transfert de RAND ou de n’importe quel token RPL, un bond, un dépôt de bridge (BridgeAttest, payé par son relayeur), un TokenMint, un SetAuthority, un TokenBurn, un ListBackingBUNDLE_BASE = 1 000 000 unités0,001 RAND
DeployBUNDLE_BASE + DEPLOY_PER_WORD (100 000) × (mots du programme + mots publics)un programme de 256 mots : 0,0266 RAND ; un programme de 4 096 mots : 0,4106 RAND ; l’image SPL Token de 65 096 mots avec son entrée publique de 27 151 mots : 9,2257 RAND (déployée sur la chain 13, le 2026-09-19). Depuis la chain 16 (hardening_v6, chain 20 incluse), un déploiement qu’aucun appel ne peut contenir est refusé, comme ProgramUncallable : plus de 8 180 mots pour un programme sans entrée publique, moins à côté d’une entrée publique. Cette image serait refusée sur la chain 20
Callsur une chain dont le genesis comporte une section gas (chain 20, comme les chains 18 et 19) : BUNDLE_BASE + gas_price × la limite de gas que déclare la preuve de l’appel + byte_price × chaque KiB (un KiB partiel comptant pour un entier) de preuve d’appel plus d’enveloppe d’entrée, dès l’octet 0. Voir Gaz sur la chain 18. Sur une chain sans cette section : BUNDLE_BASE + call_fee(tier), où call_fee = CALL_BASE (1 000 000) + 100 000 par tranche de deux paliers au-dessus de 10, plus 1 000 unités par KiB (un KiB partiel comptant pour un entier) de preuve d’appel plus d’enveloppe d’entrée au-delà de la franchise de 2 097 152 + 18 432 octetschain 20, aux prix de départ de 100 et 800 unités : un appel de palier 14 déclarant le plafond du palier, 20 479 de gas, avec 1 350 000 octets de preuve et d’enveloppe, a un plancher de 0,0041 RAND ; le défaut du wallet paie davantage (ci-dessous). Sans section gas, comme sur la chain 15 : palier 10 : 0,002 ; palier 14 : 0,0022 RAND, sous la franchise. Depuis la v0.5.6, un appel au-dessus du palier 14 est refusé quel que soit son paiement (MAX_CALL_TIER). L’appel approve ERC-20 effectué sur la chain 13 le 2026-09-19 (palier 16, une preuve de 3 412 405 octets) a payé 0,00357 RAND et serait refusé sur toute chain depuis la v0.5.6
Invoke (RPL-2, chain 20)le plancher d’un appel, plus le program_state.cell_fee du genesis pour chaque cellule que crée la transition (une cellule écrite non nulle là où rien n’était stocké) ; réécrire ou supprimer une cellule n’ajoute rienle cell_fee de la chain 20 est de 0,01 RAND : l’appel de palier 14 ci-dessus, créant deux cellules, a un plancher de 0,0241 RAND
BridgeBurnBRIDGE_BURN_FEE = 10 × BUNDLE_BASE : la base plus la charge propre du bridge, toutes deux payées par l’unique bundle que porte un burn0,01 RAND ; sur la chain 20, le bridge retient en outre 10 bps du zUSD brûlé (ci-dessous)
RegisterToken, RegisterBridgedTokenBUNDLE_BASE + le registration_fee du registre de tokens (un paramètre du genesis)chain 20 : 0,001 + 1 RAND = 1,001 RAND, dont 1 RAND est brûlé
Mint (faucet), Unbond0 ; pas de bundle, signé par un validator—
PauseMints, UnpauseMints0 ; pas de bundle, autorisé par la clé de pause ou par un quorum de gardiens PQ, de sorte qu’une pause fonctionne depuis un wallet ne détenant aucun RAND—
Withdraw0 sur le réseau ; la base de 0,001 RAND est prélevée sur le montant retiréun retrait de 1 000 RAND crée une note d’une valeur de 999,999

Les frais sont un mot public du bundle et sont toujours en RAND. Le guest de bundle à actifs masqués les lie à la relation d’équilibre du groupe RAND, in₂ + in₃ = out₂ + out₃ + fee + burn_r (emplacements 2–3) : ils sont donc payés à partir de notes masquées sans révéler lesquelles ; un token circule par les emplacements 0–1 du même bundle, selon sa propre relation, in₀ + in₁ = out₀ + out₁ + burn_a. Une seule preuve déplace donc un token et paie ses frais en RAND, et un wallet qui détient un token mais aucun RAND dépensable est refusé avant la preuve.

La charge du bridge porte sur le burn parce que c’est la seule transaction de bridge dont l’émetteur détient à coup sûr du RAND ; un dépôt ne paie que la base, puisque le déposant n’a pas encore de RAND. Les frais d’enregistrement relèvent de l’état du registre et non de fee_floor : ils sont donc vérifiés avec les règles propres à l’action (RegistrationFeeTooLow), de la même façon que la part de gas ou de palier d’un appel est facturée une fois sa preuve décodée. Le plancher d’un appel est vérifié deux fois, car ce qu’il déclare n’est pris pour fiable qu’une fois sa preuve vérifiée. Sur la chain 20, la première vérification, avant toute vérification de preuve, valorise les octets de l’appel et une limite déclarée de 1 (un Invoke y doit aussi son cell fee) ; la seconde valorise la limite que déclare la preuve vérifiée. Sur une chain sans section gas, les deux sont BUNDLE_BASE + CALL_BASE et le plancher exact du palier.

Interrogez le nœud plutôt que de deviner :

rand fee bundle          # 0.001 RAND
rand fee deploy 256      # 0.0266 RAND
rand fee call 14 --gas 20479 --bytes 1350000   # 0.0041 RAND at chain 20's starting prices
→ {"method":"rand_estimateFee","params":[{"kind":"call","tier":14,"gas":20479,"bytes":1350000}]}
← "4103100"

Cette réponse vaut 1,000,000 + 100 × 20,479 + 800 × 1,319 aux prix de départ ; le nœud applique les prix du tip au moment où on l’interroge. Sur la chain 20, le champ gas de la spécification d’appel est obligatoire en JSON-RPC (-32602 sans lui). rand fee call <tier> sans --gas envoie le plafond du palier, et sans --bytes ne valorise aucun octet. Un invoke est valorisé de la même façon, avec "kind":"invoke" et "created_cells": c qui ajoute c cell fees (24103100 pour l’exemple ci-dessus avec deux cellules, demandé à la chain 20 le 2026-10-01). Sur la chain 15, qui n’avait pas de section gas, rand fee call 14 répondait 0,0022 RAND.

Les défauts du wallet sont exactement les planchers, sauf pour un appel sur la chain 20, dont le défaut ajoute une marge (ci-dessous) ; rand send --fee <RAND> et l’option --fee de bond, call, bridge-mint, bridge-burn (défaut 0,01) et des sous-commandes token les relèvent. Tout ce qui dépasse le plancher est un pourboire.

Gaz sur la chain 18

La chain 18, en service du 2026-09-29 au 2026-10-01, a été la première chain mesurée en gas, et la chain 19, son re-genesis sur le même build, portait la même section gas. La chain 20 (v0.6.8) conserve toutes les règles de cette section et y ajoute deux plafonds de prix et une charge en octets comptée uniquement sur les octets qui paient (ci-dessous). Une chain sans section gas conserve le barème par paliers du tableau ci-dessus.

Ce qui est compté. Un gas correspond à une ligne de la table cpu du zkVM : une instruction ordinaire coûte donc 1. Les syscalls sont les seuls autres coûts :

opérationgas
toute instruction, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC1
POSEIDON2 sur n mots3 + 3 × ⌈n/4⌉ (chaque ligne d’absorption coûte 3)
SHA256, une compression64
KECCAK, une permutation192

La limite déclarée. Une preuve d’appel porte une valeur publique de plus qu’auparavant, GAS_LIMIT, et le circuit (jeu de contraintes 8) prouve que le gas de l’exécution est inférieur ou égal à cette limite. Le gas exact reste privé. Une limite supérieure au plafond de l’en-tête propre à la preuve est refusée avant la construction de toute clé de vérification :

gas_max = (2ᵗ − 1) + 2^(t−2) + 191 × (keccak rows / 32) + 63 × (sha256 rows / 64)

Cela donne 1 279 au palier 10, 5 119 au palier 12 et 20 479 au palier 14 pour un appel qui n’utilise aucune des deux tables de hachage.

Le plancher. Le ledger facture la limite déclarée et les octets, aux prix en vigueur au début du bloc :

fee ≥ BUNDLE_BASE + gas_price × gas_limit + byte_price × ⌈bytes / 1024⌉

bytes est la preuve d’appel plus l’enveloppe d’entrée. Le barème par paliers et sa franchise ne s’appliquent pas sur une telle chain. Le pool exige exactement ce qu’exige un bloc, et refuse un appel moins cher comme FeeTooLow.

Les bundles et la preuve d’autorisation déclarent des limites fixées. Chaque preuve de bundle doit déclarer exactement le bundle_gas_limit du genesis, 20 479 (le plafond du palier 14), faute de quoi la transaction est refusée comme BundleGasLimit. La chain 20, comme toute chain depuis la 17, exécute l’autorisation scindée : chaque bundle porte donc une preuve d’autorisation de palier 10, qui doit déclarer 1 279 (AuthGasLimit). Aucune des deux n’est une charge : un transfert, un bond ou un déploiement paie toujours son plancher fixe du tableau ci-dessus. Le wallet déclare lui-même les deux valeurs.

Les prix varient. La section gas de la chain 20 comporte un contrôleur dynamic : gas_price et byte_price sont donc de l’état du ledger, engagé dans la racine d’état. Une fois par bloc, après ses transactions, chacun évolue selon le remplissage du bloc par rapport à une cible :

price' = min(max_price, max(min_price, price + ⌊price × adjust_bps × (used − target) / (10,000 × target)⌋))

used est plafonné à deux fois la cible, de sorte que adjust_bps est la plus forte variation qu’un bloc puisse produire dans un sens ou dans l’autre. max_price est le plafond du genesis ; les chains 18 et 19 n’en avaient pas. byte_price suit les octets payants du bloc (ci-dessous) ; gas_price suit la somme des limites déclarées par ses appels et ses invokes, plus 20 479 pour chaque bundle.

section gas de la chain 20valeur
gas_price, byte_price à la genèse100 unités par gas ; 800 unités par KiB
min_gas_price, min_byte_price100 ; 800, donc les prix de départ sont aussi les planchers
max_gas_price, max_byte_price10 000 ; 80 000 : les plafonds, 100 fois les planchers (nouveau sur la chain 20)
byte_loadpaying (nouveau sur la chain 20)
target_block_bytes10 485 760, la moitié du plafond de bloc de 20 MiB
target_block_gas262 144
adjust_bps1 250 : au plus 12,5 % par bloc
bundle_gas_limit20 479
meteringcircuit

Un bloc sous ses cibles fait baisser les prix ; sur une chain calme, ils restent donc aux planchers. rand_getLimits renvoie les gas_price et byte_price courants de la tête de chaîne avec gas_metering: "circuit", bundle_gas_limit, adjust_bps et, depuis la v0.6.8, max_gas_price, max_byte_price et byte_load (null sur une chain qui n’en a pas) ; rand_status rapporte les deux mêmes prix sous gas_prices.

Plafonds et charge en octets payants

Les deux sont des champs de genèse de gas.dynamic, nouveaux dans la v0.6.8 (audit v6, POOL-2), et la chain 20 est la première à les définir. Ils corrigent un défaut des chains 18 et 19. Sur celles-ci, byte_price suivait la longueur encodée de chaque transaction, alors que seul un appel le paie. Sept transferts (environ 2,85 MB chacun, frais fixes) remplissent un bloc à environ 1,9 fois la cible en octets et font monter le prix d’environ 11 % par bloc, sans plafond : environ 65 blocs de ce type le portent à 1 000 fois son niveau de départ, et un appel coûte alors environ 1 RAND au lieu d’environ 0,004.

  • Plafonds. max_gas_price et max_byte_price sont des prix que le contrôleur ne dépasse jamais. Le pas est le même, puis borné (gas::next_price_capped) : un prix à son plafond y reste durant toute série de blocs pleins et redescend dès le premier bloc sous la cible. Une genèse doit définir chacun à un niveau supérieur ou égal à son prix de départ. Aux plafonds de la chain 20, l’appel de tier 14 ci-dessus, soit 20 479 gas et 1 319 KiB, a un plancher de 0,3113 RAND, le maximum que son plancher puisse atteindre.
  • La charge en octets payants. Avec byte_load: "paying", le bytes_used du bloc ne compte que les octets sur lesquels le prix par octet est facturé : la preuve et l’enveloppe d’entrée de chaque Call et Invoke (gas::call_bytes), rien d’un transfert, d’un bond ou d’un burn. Un bloc de transferts ne déplace aucun prix par octet. Ledger::block_usage calcule ce chiffre pour le proposeur comme pour le réplica, de sorte que les deux concordent.

Avec la charge en octets payants, un bloc plein de transferts ne donne aucun signal de prix à un appel ; un proposeur honnête réserve donc aussi de la place pour les appels. C’est une politique du nœud, non une règle de validité : tant qu’un call ou un invoke est dans le pool et paie son plancher courant, les transactions à frais fixes prennent au plus les trois quarts des octets du bloc lors de la première passe, puis ce que les appels laissent du dernier quart est comblé avec les transferts mis de côté (Mempool).

Ce que le wallet paie pour un appel

rand call exécute d’abord le programme dans l’émulateur et affiche gas: <exact> (declaring <limit>, tier <t>). Par défaut, il déclare le gas exact arrondi au multiple supérieur de 2^(tier − 2) ; --gas-limit N déclare un chiffre exact et --gas-limit max le plafond de l’en-tête.

Deux éléments font que les frais par défaut dépassent le plancher :

  • La preuve est facturée au plafond. Depuis la chain 16 (hardening_v6, chain 20 comprise), la preuve d’un appel est liée à sa propre transaction ; les frais sont donc fixés avant que la preuve n’existe. Le wallet facture le plafond de preuve de la chain, 4 MiB, à la place de la taille réelle de la preuve (wallet::hardened_call_quote_bytes). Cela ne peut que conduire à payer trop.
  • Deux pas de prix de marge. rand_getLimits renvoie les prix de la tête validée et un appel est inclus deux ou trois blocs plus tard ; par défaut, le wallet multiplie donc le plancher par (1 + adjust_bps / 10,000)², soit 1,125² sur la chain 20.

Mesurés sur la chain 18 le 2026-09-30, quatre appels ont coûté chacun entre 0,0057 et 0,0075 RAND avec ces valeurs par défaut (Example programs). --fee remplace la valeur par défaut ; le wallet refacture la preuve réelle avant de soumettre et refuse des frais inférieurs à leur plancher.

Où vont les frais

La totalité — plancher et pourboire — est créditée aux rewards du proposeur de bloc dans le registre des validateurs, le seul endroit de cette chain qui stocke un montant en clair, à une exception près. Sur une genèse avec tokens.burn_registration_fee (chain 20, comme toute chain depuis la 15), le registration_fee d’un RegisterToken ou d’un RegisterBridgedToken est brûlé et le proposeur conserve fee − registration_fee ; rand_getSupply le compte dans registration_fees_burned. Sur la chain 14, qui n’avait pas ce drapeau, le proposeur conservait la totalité des frais. Rien d’autre n’est brûlé et il n’y a aucun partage. Un validateur transforme les récompenses accumulées en note avec rand-node withdraw (Staking). Les frais de cellule d’un invoke font partie des frais de son bundle et vont au proposeur avec le reste. Il n’y a ni récompense de bloc ni inflation sur la chain 20 : les frais constituent l’intégralité du revenu d’un validateur.

Un frais sur la chain 20 n’est pas en RAND et ne va à aucun validateur. Selon le bridge.fees de la genèse (nouveau dans la v0.6.8), le bridge retient 10 bps de chaque dépôt de zUSD et de chaque burn de zUSD, arrondis à l’unité entière de libération du collatéral inférieure, sous la forme d’une note zUSD envoyée à une adresse de trésorerie que nomme la genèse. Un dépôt crédite au déposant le brut moins les frais ; un burn libère amount − fee sur la chain d’origine. La note de frais reste émise et adossée. Voir Bridge et RPL tokens.

Le mempool ordonne les candidats selon ce qu’ils paient au-dessus de leur plancher par KiB, puis selon les frais (Mempool) ; des frais plus élevés ne comptent donc que sous congestion. Un transfert pèse environ 2,85 MB (mesuré sur la chain 18) : une preuve de bundle de 1,49 MB et une preuve d’autorisation de 1,36 MB, si bien qu’environ sept tiennent dans le bloc de 20 MiB de la chain 20. La remarque du fullnode lui-même était qu’un marché de frais sur les octets — et non sur les opcodes — est ce que le plancher devrait devenir avant mainnet-beta. Le prix par octet de la chain 20 est ce marché pour les appels, déplacé uniquement par les octets que les appels paient ; le plancher d’un transfert reste fixe.

Pourquoi le gas est ici un prix, non une borne

Ethereum mesure chaque opcode parce que chaque nœud exécute chaque transaction et que l’exécution n’a pas de limite propre. Sur Rand, aucun nœud n’exécute quoi que ce soit : l’expéditeur exécute le programme une fois, hors chaîne, et remet à la chain une preuve. Le travail d’un nœud par transaction est une séquence fixe — décoder les champs publics, exécuter les vérifications gratuites, vérifier une STARK — et cette vérification coûte environ 16 ms avec une clé de vérificateur chaude, quoi qu’ait fait le programme. Les trois fonctions du gas se dissocient donc :

  • La terminaison est imposée par le tier : une exécution qui ne s’arrête pas en 2ᵗ − 1 cycles ne peut pas être prouvée du tout, et le prouveur absorbe l’échec.
  • La tarification du travail est ce que fait le plancher de gas, depuis la chain 18. Le coût du réseau par instruction est proche de zéro, mais le travail de preuve de l’expéditeur est réel, et un plancher fixe facturait un appel d’un million de cycles comme un appel de mille cycles. Jusqu’à la chain 18, le plancher était fixe par tier.
  • Le dimensionnement des blocs est un budget en octets (MAX_BLOCK_BYTES = 4 MiB par défaut, le max_block_bytes de la genèse sinon) et un nombre de transactions (2 000).

Deux réserves que le fullnode énonce sans détour : les octets varient bel et bien (une preuve dépasse un mégaoctet, de sorte que l’espace de bloc, et non le calcul, est la ressource rare), et la croissance de l’état — l’arbre, l’ensemble des nullifiers, les octets des programmes déployés — n’est tarifée que par les frais de déploiement par mot et la base fixe, ce qui suffit pour un testnet et mérite d’être revu quand la taille de l’état comptera.

Ce que l’expéditeur paie à la place : la preuve

Le seul coût variable est la production de la preuve STARK, et seule la machine de l’expéditeur le supporte. Par ordre d’effet :

  1. Le tier t ∈ {10, 12, 14, 16, 18, 20} (un appel au-dessus de 14 est refusé sur la chain depuis la v0.5.6 ; un bundle est fixé à 14 et, depuis la chain 17, porte à ses côtés une preuve d’autorisation de tier 10, soit environ 7 s de plus) : la table cpu est complétée à 2ᵗ lignes et chaque pas quadruple à peu près la trace complétée. Mesuré sur l’ordinateur portable de développement : private_payment au tier 10 en environ 7 s ; le guest de bundle à actif masqué au tier 14 en environ 100 s, quel que soit l’actif qu’il déplace.
  2. Les cycles exécutés, parce qu’ils déterminent le tier : les instructions exécutées, non les instructions écrites.
  3. La taille du programme, faiblement : une ligne de condensat par quatre mots au début de la trace.
  4. La taille de l’entrée privée, de la même façon.
  5. Les tables présentes : une table keccak ou sha256 ne coûte la part de ses colonnes que si le guest l’a utilisée.
  6. Le profil FRI : 80 requêtes au lieu de 27 triplent la taille de la preuve mais ne changent presque pas le temps de preuve.

La preuve ne dépend ni des montants, ni du nombre de notes que possède l’expéditeur, ni de la taille de la chain : l’appartenance est prouvée par rapport à un arbre de profondeur fixe, de sorte qu’un transfert coûte la même chose au bloc dix et au bloc dix millions.

Référence

constantevaleur
BUNDLE_BASE1 000 000 unités
BRIDGE_BURN_FEE10 000 000 unités (10 × BUNDLE_BASE)
registration_fee (section tokens de la genèse)1 000 000 000 unités sur la chain 20, brûlées (burn_registration_fee)
DEPLOY_PER_WORD100 000 unités
CALL_BASE, CALL_PER_TIER_STEP1 000 000 ; 100 000 unités ; le barème de paliers d’une chain sans section gas
GAS_PRICE_DEFAULT, BYTE_PRICE_DEFAULT100 unités par gas ; 800 unités par KiB. Prix initiaux de la chain 20 et ses planchers de prix
KECCAK_GAS, SHA256_GAS, POSEIDON2_ABSORB_GAS192 ; 64 ; 3 gas par ligne
max_gas_price, max_byte_price (gas.dynamic de la genèse)10 000 unités par gas ; 80 000 unités par KiB sur la chain 20 ; aucun plafond en l’absence de la clé (chains 18 et 19)
byte_load (gas.dynamic de la genèse)paying sur la chain 20 : seuls les octets des preuves et des enveloppes de call et d’invoke font bouger byte_price ; en l’absence de la clé, les octets de toutes les transactions
cell_fee (program_state de la genèse)10 000 000 unités (0,01 RAND) par cellule créée sur la chain 20 ; au plus 1 000 RAND
bundle_gas_limit (section gas de la genèse)20 479 sur la chain 20, gas_max(14, 0, 0) ; une preuve d’autorisation en déclare 1 279, gas_max(10, 0, 0)
adjust_bps (gas.dynamic de la genèse)1 250 sur la chain 20 ; une genèse peut fixer une valeur de 1 à 5 000
MAX_PROGRAM_WORDS4 096, sauf si la genèse fixe max_program_words (65 535 au plus ; la chain 20 fixe 65 535, et hardening_v6 refuse un programme de plus de 8 180 mots qu’aucun call ne pourrait contenir)
MAX_PROOF_BYTES2 MiB, sauf si la genèse fixe max_proof_bytes (4 MiB sur la chain 20 ; 8 MiB sur les chains 13 à 15)
MAX_BLOCK_BYTES, MAX_BLOCK_TXS4 MiB (sauf si la genèse fixe max_block_bytes ; 20 MiB sur la chain 20), 2 000
paliers10, 12, 14, 16, 18, 20 ; cycles ≤ 2ᵗ − 1
MAX_CALL_TIER14 : une preuve de call au-delà est refusée à l’admission et dans un bloc (depuis la v0.5.6)

Dernière vérification par rapport au commit c0878c3f de fullnode (chaîne 21) le 2026-10-12. Lorsque le code et cette page divergent, c'est le code qui a raison ; signalez-le-nous.