core: use source-based refund mechanism

This commit is contained in:
Gary Rong 2026-06-16 19:59:58 +08:00
parent 0b3dffcf00
commit 906488333c
5 changed files with 78 additions and 78 deletions

View file

@ -674,11 +674,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
ret []byte ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err vmerr error // vm errors do not effect consensus and are therefore not assigned to err
result vm.GasBudget result vm.GasBudget
// Capture the forwarded regular-gas amount BEFORE ForwardAll consumes
// it, so Absorb can back out state-gas spillover from UsedRegularGas
// per EIP-8037.
forwarded = st.gasRemaining.RegularGas
) )
if contractCreation { if contractCreation {
// Check whether the init code size has been exceeded. // Check whether the init code size has been exceeded.
@ -688,7 +683,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// Execute the transaction's creation. // Execute the transaction's creation.
var creation bool var creation bool
ret, _, result, creation, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value) ret, _, result, creation, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value)
st.gasRemaining.Absorb(result, forwarded) st.gasRemaining.Absorb(result)
// If the contract creation failed, or the destination was pre-existing, // If the contract creation failed, or the destination was pre-existing,
// refund the account-creation state gas pre-charged in IntrinsicGas. // refund the account-creation state gas pre-charged in IntrinsicGas.
@ -712,7 +707,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Execute the transaction's call. // Execute the transaction's call.
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value) ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
st.gasRemaining.Absorb(result, forwarded) st.gasRemaining.Absorb(result)
} }
// Settle down the gas usage and refund the ETH back if any remaining // Settle down the gas usage and refund the ETH back if any remaining

View file

@ -163,9 +163,9 @@ func (c *Contract) refundState(s uint64, logger *tracing.Hooks, reason tracing.G
} }
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state. // refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state.
func (c *Contract) refundGas(child GasBudget, forwarded uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) { func (c *Contract) refundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
prior := c.Gas prior := c.Gas
c.Gas.Absorb(child, forwarded) c.Gas.Absorb(child)
if logger.HasGasHook() && reason != tracing.GasChangeIgnored { if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason) logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
} }

View file

@ -265,7 +265,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) { if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
return nil, gas, ErrInsufficientBalance return nil, gas, ErrInsufficientBalance
} }
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas snapshot := evm.StateDB.Snapshot()
p, isPrecompile := evm.precompile(addr) p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) { if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) { if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
@ -279,7 +279,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false) wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
if _, ok := gas.ChargeRegular(wgas); !ok { if _, ok := gas.ChargeRegular(wgas); !ok {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
return nil, gas.ExitHalt(reservoir), ErrOutOfGas return nil, gas.ExitHalt(), ErrOutOfGas
} }
} }
@ -314,7 +314,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
} }
// Calculate the remaining gas at the end of frame // Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, reservoir) exitGas := gas.Exit(err)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
@ -350,7 +350,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
if !evm.Context.CanTransfer(evm.StateDB, caller, value) { if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
return nil, gas, ErrInsufficientBalance return nil, gas, ErrInsufficientBalance
} }
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas snapshot := evm.StateDB.Snapshot()
// It is allowed to call precompiles, even via delegatecall // It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile { if p, isPrecompile := evm.precompile(addr); isPrecompile {
@ -365,7 +365,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
} }
// Calculate the remaining gas at the end of frame // Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, reservoir) exitGas := gas.Exit(err)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
@ -396,7 +396,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
} }
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas snapshot := evm.StateDB.Snapshot()
// It is allowed to call precompiles, even via delegatecall // It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile { if p, isPrecompile := evm.precompile(addr); isPrecompile {
@ -409,7 +409,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
} }
// Calculate the remaining gas at the end of frame // Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, reservoir) exitGas := gas.Exit(err)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
@ -443,7 +443,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// after all empty accounts were deleted, so this is not required. However, if we omit this, // after all empty accounts were deleted, so this is not required. However, if we omit this,
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
// We could change this, but for now it's left for legacy reasons // We could change this, but for now it's left for legacy reasons
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas snapshot := evm.StateDB.Snapshot()
// We do an AddBalance of zero here, just in order to trigger a touch. // We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
@ -461,7 +461,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
} }
// Calculate the remaining gas at the end of frame // Calculate the remaining gas at the end of frame
exitGas := gas.Exit(err, reservoir) exitGas := gas.Exit(err)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
@ -499,14 +499,13 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
} }
// Increment the caller's nonce after passing all validations // Increment the caller's nonce after passing all validations
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator) evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
reservoir := gas.StateGas
// Charge the contract creation init gas in verkle mode // Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 {
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas) statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas}) prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
if !ok { if !ok {
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas return nil, common.Address{}, gas.ExitHalt(), false, ErrOutOfGas
} }
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck) evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
@ -527,7 +526,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
if evm.StateDB.GetNonce(address) != 0 || if evm.StateDB.GetNonce(address) != 0 ||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) { isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
halt := gas.ExitHalt(reservoir) halt := gas.ExitHalt()
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), halt.AsTracing(), tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(gas.AsTracing(), halt.AsTracing(), tracing.GasChangeCallFailedExecution)
} }
@ -556,7 +555,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas) consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
if consumed < wanted { if consumed < wanted {
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas return nil, common.Address{}, gas.ExitHalt(), false, ErrOutOfGas
} }
prior, _ := gas.Charge(GasCosts{RegularGas: consumed}) prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
@ -581,7 +580,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
exit := contract.Gas.Exit(err, reservoir) exit := contract.Gas.Exit(err)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer.HasGasHook() { if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution) evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)

View file

@ -65,6 +65,11 @@ type GasBudget struct {
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb) StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
UsedRegularGas uint64 // gross regular gas consumed in this frame UsedRegularGas uint64 // gross regular gas consumed in this frame
UsedStateGas int64 // signed net state-gas consumed in this frame UsedStateGas int64 // signed net state-gas consumed in this frame
// StateGasFromGasLeft tracks how much of this frame's regular gas (gas_left)
// has been borrowed to cover state-gas charges that exceeded the reservoir
// (the "spillover").
StateGasFromGasLeft uint64
} }
// NewGasBudget initializes a fresh GasBudget for execution / forwarding, // NewGasBudget initializes a fresh GasBudget for execution / forwarding,
@ -82,7 +87,7 @@ func (g GasBudget) Used(initial GasBudget) uint64 {
// String returns a visual representation of the budget. // String returns a visual representation of the budget.
func (g GasBudget) String() string { func (g GasBudget) String() string {
return fmt.Sprintf("<%v,%v,used=<%v,%v>>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas) return fmt.Sprintf("<%v,%v,used=<%v,%v>,borrowed=%v>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas, g.StateGasFromGasLeft)
} }
// CanAfford reports whether the running balance can cover the given cost. // CanAfford reports whether the running balance can cover the given cost.
@ -117,6 +122,10 @@ func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
spillover := cost.StateGas - g.StateGas spillover := cost.StateGas - g.StateGas
g.StateGas = 0 g.StateGas = 0
g.RegularGas -= spillover g.RegularGas -= spillover
// Record the regular gas borrowed to cover the overflowing state gas
// so a later inline refund can repay it before topping up the reservoir.
g.StateGasFromGasLeft += spillover
} else { } else {
g.StateGas -= cost.StateGas g.StateGas -= cost.StateGas
} }
@ -146,14 +155,27 @@ func (g *GasBudget) IsZero() bool {
} }
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0). // RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
// The reservoir is credited and the signed usage counter is decremented
// in lockstep, preserving the per-frame invariant:
// //
// StateGas + UsedStateGas == initialStateGas + spillover_so_far // Per EIP-8037, the refund repays the regular gas previously borrowed for
// state-gas spillover (tracked by StateGasFromGasLeft) before crediting the
// reservoir: it is returned to RegularGas up to the outstanding borrowed
// amount, and only the remainder tops up StateGas. This keeps the regular and
// state pools from drifting into one another.
// //
// which the revert path relies on for the correct gross refund. // The signed usage counter is decremented by the full refund regardless of the
// split, preserving the per-frame invariant:
//
// StateGas + UsedStateGas == initialStateGas + StateGasFromGasLeft
//
// which the revert and halt paths rely on for the correct gross refund.
func (g *GasBudget) RefundState(s uint64) { func (g *GasBudget) RefundState(s uint64) {
g.StateGas += s // Repay the borrowed regular gas first, capped at the outstanding amount.
repay := min(s, g.StateGasFromGasLeft)
g.RegularGas += repay
g.StateGasFromGasLeft -= repay
// Whatever is left tops up the reservoir.
g.StateGas += s - repay
g.UsedStateGas -= int64(s) g.UsedStateGas -= int64(s)
} }
@ -201,53 +223,46 @@ func (g GasBudget) ExitSuccess() GasBudget {
return g return g
} }
// ExitRevert produces the leftover for a REVERT exit. Per EIP-8037, all state // ExitRevert produces the leftover for a REVERT exit. The frame's state
// gas charged by the reverted frame is refunded to the caller's reservoir: // changes are discarded, so all state gas it charged is refilled to its origin
// // (EIP-8037): up to StateGasFromGasLeft is returned to RegularGas (the regular
// leftover.StateGas = StateGas + UsedStateGas // gas it borrowed), and the remainder restores the reservoir. Because the
// // borrowed regular gas is repaid first, the reservoir is made whole back to its
// UsedStateGas is reset since the frame's state changes are discarded. // start-of-frame value:
func (g GasBudget) ExitRevert() GasBudget { func (g GasBudget) ExitRevert() GasBudget {
reservoir := int64(g.StateGas) + g.UsedStateGas reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.StateGasFromGasLeft)
if reservoir < 0 { if reservoir < 0 {
// Reservoir should never be negative. By construction it equals // Reservoir should never be negative. By construction it equals
// the initial state-gas allocation plus any spillover to regular // the initial state-gas allocation.
// gas.
reservoir = 0 reservoir = 0
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas) log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.StateGasFromGasLeft)
} }
return GasBudget{ return GasBudget{
RegularGas: g.RegularGas, RegularGas: g.RegularGas + g.StateGasFromGasLeft,
StateGas: uint64(reservoir), StateGas: uint64(reservoir),
UsedRegularGas: g.UsedRegularGas, UsedRegularGas: g.UsedRegularGas,
UsedStateGas: 0, UsedStateGas: 0,
} }
} }
// ExitHalt produces the leftover for an exceptional halt. // ExitHalt produces the leftover for an exceptional halt. As with a revert, the
// // frame's state changes are rolled back and its state gas is refilled to origin
// - state_gas_reservoir is reset back to its value at the start of the child frame // (EIP-8037); the difference is that the frame's gas_left is consumed rather
// - the gas_left initially given to the child is consumed (set to zero) // than returned. The portion refilled to RegularGas is therefore burned along
func (g GasBudget) ExitHalt(initStateReservoir uint64) GasBudget { // with the rest of gas_left, leaving only the reservoir portion to survive,
reservoir := int64(g.StateGas) + g.UsedStateGas // which equals the reservoir's value at the start of the frame.
func (g GasBudget) ExitHalt() GasBudget {
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.StateGasFromGasLeft)
if reservoir < 0 { if reservoir < 0 {
// Reservoir should never be negative. By construction it equals // Reservoir should never be negative. By construction it equals
// the initial state-gas allocation plus any spillover to regular // the initial state-gas allocation.
// gas.
reservoir = 0 reservoir = 0
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas) log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.StateGasFromGasLeft)
}
// The portion of state gas charged from regular gas is also burned
// together with the regular gas, rather than being returned to the
// parent's state-gas reservoir.
var spilled uint64
if uint64(reservoir) > initStateReservoir {
spilled = uint64(reservoir) - initStateReservoir
} }
return GasBudget{ return GasBudget{
RegularGas: 0, RegularGas: 0,
StateGas: initStateReservoir, StateGas: uint64(reservoir),
UsedRegularGas: g.UsedRegularGas + g.RegularGas + spilled, UsedRegularGas: g.UsedRegularGas + g.RegularGas + g.StateGasFromGasLeft,
UsedStateGas: 0, UsedStateGas: 0,
} }
} }
@ -258,17 +273,14 @@ func (g GasBudget) ExitHalt(initStateReservoir uint64) GasBudget {
// - err == nil → ExitSuccess // - err == nil → ExitSuccess
// - err == ErrExecutionReverted → ExitRevert // - err == ErrExecutionReverted → ExitRevert
// - any other err → ExitHalt // - any other err → ExitHalt
// func (g GasBudget) Exit(err error) GasBudget {
// Soft validation failures (occurring BEFORE evm.Run) should call Preserved
// directly instead of going through this dispatcher.
func (g GasBudget) Exit(err error, initStateReservoir uint64) GasBudget {
switch { switch {
case err == nil: case err == nil:
return g.ExitSuccess() return g.ExitSuccess()
case err == ErrExecutionReverted: case err == ErrExecutionReverted:
return g.ExitRevert() return g.ExitRevert()
default: default:
return g.ExitHalt(initStateReservoir) return g.ExitHalt()
} }
} }
@ -276,18 +288,12 @@ func (g GasBudget) Exit(err error, initStateReservoir uint64) GasBudget {
// budget. Additionally, it does an EIP-8037 spillover correction: // budget. Additionally, it does an EIP-8037 spillover correction:
// state-gas that spilled into the regular pool inside the child frame is // state-gas that spilled into the regular pool inside the child frame is
// excluded from the UsedRegularGas. // excluded from the UsedRegularGas.
// func (g *GasBudget) Absorb(child GasBudget) {
// spillover = forwarded - child.RegularGas - child.UsedRegularGas
//
// forwarded is the regular-gas amount that was passed to the child at call
// entry (i.e., the regular initial of the child's GasBudget).
func (g *GasBudget) Absorb(child GasBudget, forwarded uint64) {
spillover := forwarded - child.RegularGas - child.UsedRegularGas
g.UsedRegularGas -= child.RegularGas g.UsedRegularGas -= child.RegularGas
g.RegularGas += child.RegularGas g.RegularGas += child.RegularGas
g.StateGas = child.StateGas g.StateGas = child.StateGas
g.UsedStateGas += child.UsedStateGas g.UsedStateGas += child.UsedStateGas
g.UsedRegularGas -= spillover g.UsedRegularGas -= child.StateGasFromGasLeft
g.StateGasFromGasLeft += child.StateGasFromGasLeft
} }

View file

@ -677,7 +677,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Stack.push(&stackvalue) scope.Stack.push(&stackvalue)
// Refund the leftover gas back to current frame // Refund the leftover gas back to current frame
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
// Refund the state gas of account-creation if creation doesn't happen // Refund the state gas of account-creation if creation doesn't happen
if evm.GetRules().IsAmsterdam && !creation { if evm.GetRules().IsAmsterdam && !creation {
@ -715,7 +715,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Stack.push(&stackvalue) scope.Stack.push(&stackvalue)
// Refund the leftover gas back to current frame // Refund the leftover gas back to current frame
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
// Refund the state gas of account-creation if creation doesn't happen // Refund the state gas of account-creation if creation doesn't happen
if evm.GetRules().IsAmsterdam && !creation { if evm.GetRules().IsAmsterdam && !creation {
@ -764,7 +764,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
// If the call frame reverts or halts exceptionally, the charged state-gas // If the call frame reverts or halts exceptionally, the charged state-gas
// is refilled back to the state reservoir in Amsterdam. // is refilled back to the state reservoir in Amsterdam.
@ -805,7 +805,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil
@ -837,7 +837,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil
@ -870,7 +870,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
evm.returnData = ret evm.returnData = ret
return ret, nil return ret, nil