Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a829654
fixed trace_transaction (yet to test)
AryaLanjewar3005 Sep 29, 2025
23f8d3c
fixed predecessor issue
AryaLanjewar3005 Oct 7, 2025
421162e
Removed Debug logs
AryaLanjewar3005 Oct 7, 2025
79018a1
removed hardcoded gasPrice from parseDerivedTxFromAdditionalFieldsFor…
AryaLanjewar3005 Oct 14, 2025
c8c00ee
Merge branch 'fix/trace_transaction' into fix/trace
AryaLanjewar3005 Dec 2, 2025
f5900f1
fixed v, r and s Error
AryaLanjewar3005 Dec 5, 2025
14d899f
gas buffer added to parseDerivedTxFromAdditionalForTrace
AryaLanjewar3005 Dec 5, 2025
1bdb7fd
added gasLimit emission event
AryaLanjewar3005 Dec 8, 2025
3cfc2b9
BurnBaseFee in ApplyTransaction after RefundGas
AryaLanjewar3005 Dec 10, 2025
61e6b5d
Merge remote-tracking branch 'origin/feat/derived-tx' into feat/burnB…
AryaLanjewar3005 Jan 29, 2026
e1e7248
evm module directly burns the baseFee; route for baseFee from feeColl…
AryaLanjewar3005 Jan 29, 2026
f7ffdde
RefundGas logic to include baseFeeBurn for validators
AryaLanjewar3005 Feb 6, 2026
8c9a1ce
fixed the failing testcase
AryaLanjewar3005 Feb 6, 2026
89c1d16
Fixed: TestEthereumTx mints FeeCollector to test new baseFeeBurn logic
AryaLanjewar3005 Feb 6, 2026
fa14635
Changes to setup_test.go to mint FeeCollector
AryaLanjewar3005 Feb 6, 2026
aca2018
Merge remote-tracking branch 'origin/feat/derived-tx' into feat/burnB…
AryaLanjewar3005 Feb 6, 2026
63fbb0f
fix: failing TestEthereumTx testcase
AryaLanjewar3005 Feb 10, 2026
afd5602
working fix
AryaLanjewar3005 Feb 10, 2026
2fd7227
final fix to refundGas issue
AryaLanjewar3005 Feb 10, 2026
33f7180
fix trace unit test
AryaLanjewar3005 Feb 10, 2026
65daae4
fix: bank extension suite failure
AryaLanjewar3005 Feb 10, 2026
c3f69b9
fixed hardcoded value in TestEthereumTx
AryaLanjewar3005 Feb 18, 2026
823606a
refactor: fixed the old comment
0xNilesh Feb 18, 2026
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
1 change: 1 addition & 0 deletions evmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"github.com/cosmos/evm/x/feemarket"
feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper"
feemarkettypes "github.com/cosmos/evm/x/feemarket/types"

Check failure on line 35 in evmd/app.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not properly formatted (gci)
// NOTE: override ICS20 keeper to support IBC transfers of ERC20 tokens
"github.com/cosmos/evm/x/ibc/transfer"
transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper"
Expand Down
10 changes: 8 additions & 2 deletions precompiles/bank/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ var _ = Describe("Bank Extension -", func() {
contractData ContractData
passCheck testutil.LogCheckArgs

cosmosEVMTotalSupply, _ = new(big.Int).SetString("200003000000000000000000", 10)
xmplTotalSupply, _ = new(big.Int).SetString("200000000000000000000000", 10)
cosmosEVMTotalSupply *big.Int
xmplTotalSupply *big.Int
)

BeforeEach(func() {
Expand Down Expand Up @@ -146,6 +146,12 @@ var _ = Describe("Bank Extension -", func() {

err = is.network.NextBlock()
Expect(err).ToNot(HaveOccurred(), "failed to advance block")

// Get actual total supply after contract deployment
totSupplRes, err := is.grpcHandler.GetTotalSupply()
Expect(err).ToNot(HaveOccurred(), "failed to get total supply")
cosmosEVMTotalSupply = totSupplRes.Supply.AmountOf(is.bondDenom).BigInt()
xmplTotalSupply = totSupplRes.Supply.AmountOf(is.tokenDenom).BigInt()
})

Context("Direct precompile queries", func() {
Expand Down
1 change: 1 addition & 0 deletions rpc/backend/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

var predecessors []*evmtypes.MsgEthereumTx
for i := 0; i < int(transaction.TxIndex); i++ {
predecessorTx, txAdditional, err := b.GetTxByTxIndex(blk.Block.Height, uint(i))

Check failure on line 53 in rpc/backend/tracing.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

G115: integer overflow conversion int -> uint (gosec)
if err != nil {
b.logger.Debug("failed to get tx by index",
"height", blk.Block.Height,
Expand Down Expand Up @@ -127,6 +127,7 @@

// add predecessor messages in current cosmos tx
index := int(transaction.MsgIndex) // #nosec G115

for i := 0; i < index; i++ {
msg := tx.GetMsgs()[i]
// Check if it's a normal Ethereum tx
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/tracing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (suite *BackendTestSuite) TestTraceTransaction() {
)
_, err := RegisterBlockMultipleTxs(client, height, []types.Tx{txBz, txBz2})
suite.Require().NoError(err)
RegisterTraceTransactionWithPredecessors(queryClient, msgEthereumTx, []*evmtypes.MsgEthereumTx{msgEthereumTx})
RegisterTraceTransactionWithPredecessors(queryClient, msgEthereumTx, nil)
RegisterConsensusParams(client, height)
},
&types.Block{Header: types.Header{Height: 1, ChainID: ChainID}, Data: types.Data{Txs: []types.Tx{txBz, txBz2}}},
Expand Down
32 changes: 26 additions & 6 deletions x/vm/keeper/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,34 @@ func (k *Keeper) GetEthIntrinsicGas(ctx sdk.Context, msg core.Message, cfg *para
return core.IntrinsicGas(msg.Data(), msg.AccessList(), isContractCreation, homestead, istanbul)
}

// RefundGas transfers the leftover gas to the sender of the message, capped to half of the total gas
// RefundGas transfers the leftover gas and baseFee amount to the sender of the message, capped to half of the total gas
// consumed in the transaction. Additionally, the function sets the total gas consumed to the value
// 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 EVM tokens for remaining gas, exchanged at the original rate.
// and then burns baseFee amount from the sender account.
func (k *Keeper) RefundGas(ctx sdk.Context, msg core.Message, leftoverGas uint64, gasUsed uint64, baseFee *big.Int, denom string) error {
if msg.GasPrice().Sign() < 0 {
return errorsmod.Wrapf(types.ErrInvalidRefund, "gas price cannot be negative %d", msg.GasPrice().Int64())
}

// refundable amount for leftover gas: leftoverGas * effectiveGasPrice
remaining := new(big.Int).Mul(new(big.Int).SetUint64(leftoverGas), msg.GasPrice())

switch remaining.Sign() {
// refundable amount for base fee: baseFee * gasUsed
baseFeeRefund := big.NewInt(0)
if msg.GasPrice().Sign() > 0 && baseFee != nil && baseFee.Sign() > 0 && gasUsed > 0 {
baseFeeRefund = new(big.Int).Mul(baseFee, new(big.Int).SetUint64(gasUsed))
}

refundAmt := new(big.Int).Add(remaining, baseFeeRefund)

switch refundAmt.Sign() {
case -1:
// negative refund errors
return errorsmod.Wrapf(types.ErrInvalidRefund, "refunded amount value cannot be negative %d", remaining.Int64())
return errorsmod.Wrapf(types.ErrInvalidRefund, "refunded amount value cannot be negative %d", refundAmt.Int64())
case 1:
// positive amount refund
refundedCoins := sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(remaining))}
refundedCoins := sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(refundAmt))}

// refund to sender from the fee collector module account, which is the escrow account in charge of collecting tx fees
err := k.bankWrapper.SendCoinsFromModuleToAccount(ctx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins)
Expand All @@ -51,6 +64,13 @@ func (k *Keeper) RefundGas(ctx sdk.Context, msg core.Message, leftoverGas uint64
// no refund, consume gas and update the tx gas meter
}

// burn baseFee * gasUsed from the sender account after refund
if baseFeeRefund.Sign() > 0 {
if err := k.bankWrapper.BurnAmountFromAccount(ctx, msg.From().Bytes(), baseFeeRefund); err != nil {
return errorsmod.Wrapf(err, "failed to burn base fee from sender %s", msg.From().Hex())
}
}

return nil
}

Expand Down
34 changes: 32 additions & 2 deletions x/vm/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import (
"math/big"

ethparams "github.com/ethereum/go-ethereum/params"

sdkmath "cosmossdk.io/math"

Check failure on line 8 in x/vm/keeper/msg_server_test.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not properly formatted (gci)
"github.com/cosmos/evm/testutil/integration/os/utils"
"github.com/cosmos/evm/x/vm/types"

Expand All @@ -13,7 +16,11 @@

func (suite *KeeperTestSuite) TestEthereumTx() {
suite.enableFeemarket = true
defer func() { suite.enableFeemarket = false }()
suite.mintFeeCollector = true
defer func() {
suite.enableFeemarket = false
suite.mintFeeCollector = false
}()
suite.SetupTest()
testCases := []struct {
name string
Expand Down Expand Up @@ -53,6 +60,30 @@
suite.Run(tc.name, func() {
msg := tc.getMsg()

// Ensure fee collector has sufficient balance for each subtest
if suite.mintFeeCollector {
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
denom := types.GetEVMCoinExtendedDenom()
currentBalance := suite.network.App.BankKeeper.GetBalance(suite.network.GetContext(), feeCollectorAddr, denom)

baseFee := suite.network.App.EVMKeeper.GetBaseFee(suite.network.GetContext())
if baseFee == nil {
baseFee = big.NewInt(0)
}

gasLimit := new(big.Int).SetUint64(msg.GetGas())
requiredBalance := sdkmath.NewIntFromBigInt(new(big.Int).Mul(gasLimit, baseFee)).
Add(sdkmath.NewIntFromUint64(ethparams.TxGas - 1))

if currentBalance.Amount.LT(requiredBalance) {
coinsToAdd := sdktypes.NewCoins(sdktypes.NewCoin(denom, requiredBalance.Sub(currentBalance.Amount)))
err := suite.network.App.BankKeeper.MintCoins(suite.network.GetContext(), types.ModuleName, coinsToAdd)
suite.Require().NoError(err)
err = suite.network.App.BankKeeper.SendCoinsFromModuleToModule(suite.network.GetContext(), types.ModuleName, authtypes.FeeCollectorName, coinsToAdd)
suite.Require().NoError(err)
}
}

// Function to be tested
res, err := suite.network.App.EVMKeeper.EthereumTx(suite.network.GetContext(), msg)

Expand All @@ -76,7 +107,6 @@
suite.Require().NoError(err)
})
}
suite.enableFeemarket = false
}

func (suite *KeeperTestSuite) TestUpdateParams() {
Expand Down
25 changes: 15 additions & 10 deletions x/vm/keeper/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"math"
"testing"

"github.com/ethereum/go-ethereum/params"
ethparams "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/suite"

"github.com/cosmos/evm/testutil/integration/os/factory"
Expand Down Expand Up @@ -56,17 +56,22 @@ func (suite *KeeperTestSuite) SetupTest() {
// Set custom balance based on test params
customGenesis := network.CustomGenesisState{}
feemarketGenesis := feemarkettypes.DefaultGenesisState()
if s.enableFeemarket {
if suite.enableFeemarket {
feemarketGenesis.Params.EnableHeight = 1
feemarketGenesis.Params.NoBaseFee = false
} else {
feemarketGenesis.Params.NoBaseFee = true
}
customGenesis[feemarkettypes.ModuleName] = feemarketGenesis

if s.mintFeeCollector {
// mint some coin to fee collector
coins := sdk.NewCoins(sdk.NewCoin(evmtypes.GetEVMCoinDenom(), sdkmath.NewInt(int64(params.TxGas)-1)))
if suite.mintFeeCollector {
// Mint coins to fee collector for gas refunds
baseFee := feemarketGenesis.Params.BaseFee.TruncateInt()
gasUsed := sdkmath.NewIntFromUint64(ethparams.TxGas)
refundBuffer := sdkmath.NewIntFromUint64(ethparams.TxGas - 1)
requiredBalance := gasUsed.Mul(baseFee).Add(refundBuffer)

coins := sdk.NewCoins(sdk.NewCoin(evmtypes.GetEVMCoinExtendedDenom(), requiredBalance))
balances := []banktypes.Balance{
{
Address: authtypes.NewModuleAddress(authtypes.FeeCollectorName).String(),
Expand All @@ -85,13 +90,13 @@ func (suite *KeeperTestSuite) SetupTest() {
gh := grpc.NewIntegrationHandler(nw)
tf := factory.New(nw, gh)

s.network = nw
s.factory = tf
s.handler = gh
s.keyring = keys
suite.network = nw
suite.factory = tf
suite.handler = gh
suite.keyring = keys

chainConfig := evmtypes.DefaultChainConfig(suite.network.GetChainID())
if !s.enableLondonHF {
if !suite.enableLondonHF {
maxInt := sdkmath.NewInt(math.MaxInt64)
chainConfig.LondonBlock = &maxInt
chainConfig.ArrowGlacierBlock = &maxInt
Expand Down
2 changes: 1 addition & 1 deletion x/vm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t
evmDenom := types.GetEVMCoinDenom()

// refund gas in order to match the Ethereum gas consumption instead of the default SDK one.
if err = k.RefundGas(ctx, msg, msg.Gas()-res.GasUsed, evmDenom); err != nil {
if err = k.RefundGas(ctx, msg, msg.Gas()-res.GasUsed, res.GasUsed, cfg.BaseFee, evmDenom); err != nil {
return nil, errorsmod.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From())
}

Expand Down
3 changes: 3 additions & 0 deletions x/vm/keeper/state_transition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,14 @@ func (suite *KeeperTestSuite) TestRefundGas() {
gasUsed := transactionGas - tc.leftoverGas
refund := keeper.GasToRefund(vmdb.GetRefund(), gasUsed, tc.refundQuotient)
suite.Require().Equal(tc.expGasRefund, refund)
baseFee := big.NewInt(1)

err = unitNetwork.App.EVMKeeper.RefundGas(
unitNetwork.GetContext(),
coreMsg,
refund,
gasUsed,
baseFee,
unitNetwork.GetBaseDenom(),
)

Expand Down
Loading