Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

On-chain gas and fees

Measured cost of verifying one UltraHonk proof through the deployed Soroban verifier contract on the Stellar testnet, what drives it, and the levers we control (and the ones we do not).

Per-transaction cost (measured on testnet)

Contract: CCS6Z3VVCKV4F5BCH7VXJLKKWMDROUWOTZYROJ4T26CM7R45SE4IFYI2 (transfer-circuit VK — see docs/soroban-verification.md).

CallOutcomeminResourceFeeFee actually chargedHost instructions
verify_proof — valid transfer proof✅ verified149,208 stroops (0.014921 XLM)134,418 stroops (0.013442 XLM)95,297,789
verify_proof — tampered proof❌ rejected (VerificationFailed)0 (never lands)early exit before crypto
verify_proof — wrong-length proof❌ rejected (ProofParseError)0 (never lands)early exit before crypto
verify_proof — wrong public inputs❌ rejected0 (never lands)early exit before crypto

The simulated minResourceFee (149,208) is the RPC’s own fee estimate and tracks the actual charge (134,418) closely; the estimate carries a safety buffer, and the ledger charges only the resources actually used. Because verify_proof is read-only, the simulation estimate is also the exact cost any submission would carry — you never need to submit to know the price.

Where the fee goes

Soroban fees are resource-based: instructions, ledger I/O, and memory. The measured resource footprint of one verification:

ResourceMeasuredNotes
Instructions95.3 M~100% of the fee. The UltraHonk verification: 28 sumcheck rounds × 26 subrelations, the Shplemini batch opening (65-entry MSM), and the final KZG pairing, all on BN254 via the host’s bn254 crypto functions.
Disk read0 BVerification is read-only.
Write0 BThe verifier writes nothing.
Ledger keys2 readThe VK (1,760 B) read from instance storage.

There is no ledger I/O cost to optimize — the fee is almost entirely CPU (the 95 M instructions above). The proof itself (14,592 B) and public inputs (288 B) travel as invocation arguments; at ~10⁷ stroops per 100 M instructions, argument bytes are noise next to the crypto.

The cheap-fail property (the caller-side optimization that matters)

The contract validates before it computes: proof length is checked first (ProofParseError), then the VK is loaded, then — and only then — the 95 M-instruction cryptography runs. Consequences:

  • Tampered, truncated, or malformed proofs cost nothing — they are rejected in simulation for free and, if submitted, fail at base fee only.
  • Pre-validate before submitting. The live client (LiveSorobanClient::verify_proof_with_cost) already resolves the verdict by simulation, so the standard verification path pays exactly zero. Submission is only needed when an on-chain consumer must witness the proof, and only a proof that already simulated as valid should ever be submitted.
  • Budget with the oracle. verify_proof_with_cost returns the RPC’s minResourceFee and resource breakdown alongside the verdict, so callers can budget per-transaction cost without spending anything.

What we already do (and should not change)

  • Minimal calldata. The client sends the raw wire bytes — 288 B of public inputs, 14,592 B proof — with no envelope overhead. The SorobanPayload version/count prefix exists only for local round-tripping and never reaches the contract.
  • One op, no auth. A verification is a single InvokeHostFunction operation with an empty authorization vector.
  • No storage writes — the verifier never mutates state, so there is no write-footprint fee component.

What we cannot optimize (honest boundary)

The 95 M instructions are the fixed cost of any on-chain UltraHonk verification using this verifier: the contract is Nethermind’s audited implementation with an immutable VK and no admin path. Rewriting its cryptography (e.g. hand-optimizing the MSM or transcript) to chase gas would void the audit and the byte-for-byte agreement with bb, and is explicitly out of scope. The levers that could actually move this number are upstream:

  • A newer audited verifier tracking a Barretenberg version with a more efficient proof/verifier layout (the current one is pinned to the bb 0.87.0 byte format the contract was audited against).
  • Recursive/aggregated proofs (verify many transfers with one UltraHonk proof) — a proving-system change in the circuits, not a contract change.
  • Protocol-level host improvements (faster BN254 MSM/pairing in the Soroban host).

Re-benchmarking

The benchmark is a network-gated live test; it needs no funded account and pays nothing (simulation only):

CRUCIBLE_SOROBAN_LIVE=1 cargo test -p crucible-soroban-adapter --test live \
  -- --nocapture onchain_cost_report

Expected output:

on-chain cost report (testnet, transfer circuit):
  valid proof   : verified · minResourceFee 149208 stroops (0.014921 XLM) · 95297789 instructions · 0 B read · 0 B written · 2 read keys
  tampered proof: rejected (fee 0, 0 instructions before early exit)

The numbers above were captured 2026-09-08 on the Stellar public testnet (ledger ~4,569,705). They will move as the network’s fee parameters and host pricing evolve; re-run the benchmark before quoting them.