What
Arc overrides reward_beneficiary to redirect the full fee (base + priority) to the block
beneficiary (crates/evm/src/handler.rs:85). It computes the reward from exec_result.gas().used():
let gas_used = exec_result.gas().used();
let total_fee_amount = U256::from(effective_gas_price) * U256::from(gas_used);
Upstream revm 38 post_execution::reward_beneficiary instead pays for
gas.used().saturating_sub(gas.reservoir()), while reimburse_caller refunds
remaining + reservoir + refunded. The reservoir (EIP-8037 state-gas) is unused gas that must be
returned to the caller and not paid to the beneficiary.
Impact
Today this is latent and harmless: EIP-8037 / Amsterdam is not active on any Arc chain
(ArcChainSpec::is_amsterdam_active_at_timestamp is asserted false in tests), so gas.reservoir()
is always 0 and used() already equals the intended amount.
But the precompile-helper layer is already threading a reservoir value everywhere in anticipation
of EIP-8037. If Amsterdam/EIP-8037 is ever scheduled without also fixing this override, the
beneficiary would be paid for reservoir gas that the caller is simultaneously reimbursed for — a
net native-coin mint per transaction (Arc pays base fee to the proposer rather than burning it, which
makes the double-count real value, not just a burn discrepancy).
Suggested fix
Mirror upstream: pay gas.used().saturating_sub(gas.reservoir()). As a minimum guard until then, add
debug_assert!(exec_result.gas().reservoir() == 0) in reward_beneficiary so the assumption is
enforced the moment the reservoir becomes non-zero.
Environment: main @ 2a3e8ab (v0.8.0), revm 38 / revm-handler 18.1.0.
(Opening as an issue first per CONTRIBUTING.md; happy to be assigned and submit the fix.)
What
Arc overrides
reward_beneficiaryto redirect the full fee (base + priority) to the blockbeneficiary (
crates/evm/src/handler.rs:85). It computes the reward fromexec_result.gas().used():Upstream revm 38
post_execution::reward_beneficiaryinstead pays forgas.used().saturating_sub(gas.reservoir()), whilereimburse_callerrefundsremaining + reservoir + refunded. The reservoir (EIP-8037 state-gas) is unused gas that must bereturned to the caller and not paid to the beneficiary.
Impact
Today this is latent and harmless: EIP-8037 / Amsterdam is not active on any Arc chain
(
ArcChainSpec::is_amsterdam_active_at_timestampis asserted false in tests), sogas.reservoir()is always 0 and
used()already equals the intended amount.But the precompile-helper layer is already threading a
reservoirvalue everywhere in anticipationof EIP-8037. If Amsterdam/EIP-8037 is ever scheduled without also fixing this override, the
beneficiary would be paid for reservoir gas that the caller is simultaneously reimbursed for — a
net native-coin mint per transaction (Arc pays base fee to the proposer rather than burning it, which
makes the double-count real value, not just a burn discrepancy).
Suggested fix
Mirror upstream: pay
gas.used().saturating_sub(gas.reservoir()). As a minimum guard until then, adddebug_assert!(exec_result.gas().reservoir() == 0)inreward_beneficiaryso the assumption isenforced the moment the reservoir becomes non-zero.
Environment: main @
2a3e8ab(v0.8.0), revm 38 / revm-handler 18.1.0.(Opening as an issue first per CONTRIBUTING.md; happy to be assigned and submit the fix.)