diff --git a/core/vm/evm.go b/core/vm/evm.go index c365f637d2..503e25d427 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -245,13 +245,14 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - // Fail if we're trying to transfer more than the available balance - if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) { + syscall := isSystemCall(caller) + + // Fail if we're trying to transfer more than the available balance. + if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) { return nil, gas, ErrInsufficientBalance } snapshot := evm.StateDB.Snapshot() p, isPrecompile := evm.precompile(addr) - if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) { // Add proof of absence to witness @@ -275,8 +276,12 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g } evm.StateDB.CreateAccount(addr) } - evm.Context.Transfer(evm.StateDB, caller, addr, value) - + // Perform the value transfer only in non-syscall mode. + // Calling this is required even for zero-value transfers, + // to ensure the state clearing mechanism is applied. + if !syscall { + evm.Context.Transfer(evm.StateDB, caller, addr, value) + } if isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { @@ -302,7 +307,6 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } - gas = 0 } // TODO: consider clearing up unused snapshots: