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
31 changes: 18 additions & 13 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2060,13 +2060,17 @@ func (app *App) executeEVMTxWithGigaExecutor(ctx sdk.Context, msg *evmtypes.MsgE
}, nil
}

// Prepare context for EVM transaction (set infinite gas meter like original flow)
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx))

_, isAssociated := app.GigaEvmKeeper.GetEVMAddress(ctx, seiAddr)

// Run validation checks (fee/nonce/balance - stateless checks done earlier)
validation := app.validateGigaEVMTx(ctx, ethTx, sender, seiAddr, isAssociated)
// Create state DB before validation so balance checks use DBImpl hooks.
stateDB := gigaevmstate.NewDBImpl(ctx, &app.GigaEvmKeeper, false)
defer stateDB.Cleanup()

// Prepare context for EVM transaction (set infinite gas meter like original flow)
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx))
// Run validation checks (fee/nonce/balance - stateless checks done earlier)
validation := app.validateGigaEVMTx(ctx, stateDB, ethTx, sender, seiAddr, isAssociated)

if validation.err != nil {
// v2 rejects fee/nonce/balance failures in its ante chain, whose
Expand All @@ -2082,10 +2086,6 @@ func (app *App) executeEVMTxWithGigaExecutor(ctx sdk.Context, msg *evmtypes.MsgE
return nil, gigaprecompiles.ErrBalanceMigrationRequired
}

// Create state DB for this transaction (only for valid transactions)
stateDB := gigaevmstate.NewDBImpl(ctx, &app.GigaEvmKeeper, false)
defer stateDB.Cleanup()

// Pre-charge gas fee (like V2's ante handler), then execute with feeAlreadyCharged=true.
// V2 charges fees in the ante handler, then runs the EVM with feeAlreadyCharged=true
// which skips buyGas/refundGas/coinbase. Without this, GasUsed differs between Giga
Expand Down Expand Up @@ -3059,6 +3059,7 @@ type gigaValidationResult struct {
// 7. Balance check
func (app *App) validateGigaEVMTx(
ctx sdk.Context,
stateDB *gigaevmstate.DBImpl,
ethTx *ethtypes.Transaction,
sender common.Address,
seiAddr sdk.AccAddress,
Expand Down Expand Up @@ -3209,13 +3210,17 @@ func (app *App) validateGigaEVMTx(
balanceCheck := new(big.Int).Mul(new(big.Int).SetUint64(ethTx.Gas()), ethTx.GasFeeCap())
balanceCheck.Add(balanceCheck, ethTx.Value())

senderBalance := app.GigaEvmKeeper.GetBalance(ctx, seiAddr)
// Route validation balance reads through DBImpl so mock_balances follows
// the same ensureMinimumBalance behavior as V2's StateTransition.BuyGas.
senderBalance := stateDB.GetBalance(sender).ToBig()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] mock_balances only: stateDB.GetBalance(sender) invokes ensureMinimumBalance, which mints a 100-SEI top-off directly via the bank keeper on ctx. That mint is not journaled in the DBImpl, so defer stateDB.Cleanup() does not revert it. If validation then fails (e.g. cost exceeds the top-off), executeEVMTxWithGigaExecutor returns (validation.err, nil) and the synchronous caller still runs ctx.GigaMultiStore().WriteGiga() (app.go:1636), committing the top-off. A rejected tx therefore increases the sender's balance — unlike V2, whose ante-handler top-off runs on a cache context discarded on failure. Test/benchmark-build only (panics on pacific-1), so no consensus impact, but it diverges from the V2 parity this PR is trying to achieve.


// Include cast address balance for unassociated addresses (matches V2 PreprocessDecorator)
// Include the derived Sei address balance for unassociated addresses (matches V2 PreprocessDecorator).
if !isAssociated {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] Confirmed the Codex P2 concern, scoped to mock_balances builds only. For an unassociated sender, stateDB.GetBalance(sender) tops off the cast address (sender[:]) and stateDB.GetBalance(seiEVMAddr) tops off the derived Sei address, each to the 100 SEI TopOffAmount — and ensureMinimumBalancetopOffAccount persists both mints. The tx then always falls back to V2 (ErrBalanceMigrationRequired), which migrates the cast balance, leaving ~200 SEI instead of the ~100 SEI a direct V2 execution creates. This has no production impact (ensureMinimumBalance is a no-op without the build tag, and it panics on pacific-1), but it can cause an app-hash divergence between giga and V2 specifically in mock_balances benchmark/test runs — the scenario this PR aims to align. Since the unassociated path always falls back to V2 anyway, consider skipping the DBImpl-backed top-off for unassociated senders (or reading raw balances there) and adding a test covering it.

castAddr := sdk.AccAddress(sender[:])
castBalance := app.GigaEvmKeeper.GetBalance(ctx, castAddr)
senderBalance = new(big.Int).Add(senderBalance, castBalance)
seiEVMAddr := common.BytesToAddress(seiAddr)
if seiEVMAddr != sender {
seiBalance := stateDB.GetBalance(seiEVMAddr).ToBig()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] mock_balances only: for an unassociated sender both GetBalance(sender) (above) and this GetBalance(seiEVMAddr) top off two distinct derived accounts to 100 SEI each. Validation runs before the !isAssociated V2-fallback at line 2047-2051, so after topping off 200 SEI total the tx returns ErrBalanceMigrationRequired and falls back to V2 via ms.Write(), committing both mints; V2 then migrates both balances. This grants up to 200 SEI vs V2's standalone single 100-SEI top-off. Note the balance sum itself is otherwise equivalent to the previous seiAddr + castAddr logic. Test/benchmark-build only. Simplest fix: skip giga validation for unassociated senders since they always fall back to V2.

senderBalance = new(big.Int).Add(senderBalance, seiBalance)
}
}

if senderBalance.Cmp(balanceCheck) < 0 {
Expand Down
32 changes: 32 additions & 0 deletions giga/tests/giga_mock_balances_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build mock_balances

package giga_test

import (
"math/big"
"testing"
"time"

"github.com/sei-protocol/sei-chain/occ_tests/utils"
"github.com/stretchr/testify/require"
)

func TestGigaMockBalancesValidationUsesDBImpl(t *testing.T) {
blockTime := time.Now()
accts := utils.NewTestAccounts(3)
signer := utils.NewSigner()
recipient := utils.NewSigner()

gigaCtx := NewGigaTestContext(t, accts, blockTime, 1, ModeGigaSequential)
gigaCtx.TestApp.GigaEvmKeeper.SetAddressMapping(gigaCtx.Ctx, signer.AccountAddress, signer.EvmAddress)

to := recipient.EvmAddress
value := big.NewInt(1_000_000_000_000_000_000)
fee := big.NewInt(100000000000)
tx := createCustomEVMTx(t, gigaCtx, signer, &to, value, 21000, fee, fee, 0)

_, results, err := RunBlock(t, gigaCtx, [][]byte{tx})
require.NoError(t, err)
require.Len(t, results, 1)
require.Equal(t, uint32(0), results[0].Code, results[0].Log)
}
Loading