From f279d0d66954e46c373e4800125c055d8bf871f6 Mon Sep 17 00:00:00 2001 From: Wen <113942165+wen-coding@users.noreply.github.com> Date: Tue, 5 May 2026 06:18:46 -0700 Subject: [PATCH] giga: report EIP-1559 effective gas price on receipt (CON-256) (#3384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GIGA's `executeEVMTxWithGigaExecutor` was passing `ethTx.GasPrice()` into the `core.Message` used to construct the receipt. For dynamic-fee (EIP-1559) txs `Transaction.GasPrice()` returns `GasFeeCap`, so the receipt wrongly reported `maxFee` for any tx that actually paid `baseFee + tip < maxFee`. This breaks EIP-1559 RPC semantics for clients (ethers, hardhat-ethers, etc.) that read `receipt.effectiveGasPrice` to compute spend / display per-tx cost. ## What's actually wrong V2's `x/evm/keeper/msg_server.go` constructs the message via `server.GetEVMMessage(ctx, tx, sender)`, which has the EIP-1559 adjustment built in: ```go baseFee := k.GetBaseFee(ctx) if baseFee != nil { msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee) if msg.GasPrice.Cmp(msg.GasFeeCap) > 0 { msg.GasPrice = msg.GasFeeCap } } ``` GIGA's path hand-rolls a `core.Message` literal in `executeEVMTxWithGigaExecutor` and missed this step. ## The fix Use the already-computed `effectiveGasPrice` (line 1866 of `app/app.go`, which already does `min(baseFee + tipCap, feeCap)` and is what the chain actually charges via `stateDB.SubBalance` at line 1871) when constructing the receipt's `core.Message`. The chain's actual fee charge has always been correct — only the value reported on the receipt was wrong. ## Things done - [x] Single-line code change in `app/app.go executeEVMTxWithGigaExecutor`: `GasPrice: ethTx.GasPrice()` → `GasPrice: effectiveGasPrice` - [x] Unit test in `giga/deps/xevm/keeper/receipt_test.go` documents the `WriteReceipt` contract: `receipt.EffectiveGasPrice == msg.GasPrice` (caller is responsible for passing the EIP-1559 effective price) - [x] `gofmt -s` clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Masih H. Derkani (cherry picked from commit 7004ef10a052254eb5e89a28d30cf505033a68f7) --- app/app.go | 10 ++++-- giga/deps/xevm/keeper/receipt_test.go | 49 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/app/app.go b/app/app.go index d335cc432d..99e3e57174 100644 --- a/app/app.go +++ b/app/app.go @@ -1911,12 +1911,16 @@ func (app *App) executeEVMTxWithGigaExecutor(ctx sdk.Context, msg *evmtypes.MsgE vmError = execResult.Err.Error() } - // Create core.Message from ethTx for WriteReceipt - // WriteReceipt needs msg for GasPrice, To, From, Data, Nonce fields + // Create core.Message from ethTx for WriteReceipt. + // GasPrice must be the EIP-1559 effective gas price (min(baseFee+tip, + // maxFee)) — that's what the chain actually charges (see line 1866) + // and what the receipt's EffectiveGasPrice field needs to report. + // ethTx.GasPrice() returns GasFeeCap for dynamic-fee txs, which puts + // the wrong value on the receipt and breaks EIP-1559 clients. evmMsg := &core.Message{ Nonce: ethTx.Nonce(), GasLimit: ethTx.Gas(), - GasPrice: ethTx.GasPrice(), + GasPrice: effectiveGasPrice, GasFeeCap: ethTx.GasFeeCap(), GasTipCap: ethTx.GasTipCap(), To: ethTx.To(), diff --git a/giga/deps/xevm/keeper/receipt_test.go b/giga/deps/xevm/keeper/receipt_test.go index a11fdf1584..a6ea19e48f 100644 --- a/giga/deps/xevm/keeper/receipt_test.go +++ b/giga/deps/xevm/keeper/receipt_test.go @@ -1,11 +1,14 @@ package keeper_test import ( + "math/big" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" testkeeper "github.com/sei-protocol/sei-chain/giga/deps/testutil/keeper" + "github.com/sei-protocol/sei-chain/giga/deps/xevm/state" "github.com/sei-protocol/sei-chain/giga/deps/xevm/types" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/stretchr/testify/require" @@ -39,3 +42,49 @@ func TestDeleteTransientReceipt(t *testing.T) { require.Nil(t, receipt) require.Equal(t, "receipt not found", err.Error()) } + +// TestWriteReceiptStoresMsgGasPriceAsEffectiveGasPrice documents the contract +// between WriteReceipt and its caller for EIP-1559 dynamic-fee txs: +// receipt.EffectiveGasPrice is taken from msg.GasPrice verbatim, so the caller +// is responsible for setting msg.GasPrice = min(baseFee + tipCap, feeCap) +// (the actual effective gas price the chain charged). +// +// The bug this guards against: passing ethTx.GasPrice() into msg for a +// dynamic-fee tx returns GasFeeCap (i.e., maxFee) — the receipt would then +// report maxFee even when the tx actually paid baseFee+tip < maxFee, breaking +// EIP-1559 RPC semantics for clients (e.g. ethers, hardhat-ethers). +func TestWriteReceiptStoresMsgGasPriceAsEffectiveGasPrice(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper(t) + stateDB := state.NewDBImpl(ctx, k, false) + txHash := common.HexToHash("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") + + // Scenario: tip=1gwei, maxFee=10gwei, baseFee=1gwei → + // effectiveGasPrice = baseFee + tip = 2gwei (additive branch). + const ( + effectiveGasPrice uint64 = 2_000_000_000 // 2 gwei + feeCap uint64 = 10_000_000_000 // 10 gwei + tipCap uint64 = 1_000_000_000 // 1 gwei + gasUsed uint64 = 21_000 + ) + + msg := &core.Message{ + Nonce: 0, + GasLimit: gasUsed, + GasPrice: new(big.Int).SetUint64(effectiveGasPrice), + GasFeeCap: new(big.Int).SetUint64(feeCap), + GasTipCap: new(big.Int).SetUint64(tipCap), + To: nil, + Value: big.NewInt(0), + From: common.HexToAddress("0x000000000000000000000000000000000000beef"), + } + + r, err := k.WriteReceipt(ctx, stateDB, msg, uint32(ethtypes.DynamicFeeTxType), txHash, gasUsed, "") + require.NoError(t, err) + require.NotNil(t, r) + require.Equal(t, effectiveGasPrice, r.EffectiveGasPrice, + "receipt.EffectiveGasPrice must equal msg.GasPrice; if the caller passes "+ + "ethTx.GasPrice() for a dynamic-fee tx, the receipt would wrongly report GasFeeCap (%d) "+ + "instead of the EIP-1559 effective gas price (%d)", feeCap, effectiveGasPrice) + require.Equal(t, uint32(ethtypes.ReceiptStatusSuccessful), r.Status) + require.Equal(t, gasUsed, r.GasUsed) +}