diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index 54b7e54a03..e6b2a93f2e 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -597,6 +597,36 @@ func (_m *CrosschainFungibleKeeper) WithdrawFromGasStabilityPool(ctx types.Conte return r0 } +// ZEVMDepositAndCallContract provides a mock function with given fields: ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes +func (_m *CrosschainFungibleKeeper) ZEVMDepositAndCallContract(ctx types.Context, sender common.Address, to common.Address, inboundSenderChainID int64, inboundAmount *big.Int, data []byte, indexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) { + ret := _m.Called(ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes) + + if len(ret) == 0 { + panic("no return value specified for ZEVMDepositAndCallContract") + } + + var r0 *evmtypes.MsgEthereumTxResponse + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, common.Address, common.Address, int64, *big.Int, []byte, [32]byte) (*evmtypes.MsgEthereumTxResponse, error)); ok { + return rf(ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes) + } + if rf, ok := ret.Get(0).(func(types.Context, common.Address, common.Address, int64, *big.Int, []byte, [32]byte) *evmtypes.MsgEthereumTxResponse); ok { + r0 = rf(ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*evmtypes.MsgEthereumTxResponse) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, common.Address, common.Address, int64, *big.Int, []byte, [32]byte) error); ok { + r1 = rf(ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ZRC20DepositAndCallContract provides a mock function with given fields: ctx, from, to, amount, senderChainID, data, coinType, asset func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Context, from []byte, to common.Address, amount *big.Int, senderChainID int64, data []byte, coinType coin.CoinType, asset string) (*evmtypes.MsgEthereumTxResponse, bool, error) { ret := _m.Called(ctx, from, to, amount, senderChainID, data, coinType, asset) diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index 9e404b7b01..bbac6b439d 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -3,7 +3,6 @@ package keeper import ( "encoding/hex" "fmt" - "math/big" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" @@ -39,22 +38,16 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo } if inboundCoinType == coin.CoinType_Zeta { - // if coin type is Zeta, this is a deposit ZETA to zEVM cctx. - err := k.fungibleKeeper.DepositCoinZeta(ctx, to, inboundAmount) - if err != nil { - // Return !isContractReverted, err. This will set the cctx status to Aborted. - return false, err - } indexBytes, err := cctx.GetCCTXIndexBytes() if err != nil { - // Return !isContractReverted, err. This will set the cctx status to Aborted. return false, err } - _, isContract, err := k.fungibleKeeper.ZevmOnReceive(ctx, sender.Bytes(), to, big.NewInt(inboundSenderChainID), inboundAmount, data, indexBytes) - // Use error message only for a contract call , if to address is not a contract we do not need handle the error - if isContract && err != nil { - // Return !isContractReverted, err. This will set the cctx status to Aborted. - return false, err + // if coin type is Zeta, this is a deposit ZETA to zEVM cctx. + evmTxResponse, err := k.fungibleKeeper.ZEVMDepositAndCallContract(ctx, sender, to, inboundSenderChainID, inboundAmount, data, indexBytes) + if fungibletypes.IsContractReverted(evmTxResponse, err) || errShouldRevertCctx(err) { + return true, err // contract reverted; should refund + } else if err != nil { + return false, err // internal error; should abort } } else { // cointype is Gas or ERC20; then it could be a ZRC20 deposit/depositAndCall cctx. diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index f82872566a..59db2b6362 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -7,6 +7,7 @@ import ( "testing" "cosmossdk.io/math" + ethcommon "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -26,16 +27,19 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() amount := big.NewInt(42) + sender := sample.EthAddress() + senderChainId := int64(0) // expect DepositCoinZeta to be called - fungibleMock.On("DepositCoinZeta", ctx, receiver, amount).Return(nil) + fungibleMock.On("ZEVMDepositAndCallContract", ctx, ethcommon.HexToAddress(sender.String()), receiver, senderChainId, amount, mock.Anything, mock.Anything).Return(nil, nil) // call HandleEVMDeposit cctx := sample.CrossChainTx(t, "foo") cctx.GetCurrentOutTxParam().Receiver = receiver.String() cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) cctx.GetInboundTxParams().CoinType = coin.CoinType_Zeta - cctx.GetInboundTxParams().SenderChainId = 0 + cctx.GetInboundTxParams().SenderChainId = senderChainId + cctx.InboundTxParams.Sender = sender.String() reverted, err := k.HandleEVMDeposit( ctx, cctx, @@ -52,19 +56,20 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) receiver := sample.EthAddress() + sender := sample.EthAddress() + senderChainId := int64(0) amount := big.NewInt(42) - + cctx := sample.CrossChainTx(t, "foo") // expect DepositCoinZeta to be called errDeposit := errors.New("deposit failed") - fungibleMock.On("DepositCoinZeta", ctx, receiver, amount).Return(errDeposit) - + fungibleMock.On("ZEVMDepositAndCallContract", ctx, ethcommon.HexToAddress(sender.String()), receiver, senderChainId, amount, mock.Anything, mock.Anything).Return(nil, errDeposit) // call HandleEVMDeposit - cctx := sample.CrossChainTx(t, "foo") + cctx.InboundTxParams.Sender = sender.String() cctx.GetCurrentOutTxParam().Receiver = receiver.String() cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) cctx.GetInboundTxParams().CoinType = coin.CoinType_Zeta - cctx.GetInboundTxParams().SenderChainId = 0 + cctx.GetInboundTxParams().SenderChainId = senderChainId reverted, err := k.HandleEVMDeposit( ctx, cctx, diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index da4c2bc394..97b0be8f77 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" eth "github.com/ethereum/go-ethereum/common" + ethcommon "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -162,22 +163,13 @@ type FungibleKeeper interface { ) (eth.Address, error) FundGasStabilityPool(ctx sdk.Context, chainID int64, amount *big.Int) error WithdrawFromGasStabilityPool(ctx sdk.Context, chainID int64, amount *big.Int) error - ZevmOnReceive(ctx sdk.Context, - zetaTxSender []byte, - zetaTxReceiver eth.Address, - senderChainID *big.Int, - amount *big.Int, - data []byte, - cctxIndexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, bool, error) - - ZevmOnRevert(ctx sdk.Context, - zetaTxSender eth.Address, - zetaTxReceiver []byte, - senderChainID *big.Int, - destinationChainID *big.Int, - amount *big.Int, + ZEVMDepositAndCallContract(ctx sdk.Context, + sender ethcommon.Address, + to ethcommon.Address, + inboundSenderChainID int64, + inboundAmount *big.Int, data []byte, - cctxIndexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, bool, error) + indexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) } type AuthorityKeeper interface { diff --git a/x/fungible/keeper/deposits_test.go b/x/fungible/keeper/deposits_test.go index e8cef57c00..31dd1b3471 100644 --- a/x/fungible/keeper/deposits_test.go +++ b/x/fungible/keeper/deposits_test.go @@ -5,6 +5,7 @@ import ( "testing" "cosmossdk.io/math" + ethcommon "github.com/ethereum/go-ethereum/common" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -369,3 +370,16 @@ func TestKeeper_DepositCoinZeta(t *testing.T) { b = sdkk.BankKeeper.GetBalance(ctx, zetaToAddress, config.BaseDenom) require.Equal(t, amount.Int64(), b.Amount.Int64()) } + +func Test_AddressConvertion(t *testing.T) { + addressCosmosString := sample.AccAddress() + addressCosmsosAccAddress := sdk.MustAccAddressFromBech32(addressCosmosString) + // Logic used in depositCoins function + addressEth := ethcommon.HexToAddress(addressCosmosString) + //https://github.com/zeta-chain/zeta-node/blob/zevm-message-passing/x/fungible/keeper/deposits.go#L17-L17 + depositAddress := sdk.AccAddress(addressEth.Bytes()) + depositAddressString := depositAddress.String() + + require.Equal(t, addressCosmsosAccAddress, depositAddress) + require.Equal(t, addressCosmosString, depositAddressString) +} diff --git a/x/fungible/keeper/zevm_message_passing_test.go b/x/fungible/keeper/zevm_message_passing_test.go index dd7d9b0674..467e578b8f 100644 --- a/x/fungible/keeper/zevm_message_passing_test.go +++ b/x/fungible/keeper/zevm_message_passing_test.go @@ -4,17 +4,19 @@ import ( "math/big" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/crypto" "github.com/evmos/ethermint/x/evm/statedb" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/testutil/contracts" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/fungible/types" ) -func TestKeeper_ZevmOnReceive(t *testing.T) { - t.Run("successfully call ZevmOnReceive on connector contract ", func(t *testing.T) { +func TestKeeper_ZEVMDepositAndCallContract(t *testing.T) { + t.Run("successfully call ZEVMDepositAndCallContract on connector contract ", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) @@ -23,16 +25,15 @@ func TestKeeper_ZevmOnReceive(t *testing.T) { require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) - zetaTxSender := sample.EthAddress().Bytes() - senderChainID := big.NewInt(1) + zetaTxSender := sample.EthAddress() zetaTxReceiver := dAppContract - amount := big.NewInt(45) + inboundSenderChainID := int64(1) + inboundAmount := big.NewInt(45) data := []byte("message") cctxIndexBytes := [32]byte{} - _, isContract, err := k.ZevmOnReceive(ctx, zetaTxSender, zetaTxReceiver, senderChainID, amount, data, cctxIndexBytes) + _, err = k.ZEVMDepositAndCallContract(ctx, zetaTxSender, zetaTxReceiver, inboundSenderChainID, inboundAmount, data, cctxIndexBytes) require.NoError(t, err) - require.True(t, isContract) dappAbi, err := contracts.DappMetaData.GetAbi() require.NoError(t, err) @@ -53,47 +54,90 @@ func TestKeeper_ZevmOnReceive(t *testing.T) { require.NotZero(t, len(unpacked)) valSenderAddress, ok := unpacked[0].([]byte) require.True(t, ok) - require.Equal(t, zetaTxSender, valSenderAddress) + require.Equal(t, zetaTxSender.Bytes(), valSenderAddress) }) - t.Run("fail to call ZevmOnReceive if account not found for receiver address", func(t *testing.T) { + t.Run("successfully deposit coin if account is not a contract", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zetaTxSender := sample.EthAddress() + zetaTxReceiver := sample.EthAddress() + inboundSenderChainID := int64(1) + inboundAmount := big.NewInt(45) + data := []byte("message") + cctxIndexBytes := [32]byte{} - _, isContract, err := k.ZevmOnReceive(ctx, sample.EthAddress().Bytes(), - sample.EthAddress(), - big.NewInt(1), - big.NewInt(45), - []byte("message"), - [32]byte{}) - require.ErrorIs(t, err, types.ErrAccountNotFound) - require.False(t, isContract) + err := sdkk.EvmKeeper.SetAccount(ctx, zetaTxReceiver, statedb.Account{ + Nonce: 0, + Balance: big.NewInt(0), + CodeHash: crypto.Keccak256(nil), + }) + require.NoError(t, err) + + _, err = k.ZEVMDepositAndCallContract(ctx, zetaTxSender, zetaTxReceiver, inboundSenderChainID, inboundAmount, data, cctxIndexBytes) + require.NoError(t, err) + b := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(zetaTxReceiver.Bytes()), config.BaseDenom) + require.Equal(t, inboundAmount.Int64(), b.Amount.Int64()) }) - t.Run("fail to call ZevmOnReceive if account is not a contract", func(t *testing.T) { + t.Run("fail ZEVMDepositAndCallContract if account not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + zetaTxSender := sample.EthAddress() + zetaTxReceiver := sample.EthAddress() + inboundSenderChainID := int64(1) + inboundAmount := big.NewInt(45) + data := []byte("message") + cctxIndexBytes := [32]byte{} + + _, err := k.ZEVMDepositAndCallContract(ctx, zetaTxSender, zetaTxReceiver, inboundSenderChainID, inboundAmount, data, cctxIndexBytes) + require.ErrorIs(t, err, types.ErrAccountNotFound) + require.ErrorContains(t, err, "account not found") + }) +} +func TestKeeper_ZevmOnReceive(t *testing.T) { + t.Run("successfully call ZevmOnReceive on connector contract ", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + require.NoError(t, err) + assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) - zetaTxReceiver := sample.EthAddress() - err := sdkk.EvmKeeper.SetAccount(ctx, zetaTxReceiver, statedb.Account{ - Nonce: 0, - Balance: big.NewInt(100), - CodeHash: crypto.Keccak256(nil), - }) + zetaTxSender := sample.EthAddress().Bytes() + senderChainID := big.NewInt(1) + zetaTxReceiver := dAppContract + amount := big.NewInt(45) + data := []byte("message") + cctxIndexBytes := [32]byte{} + + _, err = k.ZevmOnReceive(ctx, zetaTxSender, zetaTxReceiver, senderChainID, amount, data, cctxIndexBytes) require.NoError(t, err) - _, isContract, err := k.ZevmOnReceive(ctx, sample.EthAddress().Bytes(), - zetaTxReceiver, - big.NewInt(1), - big.NewInt(45), - []byte("message"), - [32]byte{}) - require.ErrorIs(t, err, types.ErrCallNonContract) - require.False(t, isContract) + dappAbi, err := contracts.DappMetaData.GetAbi() + require.NoError(t, err) + res, err := k.CallEVM( + ctx, + *dappAbi, + types.ModuleAddressEVM, + dAppContract, + big.NewInt(0), + nil, + false, + false, + "zetaTxSenderAddress", + ) + require.NoError(t, err) + unpacked, err := dappAbi.Unpack("zetaTxSenderAddress", res.Ret) + require.NoError(t, err) + require.NotZero(t, len(unpacked)) + valSenderAddress, ok := unpacked[0].([]byte) + require.True(t, ok) + require.Equal(t, zetaTxSender, valSenderAddress) }) t.Run("fail to call ZevmOnReceive if CallOnReceiveZevmConnector fails", func(t *testing.T) { @@ -111,10 +155,9 @@ func TestKeeper_ZevmOnReceive(t *testing.T) { data := []byte("message") cctxIndexBytes := [32]byte{} - _, isContract, err := k.ZevmOnReceive(ctx, zetaTxSender, zetaTxReceiver, senderChainID, amount, data, cctxIndexBytes) + _, err = k.ZevmOnReceive(ctx, zetaTxSender, zetaTxReceiver, senderChainID, amount, data, cctxIndexBytes) require.ErrorIs(t, err, types.ErrContractNotFound) require.ErrorContains(t, err, "GetSystemContract address not found") - require.True(t, isContract) }) } diff --git a/x/fungible/keeper/zevm_msg_passing.go b/x/fungible/keeper/zevm_msg_passing.go index 2d29e8017f..abacc288ec 100644 --- a/x/fungible/keeper/zevm_msg_passing.go +++ b/x/fungible/keeper/zevm_msg_passing.go @@ -6,34 +6,44 @@ import ( "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - eth "github.com/ethereum/go-ethereum/common" + ethcommon "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/zeta-chain/zetacore/x/fungible/types" ) -func (k Keeper) ZevmOnReceive(ctx sdk.Context, - zetaTxSender []byte, - zetaTxReceiver eth.Address, - senderChainID *big.Int, - amount *big.Int, +func (k Keeper) ZEVMDepositAndCallContract(ctx sdk.Context, + sender ethcommon.Address, + to ethcommon.Address, + inboundSenderChainID int64, + inboundAmount *big.Int, data []byte, - cctxIndexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, bool, error) { - acc := k.evmKeeper.GetAccount(ctx, zetaTxReceiver) + indexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) { + acc := k.evmKeeper.GetAccount(ctx, to) if acc == nil { - return nil, false, errors.Wrap(types.ErrAccountNotFound, fmt.Sprintf("address: %s", zetaTxReceiver.String())) + return nil, errors.Wrap(types.ErrAccountNotFound, fmt.Sprintf("address: %s", to.String())) } if !acc.IsContract() { - return nil, false, errors.Wrap(types.ErrCallNonContract, fmt.Sprintf("address is not a contract: %s", zetaTxReceiver.String())) - } - evmCallResponse, err := k.CallOnReceiveZevmConnector(ctx, zetaTxSender, senderChainID, zetaTxReceiver, amount, data, cctxIndexBytes) - if err != nil { - return nil, true, err + err := k.DepositCoinZeta(ctx, to, inboundAmount) + if err != nil { + return nil, err + } + return nil, nil } - return evmCallResponse, true, nil + return k.ZevmOnReceive(ctx, sender.Bytes(), to, big.NewInt(inboundSenderChainID), inboundAmount, data, indexBytes) + +} +func (k Keeper) ZevmOnReceive(ctx sdk.Context, + zetaTxSender []byte, + zetaTxReceiver ethcommon.Address, + senderChainID *big.Int, + amount *big.Int, + data []byte, + cctxIndexBytes [32]byte) (*evmtypes.MsgEthereumTxResponse, error) { + return k.CallOnReceiveZevmConnector(ctx, zetaTxSender, senderChainID, zetaTxReceiver, amount, data, cctxIndexBytes) } func (k Keeper) ZevmOnRevert(ctx sdk.Context, - zetaTxSender eth.Address, + zetaTxSender ethcommon.Address, zetaTxReceiver []byte, senderChainID *big.Int, destinationChainID *big.Int,