Skip to content

feat(arbitrum-adapter): implement previewSequencerFee via NodeInterface.gasEstimateComponents#21

Merged
mike-diamond merged 1 commit into
mainfrom
feat/arbitrum-sequencer-fee
Jun 1, 2026
Merged

feat(arbitrum-adapter): implement previewSequencerFee via NodeInterface.gasEstimateComponents#21
mike-diamond merged 1 commit into
mainfrom
feat/arbitrum-sequencer-fee

Conversation

@mike-diamond

Copy link
Copy Markdown
Contributor

Summary

Turns the previewSequencerFee skeleton (which returned null) into a live sequencer-fee estimation. This is the alpha.1 hardening the README flagged ("helper bodies harden in alpha.1 with viem"). Scope: ONE function body + its tests + deps/docs. The pure helpers (attachSequencerFeePreview, isSequencerFeePreview, extractSequencerFeePreview) and the bridge / retryable / decoder modules are untouched.

How it works

previewSequencerFee(client, { chain, to, calldata, from?, l1BaseFeeWei? }) reads NodeInterface.gasEstimateComponents (precompile 0xC8) through a caller-supplied viem PublicClient. The precompile simulates the (to, data) call and returns (gasEstimate, gasEstimateForL1, baseFee, l1BaseFeeEstimate).

Fee math (Arbitrum Nitro - both components priced at the L2 base fee, the L1 part is denominated in L2 gas units):

  • l1FeeWei = gasEstimateForL1 * baseFee
  • l2FeeWei = (gasEstimate - gasEstimateForL1) * baseFee
  • totalFeeWei = gasEstimate * baseFee

Every SequencerFeePreview field is filled (l2GasEstimate, l1CalldataBytes, l1BaseFeeWei, l1FeeWei, l2FeeWei, totalFeeWei, isCompressed, previewBlock).

Decisions

  • Signature changed (sanctioned - package is alpha, README warned the surface was unstable). Now async, takes the viem client as the first arg (RPC-agnostic + testable), and requires to because the precompile simulates a call and needs a destination. A calldata-size-only estimate was rejected because it cannot fill the L2 compute fields.
  • One precompile, not two. gasEstimateComponents alone returns the L2 base fee and the L1 base-fee estimate, so ArbGasInfo would be a redundant read. Confirmed with the maintainer.
  • view-marked ABI. The canonical method is payable, but we own the ABI and mark it view so viem readContract accepts it; eth_call ignores declared mutability. Validated live against Arbitrum One mainnet before writing the code (readContract returned [21689n, 523n, 20062000n, 4151436n], a 4-tuple, fee math sane).
  • Errors degrade to null. RPC down, simulated call reverts, or malformed calldata all return null - the preview is advisory wallet UX and must never block signing (the type was already | null).
  • Renamed private isNova -> checkIsNova (predicate-function naming rule); only referenced inside this file.

Verification

pnpm --filter @txkit/arbitrum-adapter typecheck   # clean
pnpm --filter @txkit/arbitrum-adapter test        # 23 passed
pnpm --filter @txkit/arbitrum-adapter build       # ok; viem externalized (import preserved, not bundled)

6 new tests over a mock PublicClient: full field math, correct precompile address/function/args, Nova compression flag, l1BaseFeeWei override, null-on-failure, calldata byte counting.

Deps

viem added as a peer dependency (>=2, mirrors core/react/tx-decoder) + dev dependency (^2.48.4). tsup externalizes it - the ESM bundle keeps import { toHex } from 'viem' and does not inline the library.

Left for alpha.1

  • Nova l1CalldataBytes reports the raw byte count, not the post-Brotli/DAC size (the fee itself is still accurate - gasEstimateForL1 already accounts for the DAC).
  • A revert-resilient ArbGasInfo fallback so the L1 portion can still be shown when the simulated call reverts; plus the decoder-registry expansion and a viem custom-transport integration test.

Not merging - leaving open for review.

…ce.gasEstimateComponents

Turn the previewSequencerFee skeleton (returned null) into a live estimation.

- Reads NodeInterface.gasEstimateComponents (precompile 0xC8) through a
  caller-supplied viem PublicClient. The precompile simulates the (to, data)
  call and returns gasEstimate, gasEstimateForL1, baseFee (L2), and
  l1BaseFeeEstimate. We mark the ABI `view` so viem readContract accepts the
  canonically-payable method; eth_call ignores declared mutability. Verified
  live against Arbitrum One mainnet.
- Fee math (Arbitrum Nitro): both components priced at the L2 base fee.
  l1FeeWei = gasEstimateForL1 * baseFee; l2FeeWei = (gasEstimate -
  gasEstimateForL1) * baseFee; totalFeeWei = gasEstimate * baseFee. Fills
  every SequencerFeePreview field.
- Signature is now async (client, { chain, to, calldata, from?, l1BaseFeeWei? })
  with a required `to` (the precompile needs a destination to simulate) and
  the client injected so the function stays RPC-agnostic and testable. Any
  failure (RPC down, simulated call reverts, malformed calldata) returns null;
  the preview is advisory and must never block signing.
- Rename private isNova -> checkIsNova (predicate-function naming rule).
- Add viem as a peer dependency (>=2); externalized by tsup, not bundled.
- Replace the single stub test with 6 cases over a mock PublicClient: field
  math, correct precompile address/function/args, Nova compression flag,
  l1BaseFeeWei override, null-on-failure, calldata byte counting.

Untouched: attachSequencerFeePreview, isSequencerFeePreview,
extractSequencerFeePreview, and the bridge / retryable / decoder helpers.
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
txkit-docs Ready Ready Preview, Comment Jun 1, 2026 7:27am
txkit-land Ready Ready Preview, Comment Jun 1, 2026 7:27am
txkit-story Ready Ready Preview, Comment Jun 1, 2026 7:27am

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​axe-core/​playwright@​4.11.31001001009770
Addedethers@​6.16.07410010082100
Added@​types/​node@​22.19.171001008195100
Addedtypescript@​5.9.3100100909590
Addedviem@​2.48.89810010098100
Added@​playwright/​test@​1.59.110010010099100

View full report

@mike-diamond mike-diamond merged commit 4264f34 into main Jun 1, 2026
12 of 15 checks passed
@mike-diamond mike-diamond deleted the feat/arbitrum-sequencer-fee branch June 1, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant