Summary
When sending transactions, the CLI/ethutil package hardcodes the gas limit to 30_000_000 instead of letting go-ethereum estimate it. On networks (testnets / L2s) that enforce a maximum gas-per-transaction below 30M, the node rejects the transaction during pre-flight validation with exceeds max transaction gas limit, so it never reaches a block. The fix is to default to gas estimation and expose an optional override flag.
Current behavior
pkg/ethutil/ethutil.go defines a fixed constant:
// Gas limit when sending transactions.
const GasLimit = 30_000_000
Every write path (AddInput, AddInputAsync, ExecuteOutput) forwards this constant, and pkg/ethutil/transaction.go writes it unconditionally onto the transaction options:
// _prepareTransaction
txOpts.GasLimit = gasLimit // always 30_000_000
txOpts.GasPrice = gasPrice
Because go-ethereum's bind package only runs gas estimation when TransactOpts.GasLimit == 0, setting a non-zero value here disables estimation entirely and forces the fixed 30M onto every transaction. 30_000_000 happens to be Ethereum mainnet's block gas limit, which is fine on Anvil/Foundry but too high for networks that cap per-transaction gas below that.
Relevant code:
pkg/ethutil/ethutil.go#L23-L24 (the constant)
pkg/ethutil/transaction.go#L46-L68 (_prepareTransaction setting the limit)
Steps to reproduce
Against a testnet/L2 whose per-transaction gas cap is below 30M:
$ cartesi-rollups-cli execute tester 12 --yes
Error: failed to send transaction: exceeds max transaction gas limit
$ cartesi-rollups-cli send tester 0x68656c6c6f --hex --yes
Error: failed to send transaction: exceeds max transaction gas limit
Both commands fail identically: execute → ExecuteOutput, send → AddInput, both routed through sendTransaction with the same hardcoded GasLimit.
Root cause
The hardcoded GasLimit = 30_000_000 exceeds the maximum gas allowed per transaction on the target network. The node rejects it at the RPC/txpool validation step (before execution), so this is not a revert, an out-of-gas, or an insufficient-funds error — it's the network refusing a transaction whose declared gas limit is too high.
Proposed fix
- Default to estimation. Stop setting a fixed gas limit; leave
TransactOpts.GasLimit = 0 so go-ethereum's bind runs eth_estimateGas (simulating against pending state) and picks an appropriate limit per transaction and per network. Concretely, drop the txOpts.GasLimit = gasLimit assignment (or set it to 0) in _prepareTransaction for the default path.
- Add an optional override. Expose a
--gas-limit flag (and/or an env var) on the relevant CLI commands. When provided and > 0, set TransactOpts.GasLimit to that value (skipping estimation); when omitted, fall through to estimation. This preserves the ability to force a fixed limit when needed.
Additional considerations
- Enabling estimation slightly changes the error surface: a transaction that would always revert now fails earlier at the
eth_estimateGas step instead of after submission. This is generally an improvement (bad transactions are caught before broadcast), but worth noting for anyone parsing error strings.
- Optionally apply a small safety margin on top of the estimate (e.g. estimate × 1.1) to absorb minor state changes between estimation and inclusion. go-ethereum already allows a tiny overestimate, so this is a refinement rather than a requirement.
AddInputAsync already carries a note about serializing nonce assignment; switching to estimation adds one extra RPC round-trip (eth_estimateGas) per call, which is fine for normal use but worth keeping in mind for the load-testing path.
Acceptance criteria
Summary
When sending transactions, the CLI/
ethutilpackage hardcodes the gas limit to30_000_000instead of letting go-ethereum estimate it. On networks (testnets / L2s) that enforce a maximum gas-per-transaction below 30M, the node rejects the transaction during pre-flight validation withexceeds max transaction gas limit, so it never reaches a block. The fix is to default to gas estimation and expose an optional override flag.Current behavior
pkg/ethutil/ethutil.godefines a fixed constant:Every write path (
AddInput,AddInputAsync,ExecuteOutput) forwards this constant, andpkg/ethutil/transaction.gowrites it unconditionally onto the transaction options:Because go-ethereum's
bindpackage only runs gas estimation whenTransactOpts.GasLimit == 0, setting a non-zero value here disables estimation entirely and forces the fixed 30M onto every transaction.30_000_000happens to be Ethereum mainnet's block gas limit, which is fine on Anvil/Foundry but too high for networks that cap per-transaction gas below that.Relevant code:
pkg/ethutil/ethutil.go#L23-L24(the constant)pkg/ethutil/transaction.go#L46-L68(_prepareTransactionsetting the limit)Steps to reproduce
Against a testnet/L2 whose per-transaction gas cap is below 30M:
Both commands fail identically:
execute→ExecuteOutput,send→AddInput, both routed throughsendTransactionwith the same hardcodedGasLimit.Root cause
The hardcoded
GasLimit = 30_000_000exceeds the maximum gas allowed per transaction on the target network. The node rejects it at the RPC/txpool validation step (before execution), so this is not a revert, an out-of-gas, or an insufficient-funds error — it's the network refusing a transaction whose declared gas limit is too high.Proposed fix
TransactOpts.GasLimit = 0so go-ethereum'sbindrunseth_estimateGas(simulating against pending state) and picks an appropriate limit per transaction and per network. Concretely, drop thetxOpts.GasLimit = gasLimitassignment (or set it to0) in_prepareTransactionfor the default path.--gas-limitflag (and/or an env var) on the relevant CLI commands. When provided and> 0, setTransactOpts.GasLimitto that value (skipping estimation); when omitted, fall through to estimation. This preserves the ability to force a fixed limit when needed.Additional considerations
eth_estimateGasstep instead of after submission. This is generally an improvement (bad transactions are caught before broadcast), but worth noting for anyone parsing error strings.AddInputAsyncalready carries a note about serializing nonce assignment; switching to estimation adds one extra RPC round-trip (eth_estimateGas) per call, which is fine for normal use but worth keeping in mind for the load-testing path.Acceptance criteria
exceeds max transaction gas limit).--gas-limitflag (and/or env var) allows forcing a fixed limit.