Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion x/evm/keeper/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,23 @@ func (k *Keeper) GetEthIntrinsicGas(msg *core.Message, rules params.Rules, isCon
// returned by the EVM execution, thus ignoring the previous intrinsic gas consumed during in the
// AnteHandler.
func (k *Keeper) RefundGas(ctx sdk.Context, msg *core.Message, leftoverGas uint64, denom string) error {
return k.RefundGasWithPrice(ctx, msg, leftoverGas, msg.GasPrice, denom)
}

// RefundGasWithPrice transfers the leftover gas to sender using the provided gas price.
func (k *Keeper) RefundGasWithPrice(
ctx sdk.Context,
msg *core.Message,
leftoverGas uint64,
gasPrice *big.Int,
denom string,
) error {
if gasPrice == nil {
gasPrice = new(big.Int)
}

// Return EVM tokens for remaining gas, exchanged at the original rate.
remaining := new(big.Int).Mul(new(big.Int).SetUint64(leftoverGas), msg.GasPrice)
remaining := new(big.Int).Mul(new(big.Int).SetUint64(leftoverGas), gasPrice)

switch remaining.Sign() {
case -1:
Expand Down
4 changes: 2 additions & 2 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,8 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
if req == nil || req.Msg == nil {
return nil, status.Error(codes.InvalidArgument, "request and message cannot be empty")
}
if req.BaseFee != nil {
baseFee = big.NewInt(req.BaseFee.Int64())
if req.BaseFee != nil && !req.BaseFee.IsNil() {
Comment thread
thomas-nguy marked this conversation as resolved.
baseFee = req.BaseFee.BigInt()
}
resultData, err := execTrace(
c,
Expand Down
39 changes: 21 additions & 18 deletions x/evm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package keeper
import (
"errors"
"fmt"
"math/big"

cmttypes "github.com/cometbft/cometbft/types"

Expand Down Expand Up @@ -318,8 +317,8 @@ func (k *Keeper) ApplyMessage(ctx sdk.Context, msg *core.Message, tracer *tracin
// # debugTrace parameter
//
// The message is applied with steps to mimic AnteHandler
// 1. the sender is consumed with gasLimit * gasPrice in full at the beginning of the execution and
// then refund with unused gas after execution.
// 1. deduct gasLimit * gasPrice (effective gas price) through the fee collector, then refund unused
// gas after execution — same path as CheckEthGasConsume and ApplyTransaction.
// 2. sender nonce is incremented by 1 before execution
func (k *Keeper) ApplyMessageWithConfig(
ctx sdk.Context,
Expand Down Expand Up @@ -356,11 +355,18 @@ func (k *Keeper) ApplyMessageWithConfig(
leftoverGas := msg.GasLimit
sender := msg.From
tracer := cfg.GetTracer()
debugFn := func() {
if tracer != nil && cfg.DebugTrace {
stateDB.AddBalance(sender, uint256.NewInt(1).Mul(uint256.MustFromBig(msg.GasPrice), uint256.NewInt(leftoverGas)), tracing.BalanceIncreaseGasReturn)

if cfg.DebugTrace {
feeAmt := debugTraceFeeAmount(msg, cfg.BaseFee)
if feeAmt.Sign() > 0 {
fees := sdk.Coins{{Denom: cfg.Params.EvmDenom, Amount: sdkmath.NewIntFromBigInt(feeAmt)}}
if err := k.DeductTxCostsFromUserBalance(ctx, fees, msg.From); err != nil {
return nil, err
}
}
tracingStateDB.SetNonce(sender, stateDB.GetNonce(sender)+1, tracing.NonceChangeEoACall)
Comment thread
thomas-nguy marked this conversation as resolved.
}

if tracer != nil {
if tracer.OnGasChange != nil {
tracer.OnGasChange(0, msg.GasLimit, tracing.GasChangeTxInitialBalance)
Expand All @@ -380,20 +386,10 @@ func (k *Keeper) ApplyMessageWithConfig(
}

defer func() {
debugFn()
if tracer.OnTxEnd != nil {
tracer.OnTxEnd(&ethtypes.Receipt{GasUsed: gasUsed}, err)
}
}()

if cfg.DebugTrace {
amount := new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))
stateDB.SubBalance(sender, uint256.MustFromBig(amount), tracing.BalanceDecreaseGasBuy)
if err := stateDB.Error(); err != nil {
return nil, err
}
tracingStateDB.SetNonce(sender, stateDB.GetNonce(sender)+1, tracing.NonceChangeEoACall)
}
}

rules := cfg.Rules
Expand Down Expand Up @@ -539,8 +535,15 @@ func (k *Keeper) ApplyMessageWithConfig(
// reset leftoverGas, to be used by the tracer
leftoverGas = msg.GasLimit - gasUsed

debugFn()
debugFn = func() {}
if cfg.DebugTrace {
debugGasPrice := debugTraceGasPrice(msg, cfg.BaseFee)
if err := k.RefundGasWithPrice(ctx, msg, leftoverGas, debugGasPrice, cfg.Params.EvmDenom); err != nil {
return nil, errorsmod.Wrapf(err, "failed to refund leftover gas to sender %s", msg.From)
}
if tracer != nil && tracer.OnGasChange != nil {
tracer.OnGasChange(leftoverGas, 0, tracing.GasChangeTxLeftOverReturned)
}
}

// The dirty states in `StateDB` is either committed or discarded after return
if commit {
Expand Down
48 changes: 48 additions & 0 deletions x/evm/keeper/state_transition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/evmos/ethermint/x/evm/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -658,6 +659,53 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() {
suite.Require().False(res.Failed())
}

func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig_DebugTraceFee() {
t := suite.T()
suite.SetupTestWithCb(t, func(a *evmd.EthermintApp, genesis evmd.GenesisState) evmd.GenesisState {
feemarketGenesis := feemarkettypes.DefaultGenesisState()
feemarketGenesis.Params.EnableHeight = 1
feemarketGenesis.Params.NoBaseFee = false
genesis[feemarkettypes.ModuleName] = a.AppCodec().MustMarshalJSON(feemarketGenesis)
return genesis
})
Comment thread
thomas-nguy marked this conversation as resolved.
suite.mintFeeCollector = true
suite.SetupTest()

baseFee := big.NewInt(1_000_000_000)
Comment thread
thomas-nguy marked this conversation as resolved.
gasTipCap := big.NewInt(0)
gasFeeCap := big.NewInt(5_000_000_000_000)
gasLimit := uint64(2_000_000)
effectiveGas := new(big.Int).Add(gasTipCap, baseFee)
effectiveFee := new(big.Int).Mul(effectiveGas, new(big.Int).SetUint64(gasLimit))

to := common.BigToAddress(big.NewInt(1))
msg := &core.Message{
From: suite.Address,
To: &to,
Nonce: suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address),
GasLimit: gasLimit,
GasPrice: gasFeeCap, // fee cap, not effective price
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Value: big.NewInt(0),
Data: nil,
SkipNonceChecks: false,
}

cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, suite.App.EvmKeeper.ChainID(), common.Hash{})
suite.Require().NoError(err)
cfg.BaseFee = baseFee
cfg.DebugTrace = true
cfg.TxConfig = suite.App.EvmKeeper.TxConfig(suite.Ctx, common.Hash{})

suite.Require().NoError(
suite.App.EvmKeeper.SetBalance(suite.Ctx, suite.Address, *uint256.MustFromBig(effectiveFee), types.DefaultEVMDenom),
)

_, err = suite.App.EvmKeeper.ApplyMessageWithConfig(suite.Ctx, msg, cfg, false)
suite.Require().NoError(err, "debug trace must deduct effective fee, not fee cap * gas")
}

func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() {
var (
msg *core.Message
Expand Down
21 changes: 21 additions & 0 deletions x/evm/keeper/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,30 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/types"
)

// debugTraceGasPrice returns the effective gas price used by debug tracing.
// It follows the same effective-price rule as ante handling.
func debugTraceGasPrice(msg *core.Message, baseFee *big.Int) *big.Int {
gasPrice := new(big.Int)
if msg.GasPrice != nil {
gasPrice.Set(msg.GasPrice)
}
if baseFee != nil && msg.GasFeeCap != nil && msg.GasTipCap != nil {
gasPrice = ethermint.BigMin(new(big.Int).Add(msg.GasTipCap, baseFee), msg.GasFeeCap)
}
return gasPrice
}

// debugTraceFeeAmount returns the gas fee charged up front during debug tracing.
// It matches the ante handler: effective gas price multiplied by gas limit.
func debugTraceFeeAmount(msg *core.Message, baseFee *big.Int) *big.Int {
gasPrice := debugTraceGasPrice(msg, baseFee)
return new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(msg.GasLimit))
Comment thread
thomas-nguy marked this conversation as resolved.
}

// GetCoinbaseAddress returns the block proposer's validator operator address.
func (k Keeper) GetCoinbaseAddress(ctx sdk.Context) (common.Address, error) {
proposerAddress := sdk.ConsAddress(ctx.BlockHeader().ProposerAddress)
Expand Down
Loading