From d3233d65d59494f0b0ca7c20c6a4084f378739dc Mon Sep 17 00:00:00 2001 From: Draco Date: Wed, 27 Mar 2024 19:52:15 -0400 Subject: [PATCH] bep3 conversion msg server tests (#1859) --- x/evmutil/keeper/msg_server_bep3_test.go | 312 +++++++++++++++++++++++ x/evmutil/types/msg_test.go | 9 + 2 files changed, 321 insertions(+) create mode 100644 x/evmutil/keeper/msg_server_bep3_test.go diff --git a/x/evmutil/keeper/msg_server_bep3_test.go b/x/evmutil/keeper/msg_server_bep3_test.go new file mode 100644 index 0000000000..d36c68fd6e --- /dev/null +++ b/x/evmutil/keeper/msg_server_bep3_test.go @@ -0,0 +1,312 @@ +package keeper_test + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +func (suite *MsgServerSuite) TestConvertCoinToERC20_Bep3() { + invoker, err := sdk.AccAddressFromBech32("kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz") + receiverAddr := testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + suite.Require().NoError(err) + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + msg types.MsgConvertCoinToERC20 + userBankBalance sdkmath.Int + expUserErc20Balance sdkmath.Int + moduleErc20Balance sdkmath.Int + expModuleErc20Balance sdkmath.Int + errArgs errArgs + }{ + { + name: "valid", + msg: types.NewMsgConvertCoinToERC20( + invoker.String(), + receiverAddr.String(), + sdk.NewCoin("bnb", sdkmath.NewInt(1234)), + ), + userBankBalance: sdkmath.NewInt(1_234), + moduleErc20Balance: sdkmath.NewInt(10e13), + expUserErc20Balance: sdkmath.NewInt(1.234e13), + expModuleErc20Balance: sdkmath.NewInt(8.766e13), + errArgs: errArgs{ + expectPass: true, + }, + }, + { + name: "invalid - invalid receiver address", + msg: types.NewMsgConvertCoinToERC20( + invoker.String(), + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + sdk.NewCoin("bnb", sdkmath.NewInt(1234)), + ), + userBankBalance: sdkmath.NewInt(1_234), + moduleErc20Balance: sdkmath.NewInt(1e14), + errArgs: errArgs{ + expectPass: false, + contains: "invalid Receiver address: string is not a hex address", + }, + }, + { + name: "invalid - initiator receiver address", + msg: types.NewMsgConvertCoinToERC20( + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + receiverAddr.String(), + sdk.NewCoin("bnb", sdkmath.NewInt(1234)), + ), + userBankBalance: sdkmath.NewInt(1_234), + moduleErc20Balance: sdkmath.NewInt(1e14), + errArgs: errArgs{ + expectPass: false, + contains: "invalid Initiator address", + }, + }, + } + + for _, tc := range tests { + suite.Run(tc.name, func() { + suite.SetupTest() + contractAddr := suite.DeployERC20() + + // enable conversion pair + params := suite.Keeper.GetParams(suite.Ctx) + params.EnabledConversionPairs[0].Denom = tc.msg.Amount.Denom + suite.Keeper.SetParams(suite.Ctx, params) + + pair := types.NewConversionPair( + contractAddr, + tc.msg.Amount.Denom, + ) + + // fund user & module account + if tc.userBankBalance.GT(sdkmath.ZeroInt()) { + err = suite.App.FundAccount( + suite.Ctx, + invoker, + sdk.NewCoins(sdk.NewCoin(pair.Denom, tc.userBankBalance)), + ) + suite.Require().NoError(err) + } + if tc.moduleErc20Balance.GT(sdkmath.ZeroInt()) { + err := suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + types.NewInternalEVMAddress(types.ModuleEVMAddress), //receiver + tc.moduleErc20Balance.BigInt(), + ) + suite.Require().NoError(err) + } + + _, err = suite.msgServer.ConvertCoinToERC20(sdk.WrapSDKContext(suite.Ctx), &tc.msg) + + if tc.errArgs.expectPass { + suite.Require().NoError(err) + + // validate user balance + bal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + receiverAddr, + ) + suite.Require().Equal( + tc.expUserErc20Balance.BigInt().Int64(), + bal.Int64(), + "user erc20 balance should match expected amount", + ) + + // validate module balance + bal = suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + types.NewInternalEVMAddress(types.ModuleEVMAddress), + ) + suite.Require().Equal( + tc.expModuleErc20Balance.BigInt().Int64(), + bal.Int64(), + "module erc20 balance should match expected amount", + ) + + // msg server event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, tc.msg.Initiator), + )) + + // keeper event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertCoinToERC20, + sdk.NewAttribute(types.AttributeKeyInitiator, tc.msg.Initiator), + sdk.NewAttribute(types.AttributeKeyReceiver, tc.msg.Receiver), + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyAmount, tc.msg.Amount.String()), + )) + + } else { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.errArgs.contains) + } + }) + } +} + +func (suite *MsgServerSuite) TestConvertERC20ToCoin_Bep3() { + invoker := testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + invokerCosmosAddr, err := sdk.AccAddressFromHexUnsafe(invoker.String()[2:]) + suite.Require().NoError(err) + contractAddr := suite.DeployERC20() + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + msg types.MsgConvertERC20ToCoin + userErc20Balance sdkmath.Int + expUserBankBalance sdkmath.Int + expUserErc20Balance sdkmath.Int + errArgs errArgs + }{ + { + name: "valid", + msg: types.NewMsgConvertERC20ToCoin( + invoker, + invokerCosmosAddr, + contractAddr, + sdkmath.NewInt(1.0031e18), + ), + userErc20Balance: sdkmath.NewInt(1.12e18), + expUserBankBalance: sdkmath.NewInt(1.0031e8), + expUserErc20Balance: sdkmath.NewInt(0.1169e18), + errArgs: errArgs{ + expectPass: true, + }, + }, + { + name: "invalid - invalid initiator address", + msg: types.MsgConvertERC20ToCoin{ + Initiator: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + Receiver: invokerCosmosAddr.String(), + KavaERC20Address: contractAddr.String(), + Amount: sdkmath.NewInt(12e8), + }, + userErc20Balance: sdkmath.NewInt(2e18), + errArgs: errArgs{ + expectPass: false, + contains: "invalid initiator address: string is not a hex address", + }, + }, + { + name: "invalid - invalid receiver address", + msg: types.MsgConvertERC20ToCoin{ + Initiator: invoker.String(), + Receiver: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + KavaERC20Address: contractAddr.String(), + Amount: sdkmath.NewInt(12e8), + }, + userErc20Balance: sdkmath.NewInt(2e18), + errArgs: errArgs{ + expectPass: false, + contains: "invalid receiver address", + }, + }, + { + name: "invalid - invalid contract address", + msg: types.MsgConvertERC20ToCoin{ + Initiator: invoker.String(), + Receiver: invokerCosmosAddr.String(), + KavaERC20Address: invokerCosmosAddr.String(), + Amount: sdkmath.NewInt(12e8), + }, + userErc20Balance: sdkmath.NewInt(2e18), + errArgs: errArgs{ + expectPass: false, + contains: "invalid contract address", + }, + }, + } + + for _, tc := range tests { + suite.Run(tc.name, func() { + suite.SetupTest() + contractAddr := suite.DeployERC20() + + conversionDenom := "bnb" + params := suite.Keeper.GetParams(suite.Ctx) + params.EnabledConversionPairs[0].Denom = conversionDenom + suite.Keeper.SetParams(suite.Ctx, params) + + pair := types.NewConversionPair( + contractAddr, + conversionDenom, + ) + + // fund user erc20 balance + if tc.userErc20Balance.GT(sdkmath.ZeroInt()) { + err := suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), + invoker, + tc.userErc20Balance.BigInt(), + ) + suite.Require().NoError(err) + } + + // create user account, otherwise `CallEVMWithData` will fail due to failing to get user account when finding its sequence. + err = suite.App.FundAccount(suite.Ctx, invokerCosmosAddr, sdk.NewCoins(sdk.NewCoin(conversionDenom, sdk.ZeroInt()))) + suite.Require().NoError(err) + + _, err := suite.msgServer.ConvertERC20ToCoin(sdk.WrapSDKContext(suite.Ctx), &tc.msg) + + if tc.errArgs.expectPass { + suite.Require().NoError(err) + + // validate user balance after conversion + bal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + testutil.MustNewInternalEVMAddressFromString(invoker.String()), + ) + suite.Require().Equal(tc.expUserErc20Balance.BigInt().Int64(), bal.Int64(), "user erc20 balance is invalid") + + // validate user coin balance + coinBal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, invokerCosmosAddr, pair.Denom) + suite.Require().Equal(tc.expUserBankBalance, coinBal.Amount, "user coin balance is invalid") + + // msg server event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, tc.msg.Initiator), + )) + + // keeper event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertERC20ToCoin, + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyInitiator, tc.msg.Initiator), + sdk.NewAttribute(types.AttributeKeyReceiver, tc.msg.Receiver), + sdk.NewAttribute(types.AttributeKeyAmount, sdk.NewCoin(pair.Denom, tc.expUserBankBalance).String()), + )) + } else { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.errArgs.contains) + } + }) + } +} diff --git a/x/evmutil/types/msg_test.go b/x/evmutil/types/msg_test.go index 6061905e73..1a73fdaa3a 100644 --- a/x/evmutil/types/msg_test.go +++ b/x/evmutil/types/msg_test.go @@ -36,6 +36,15 @@ func TestMsgConvertCoinToERC20(t *testing.T) { expectPass: true, }, }, + { + "valid-bnb", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.NewCoin("bnb", sdkmath.NewInt(1234)), + errArgs{ + expectPass: true, + }, + }, { "invalid - odd length hex address", "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz",