Skip to content

fix(rpc): eth_estimateGas returns ~6x inflated gas for calls array format #3178

@decofe

Description

@decofe

Summary

eth_estimateGas returns ~305k gas for a TIP-20 transfer when the request uses the calls array format, vs ~50k when using to+data format. Since viem always converts to calls format for Tempo TXs, every gas estimate from the wallet is inflated ~6x.

Repro

# Correct: to+data format → ~53k ✅
curl -s -X POST https://rpc.moderato.tempo.xyz \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{"type":"0x45","from":"0xa522504eebb35e9e1dbd02ffd37dffdb24afec3b","to":"0x20c0000000000000000000000000000000000002","data":"0xa9059cbb0000000000000000000000003e144273d5e4a115c80d46b84d0c5eedbc361a4a00000000000000000000000000000000000000000000000000000000000f4240","nonce":"0x0","nonceKey":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","validBefore":"0x69b9b005","feeToken":"0x20c0000000000000000000000000000000000002"}],"id":1}' | jq .

# Inflated: calls format → ~309k ❌
curl -s -X POST https://rpc.moderato.tempo.xyz \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{"type":"0x45","from":"0xa522504eebb35e9e1dbd02ffd37dffdb24afec3b","calls":[{"to":"0x20c0000000000000000000000000000000000002","data":"0xa9059cbb0000000000000000000000003e144273d5e4a115c80d46b84d0c5eedbc361a4a00000000000000000000000000000000000000000000000000000000000f4240","value":"0x0"}],"nonce":"0x0","nonceKey":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","validBefore":"0x69b9b005","feeToken":"0x20c0000000000000000000000000000000000002"}],"id":1}' | jq .

Root Cause

In crates/revm/src/handler.rs:739:

if !nonce_key.is_zero() && tx.kind().is_create() && caller_account.nonce() == 0 {
    evm.initial_gas += cfg.gas_params().get(GasId::new_account_cost()); // +250,000
}

When calls format is used, inner.to is None (viem clears to/data and wraps them in a calls array). This makes tx.kind() return Create. Combined with expiring nonce (nonce = 0), this incorrectly triggers +250,000 gas (new_account_cost) even though the actual calls in aa_calls are all CALLs, not contract deployments.

When to+data is sent directly, inner.to is set, so tx.kind() returns Call and the check is correctly skipped.

Impact

  • Every Tempo TX gas estimate from viem/wagmi is ~6x too high for TIP-20 transfers
  • TIP-1010 targets ~50k gas / $0.001 per transfer, but wallets show ~$0.006
  • Affects max-send calculations (users cannot send their full balance)

Suggested Fix

For AA transactions, this check should inspect aa_calls instead of tx.kind() from the inner TxEnv — tx.kind() is meaningless for AA batch transactions since execute_multi_call overrides it per-call anyway.

Workaround

Call eth_estimateGas with to+data format directly, bypassing viem's formatter which converts to calls.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions