@@ -2326,3 +2326,115 @@ func TestGigaValidation_AllModes(t *testing.T) {
23262326 })
23272327 }
23282328}
2329+
2330+ // createUnassociatedEVMTx creates a signed EVM transfer without calling SetAddressMapping,
2331+ // so the sender remains unassociated and triggers balance migration on first use.
2332+ func createUnassociatedEVMTx (t testing.TB , tCtx * GigaTestContext , signer utils.TestAcct , to common.Address , value * big.Int , nonce uint64 ) []byte {
2333+ tc := app .MakeEncodingConfig ().TxConfig
2334+
2335+ signedTx , err := ethtypes .SignTx (ethtypes .NewTx (& ethtypes.DynamicFeeTx {
2336+ GasFeeCap : new (big.Int ).SetUint64 (100000000000 ),
2337+ GasTipCap : new (big.Int ).SetUint64 (100000000000 ),
2338+ Gas : 21000 ,
2339+ ChainID : big .NewInt (config .DefaultChainID ),
2340+ To : & to ,
2341+ Value : value ,
2342+ Nonce : nonce ,
2343+ }), signer .EvmSigner , signer .EvmPrivateKey )
2344+ require .NoError (t , err )
2345+
2346+ txData , err := ethtx .NewTxDataFromTx (signedTx )
2347+ require .NoError (t , err )
2348+
2349+ msg , err := types .NewMsgEVMTransaction (txData )
2350+ require .NoError (t , err )
2351+
2352+ txBuilder := tc .NewTxBuilder ()
2353+ err = txBuilder .SetMsgs (msg )
2354+ require .NoError (t , err )
2355+ txBuilder .SetGasLimit (10000000000 )
2356+ txBuilder .SetFeeAmount (sdk .NewCoins (sdk .NewCoin ("usei" , sdk .NewInt (10000000000 ))))
2357+
2358+ txBytes , err := tc .TxEncoder ()(txBuilder .GetTx ())
2359+ require .NoError (t , err )
2360+
2361+ return txBytes
2362+ }
2363+
2364+ // TestGigaVsGeth_BalanceMigrationMultipleDenoms verifies that when giga encounters an
2365+ // unassociated address holding multiple token denominations, it correctly aborts and
2366+ // falls back to v2, which performs the full balance migration. The results must match
2367+ // a pure v2 execution for consensus parity.
2368+ func TestGigaVsGeth_BalanceMigrationMultipleDenoms (t * testing.T ) {
2369+ blockTime := time .Now ()
2370+ accts := utils .NewTestAccounts (5 )
2371+
2372+ signer := utils .NewSigner ()
2373+ recipient := utils .NewSigner ()
2374+
2375+ // The cast address is the EVM address bytes interpreted as a Cosmos address.
2376+ // Before association, funds sent to this "address" need to be migrated.
2377+ castAddr := sdk .AccAddress (signer .EvmAddress [:])
2378+
2379+ // Fund the cast address with MULTIPLE denominations — this is the key scenario.
2380+ // Balance migration must move all denoms, not just usei.
2381+ multiDenomCoins := sdk .NewCoins (
2382+ sdk .NewCoin ("usei" , sdk .NewInt (1000000000000000000 )), // 1e18 usei (enough for gas + transfer)
2383+ sdk .NewCoin ("uusdc" , sdk .NewInt (500000000 )), // 500 uusdc
2384+ )
2385+
2386+ // --- V2 baseline (handles migration natively) ---
2387+ v2Ctx := NewGigaTestContext (t , accts , blockTime , 1 , ModeV2Sequential )
2388+ err := v2Ctx .TestApp .BankKeeper .MintCoins (v2Ctx .Ctx , "mint" , multiDenomCoins )
2389+ require .NoError (t , err )
2390+ err = v2Ctx .TestApp .BankKeeper .SendCoinsFromModuleToAccount (v2Ctx .Ctx , "mint" , castAddr , multiDenomCoins )
2391+ require .NoError (t , err )
2392+ // DO NOT associate — this forces the preprocess ante handler to migrate balances
2393+
2394+ v2Tx := createUnassociatedEVMTx (t , v2Ctx , signer , recipient .EvmAddress , big .NewInt (1000 ), 0 )
2395+ _ , v2Results , v2Err := RunBlock (t , v2Ctx , [][]byte {v2Tx })
2396+ require .NoError (t , v2Err )
2397+ require .Len (t , v2Results , 1 )
2398+
2399+ // --- Giga Sequential (should abort and fall back to v2) ---
2400+ gigaCtx := NewGigaTestContext (t , accts , blockTime , 1 , ModeGigaSequential )
2401+ err = gigaCtx .TestApp .BankKeeper .MintCoins (gigaCtx .Ctx , "mint" , multiDenomCoins )
2402+ require .NoError (t , err )
2403+ err = gigaCtx .TestApp .BankKeeper .SendCoinsFromModuleToAccount (gigaCtx .Ctx , "mint" , castAddr , multiDenomCoins )
2404+ require .NoError (t , err )
2405+ // DO NOT associate — giga should detect this and abort
2406+
2407+ gigaTx := createUnassociatedEVMTx (t , gigaCtx , signer , recipient .EvmAddress , big .NewInt (1000 ), 0 )
2408+ _ , gigaResults , gigaErr := RunBlock (t , gigaCtx , [][]byte {gigaTx })
2409+ require .NoError (t , gigaErr )
2410+ require .Len (t , gigaResults , 1 )
2411+
2412+ // Both should succeed — giga fell back to v2 which handled migration
2413+ require .Equal (t , uint32 (0 ), v2Results [0 ].Code , "V2 tx should succeed: %s" , v2Results [0 ].Log )
2414+ require .Equal (t , uint32 (0 ), gigaResults [0 ].Code , "Giga tx should succeed (via v2 fallback): %s" , gigaResults [0 ].Log )
2415+
2416+ // Consensus-critical: deterministic fields must match
2417+ require .Equal (t , v2Results [0 ].Code , gigaResults [0 ].Code , "Code mismatch" )
2418+ require .Equal (t , v2Results [0 ].GasUsed , gigaResults [0 ].GasUsed , "GasUsed mismatch" )
2419+ require .Equal (t , v2Results [0 ].GasWanted , gigaResults [0 ].GasWanted , "GasWanted mismatch" )
2420+ CompareLastResultsHash (t , "BalanceMigrationMultipleDenoms" , v2Results , gigaResults )
2421+
2422+ // Verify migration happened in V2: cast address should be empty,
2423+ // real sei address should hold the non-usei denoms.
2424+ v2CastUsdc := v2Ctx .TestApp .BankKeeper .GetBalance (v2Ctx .Ctx , castAddr , "uusdc" )
2425+ v2RealUsdc := v2Ctx .TestApp .BankKeeper .GetBalance (v2Ctx .Ctx , signer .AccountAddress , "uusdc" )
2426+ require .True (t , v2CastUsdc .IsZero (), "V2: cast address uusdc should be 0 after migration, got %s" , v2CastUsdc )
2427+ require .Equal (t , int64 (500000000 ), v2RealUsdc .Amount .Int64 (),
2428+ "V2: real sei address should hold migrated uusdc" )
2429+
2430+ // Verify migration also happened in the Giga (v2-fallback) path
2431+ gigaCastUsdc := gigaCtx .TestApp .BankKeeper .GetBalance (gigaCtx .Ctx , castAddr , "uusdc" )
2432+ gigaRealUsdc := gigaCtx .TestApp .BankKeeper .GetBalance (gigaCtx .Ctx , signer .AccountAddress , "uusdc" )
2433+ require .True (t , gigaCastUsdc .IsZero (), "Giga: cast address uusdc should be 0 after migration, got %s" , gigaCastUsdc )
2434+ require .Equal (t , int64 (500000000 ), gigaRealUsdc .Amount .Int64 (),
2435+ "Giga: real sei address should hold migrated uusdc" )
2436+
2437+ t .Logf ("Balance migration with multiple denoms verified: V2 and Giga (v2-fallback) produce identical results" )
2438+ t .Logf (" V2 cast uusdc: %s, real uusdc: %s" , v2CastUsdc , v2RealUsdc )
2439+ t .Logf (" Giga cast uusdc: %s, real uusdc: %s" , gigaCastUsdc , gigaRealUsdc )
2440+ }
0 commit comments