diff --git a/CHANGELOG.md b/CHANGELOG.md index 391e68dbf..f5c2e4c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#2209](https://github.com/NibiruChain/nibiru/pull/2209) - refator(ci): Simplify GitHub actions based on conditional paths, removing the need for files like ".github/workflows/skip-unit-tests.yml". - [#2211](https://github.com/NibiruChain/nibiru/pull/2211) - ci(chaosnet): avoid building on cache injected directories +- [#2212](https://github.com/NibiruChain/nibiru/pull/2212) - fix(evm): proper eth tx logs emission for funtoken operations ## [v2.0.0-p1](https://github.com/NibiruChain/nibiru/releases/tag/v2.0.0-p1) - 2025-02-10 diff --git a/x/evm/evmtest/erc20.go b/x/evm/evmtest/erc20.go index feb426209..fa6f30fd8 100644 --- a/x/evm/evmtest/erc20.go +++ b/x/evm/evmtest/erc20.go @@ -120,12 +120,6 @@ func CreateFunTokenForBankCoin( return funtoken } -// BigPow multiplies "amount" by 10 to the "pow10Exp". -func BigPow(amount *big.Int, pow10Exp uint8) (powAmount *big.Int) { - pow10 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(pow10Exp)), nil) - return new(big.Int).Mul(amount, pow10) -} - type FunTokenBalanceAssert struct { FunToken evm.FunToken Account gethcommon.Address diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index 08928be64..439e6094f 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -82,15 +82,5 @@ func (k Keeper) CallContractWithInput( err = fmt.Errorf("VMError: %s", evmResp.VmError) return } - - // Success, update block gas used and bloom filter - if commit { - k.updateBlockBloom(ctx, evmResp, uint64(txConfig.LogIndex)) - - err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) - if err != nil { - return nil, errors.Wrap(err, "error emitting tx logs") - } - } return evmResp, nil } diff --git a/x/evm/keeper/funtoken_from_coin.go b/x/evm/keeper/funtoken_from_coin.go index 6cb4977d5..f796d005d 100644 --- a/x/evm/keeper/funtoken_from_coin.go +++ b/x/evm/keeper/funtoken_from_coin.go @@ -102,7 +102,7 @@ func (k *Keeper) deployERC20ForBankCoin( k.Bank.StateDB = nil }() evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB) - _, err = k.CallContractWithInput( + evmResp, err := k.CallContractWithInput( ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy, ) if err != nil { @@ -114,5 +114,11 @@ func (k *Keeper) deployERC20ForBankCoin( return gethcommon.Address{}, errors.Wrap(err, "failed to commit stateDB") } + // Emit the logs from the EVM Contract deploy execution + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(0)) + } + return erc20Addr, nil } diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index 32563b002..023bb51cb 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/suite" @@ -139,6 +140,36 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { IsMadeFromCoin: true, }, ) + + // Event "EventTxLog" must present with OwnershipTransferred event + emptyHash := gethcommon.BytesToHash(make([]byte, 32)).Hex() + signature := crypto.Keccak256Hash([]byte("OwnershipTransferred(address,address)")).Hex() + ownershipFrom := emptyHash + ownershipTo := gethcommon.BytesToHash(evm.EVM_MODULE_ADDRESS.Bytes()).Hex() + + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventTxLog{ + Logs: []evm.Log{ + { + Address: actualErc20Addr.Address.Hex(), + Topics: []string{ + signature, + ownershipFrom, + ownershipTo, + }, + Data: nil, + BlockNumber: 1, // we are in simulation, no real block numbers or tx hashes + TxHash: emptyHash, + TxIndex: 0, + BlockHash: emptyHash, + Index: 0, + Removed: false, + }, + }, + }, + ) }) s.Run("sad: CreateFunToken for the bank coin: already registered", func() { @@ -169,7 +200,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { funToken := s.fundAndCreateFunToken(deps, 100) s.T().Log("Convert bank coin to erc-20") - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()).WithEventManager(sdk.NewEventManager()) _, err := deps.EvmKeeper.ConvertCoinToEvm( sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ @@ -183,7 +214,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - s.T().Log("Check typed event") + s.T().Log("Check typed event ConvertCoinToEvm") testutil.RequireContainsTypedEvent( s.T(), deps.Ctx, @@ -195,6 +226,37 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { }, ) + s.T().Log("Check typed event EventTxLog with Transfer event") + emptyHash := gethcommon.BytesToHash(make([]byte, 32)).Hex() + signature := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Hex() + fromAddress := emptyHash // Mint + toAddress := gethcommon.BytesToHash(alice.EthAddr.Bytes()).Hex() + amountBase64 := gethcommon.LeftPadBytes(big.NewInt(10).Bytes(), 32) + + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventTxLog{ + Logs: []evm.Log{ + { + Address: funToken.Erc20Addr.Hex(), + Topics: []string{ + signature, + fromAddress, + toAddress, + }, + Data: amountBase64, + BlockNumber: 1, // we are in simulation, no real block numbers or tx hashes + TxHash: emptyHash, + TxIndex: 0, + BlockHash: emptyHash, + Index: 1, + Removed: false, + }, + }, + }, + ) + // Check 1: module balance moduleBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) s.Require().Equal(sdk.NewInt(10), moduleBalance.Amount) diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index 86960a2f3..12f1b3c7a 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/suite" @@ -271,7 +272,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { }) s.Run("happy: send Bank tokens back to erc20", func() { - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()).WithEventManager(sdk.NewEventManager()) _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ ToEthAddr: eth.EIP55Addr{ @@ -283,6 +284,52 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + // Event "EventConvertCoinToEvm" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventConvertCoinToEvm{ + Sender: randomAcc.String(), + Erc20ContractAddress: deployResp.ContractAddr.Hex(), + ToEthAddr: deps.Sender.EthAddr.String(), + BankCoin: sdk.Coin{ + Denom: bankDemon, + Amount: sdk.NewInt(1), + }, + }, + ) + + // Event "EventTxLog" must present with OwnershipTransferred event + emptyHash := gethcommon.BytesToHash(make([]byte, 32)).Hex() + signature := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Hex() + fromAddress := gethcommon.BytesToHash(evm.EVM_MODULE_ADDRESS.Bytes()).Hex() + toAddress := gethcommon.BytesToHash(deps.Sender.EthAddr.Bytes()).Hex() + amountBase64 := gethcommon.LeftPadBytes(big.NewInt(1).Bytes(), 32) + + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventTxLog{ + Logs: []evm.Log{ + { + Address: deployResp.ContractAddr.Hex(), + Topics: []string{ + signature, + fromAddress, + toAddress, + }, + Data: amountBase64, + BlockNumber: 1, // we are in simulation, no real block numbers or tx hashes + TxHash: emptyHash, + TxIndex: 0, + BlockHash: emptyHash, + Index: 0, + Removed: false, + }, + }, + }, + ) }) s.T().Log("check balances") diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index ac69d3b36..0aa3d64a0 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -565,6 +565,12 @@ func (k Keeper) convertCoinToEvmBornCoin( BankCoin: coin, }) + // Emit tx logs of Mint event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + return &evm.MsgConvertCoinToEvmResponse{}, nil } @@ -634,7 +640,7 @@ func (k Keeper) convertCoinToEvmBornERC20( true, ) evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) - _, _, err = k.ERC20().Transfer( + _, evmResp, err := k.ERC20().Transfer( erc20Addr, evm.EVM_MODULE_ADDRESS, recipient, @@ -660,6 +666,12 @@ func (k Keeper) convertCoinToEvmBornERC20( BankCoin: coin, }) + // Emit tx logs of Transfer event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + return &evm.MsgConvertCoinToEvmResponse{}, nil } diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go index 82739c612..f7b4bce9f 100644 --- a/x/evm/precompile/funtoken.go +++ b/x/evm/precompile/funtoken.go @@ -146,8 +146,6 @@ func (p precompileFunToken) sendToBank( return nil, ErrInvalidArgs(err) } - var evmResponses []*evm.MsgEthereumTxResponse - // ERC20 must have FunToken mapping funtokens := p.evmKeeper.FunTokens.Collect( ctx, p.evmKeeper.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20), @@ -170,7 +168,7 @@ func (p precompileFunToken) sendToBank( } // Caller transfers ERC20 to the EVM module account - gotAmount, transferResp, err := p.evmKeeper.ERC20().Transfer( + gotAmount, _, err := p.evmKeeper.ERC20().Transfer( erc20, /*erc20*/ caller, /*from*/ evm.EVM_MODULE_ADDRESS, /*to*/ @@ -181,7 +179,6 @@ func (p precompileFunToken) sendToBank( if err != nil { return nil, fmt.Errorf("error in ERC20.transfer from caller to EVM account: %w", err) } - evmResponses = append(evmResponses, transferResp) // EVM account mints FunToken.BankDenom to module account coinToSend := sdk.NewCoin(funtoken.BankDenom, math.NewIntFromBigInt(gotAmount)) @@ -190,11 +187,10 @@ func (p precompileFunToken) sendToBank( // owns the ERC20 contract and was the original minter of the ERC20 tokens. // Since we're sending them away and want accurate total supply tracking, the // tokens need to be burned. - burnResp, err := p.evmKeeper.ERC20().Burn(erc20, evm.EVM_MODULE_ADDRESS, gotAmount, ctx, evmObj) + _, err := p.evmKeeper.ERC20().Burn(erc20, evm.EVM_MODULE_ADDRESS, gotAmount, ctx, evmObj) if err != nil { return nil, fmt.Errorf("ERC20.Burn: %w", err) } - evmResponses = append(evmResponses, burnResp) } else { // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before // any operation that has the potential to use Bank send methods. This will @@ -225,11 +221,6 @@ func (p precompileFunToken) sendToBank( evm.ModuleName, evm.EVM_MODULE_ADDRESS.Hex(), caller.Hex(), err, ) } - for _, resp := range evmResponses { - for _, log := range resp.Logs { - startResult.StateDB.AddLog(log.ToEthereum()) - } - } // TODO: UD-DEBUG: feat: Emit EVM events // https://github.com/NibiruChain/nibiru/issues/2121