Skip to content

fix(seismic-rpc): effective balance on eth_getAccountInfo (Veridise 1206)#412

Open
samlaf wants to merge 2 commits into
veridise-audit-april-2026from
fix-getaccountinfo-effective-balance
Open

fix(seismic-rpc): effective balance on eth_getAccountInfo (Veridise 1206)#412
samlaf wants to merge 2 commits into
veridise-audit-april-2026from
fix-getaccountinfo-effective-balance

Conversation

@samlaf

@samlaf samlaf commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Fixes Veridise-1206: Account inspection RPCs return native balance instead of Seismic effective balance

Built on top of #411 since we use its effective_balance function. Should be merged into veridise-audit branch after that one is merged.

The account-surface RPCs split into two contracts, and the balance each reports should follow that split:

  • Spendability view ("what can this account spend"): eth_getBalance and eth_getAccountInfo. Wallet-facing, so they report the effective balance, max(native, usdc_scaled).
  • Committed account record: eth_getProof and eth_getAccount. These expose the raw trie leaf, so they report the raw native balance.

eth_getAccountInfo (balance, nonce, code) was inheriting the upstream default and returning the raw native balance, so a USDC-only account showed zero there while eth_getBalance reported it correctly. Override get_account_info in SeismicEthApi to report the effective balance, mirroring the upstream default and swapping only the balance field.

eth_getProof and eth_getAccount deliberately stay native:

  • eth_getProof returns a Merkle proof against the state root; the balance is part of the proven account leaf, so a synthetic value would make the proof unverifiable (the leaf would no longer hash to the proven path).
  • eth_getAccount mirrors the raw trie record (it also returns storage_root and code_hash) and is kept consistent with eth_getProof, so the two raw-state endpoints never disagree with each other or with the state root. Swapping only its balance would leave the record self-inconsistent with its own storage_root/code_hash.

The native-vs-effective divide is documented on the override so future account-surface endpoints follow it deliberately rather than by accident.

samlaf added 2 commits June 15, 2026 23:50
Gas on Seismic can be paid in native token or USDC, but a transaction's
`value` is always a native transfer. The validator collapsed both balances
into `max(native, usdc_scaled)` and compared that to the full cost
(gas + value), conflating the two: it admitted USDC-rich senders whose
native balance can't cover `value` (the tx is then unminable and stuck),
and rejected senders who could legitimately pay `value` from native and gas
from USDC.

Make affordability component-wise, with usdc.rs as the single source of
truth:

- can_afford(native, usdc, gas_cost, value): native must cover value; gas
  is paid from one token (remaining native or USDC), never split.
- gas_allowance(...): the same rule for eth_estimateGas.
- effective_balance is kept but redocumented as the display-only convention
  behind eth_getBalance, not an affordability check.

Consumers:

- validator: gate admission on can_afford instead of cost > max(...).
- caller_gas_allowance in seismic-rpc (eth_estimateGas): delegate to
  gas_allowance, removing the duplicated inline max()-value recipe.

The pool tracks a single balance scalar per sender and can't express the
component-wise rule, so the validator's Valid outcome and SeismicBalanceHook
now report native + usdc -- a sound upper bound (can_afford => cost <=
native + usdc) that stops admitted split-balance txs from stranding in the
Queued subpool. It over-approximates only when an account overcommits its
native balance across multiple value-bearing txs; block building stays the
exact gate.

Add unit tests for can_afford/gas_allowance and validator tests for the
accept/reject cases. Also tidies a drifted reth_provider import in
seismic-rpc transaction.rs.

Refs: Veridise 1224 (also resolves 1172 item 1: effective_balance now has a
single documented purpose)
…206)

The account-surface RPCs split into two contracts, and the balance each
reports should follow that split:

- Spendability view ("what can this account spend"): eth_getBalance and
  eth_getAccountInfo. Wallet-facing, so they report the effective balance,
  max(native, usdc_scaled).
- Committed account record: eth_getProof and eth_getAccount. These expose
  the raw trie leaf, so they report the raw native balance.

eth_getAccountInfo (balance, nonce, code) was inheriting the upstream default
and returning the raw native balance, so a USDC-only account showed zero
there while eth_getBalance reported it correctly. Override get_account_info
in SeismicEthApi to report the effective balance, mirroring the upstream
default and swapping only the balance field.

eth_getProof and eth_getAccount deliberately stay native:

- eth_getProof returns a Merkle proof against the state root; the balance is
  part of the proven account leaf, so a synthetic value would make the proof
  unverifiable (the leaf would no longer hash to the proven path).
- eth_getAccount mirrors the raw trie record (it also returns storage_root
  and code_hash) and is kept consistent with eth_getProof, so the two
  raw-state endpoints never disagree with each other or with the state root.
  Swapping only its balance would leave the record self-inconsistent with
  its own storage_root/code_hash.

The native-vs-effective divide is documented on the override so future
account-surface endpoints follow it deliberately rather than by accident.

Extends the e2e gas/call-variants test: for a USDC-only account,
eth_getAccountInfo balance matches eth_getBalance and is non-zero, while
eth_getAccount does not report the effective balance.

Partially fixed: getProof/getAccount kept native with documented rationale.
@samlaf samlaf requested a review from cdrappi as a code owner June 15, 2026 16:18
@github-actions

Copy link
Copy Markdown
Contributor

Adds eth_getAccountInfo RPC method that reports effective (USDC-inclusive) balance, consistent with eth_getBalance.

LGTM - this implementation follows established patterns and includes comprehensive test coverage. The distinction between effective balance for wallet-facing endpoints vs. raw native balance for trie-structural endpoints is well-documented and correctly implemented.

Base automatically changed from fix-usdc-value-affordability to veridise-audit-april-2026 June 16, 2026 10:00
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