package keeper_test import ( _ "embed" "encoding/json" "math/big" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/tharsis/ethermint/app" "github.com/tharsis/ethermint/crypto/ethsecp256k1" "github.com/tharsis/ethermint/encoding" "github.com/tharsis/ethermint/server/config" "github.com/tharsis/ethermint/tests" ethermint "github.com/tharsis/ethermint/types" "github.com/tharsis/ethermint/x/evm/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) var ( //go:embed ERC20Contract.json compiledContractJSON []byte ContractBin []byte ContractABI abi.ABI ) func init() { var tmp struct { Abi string Bin string } err := json.Unmarshal(compiledContractJSON, &tmp) if err != nil { panic(err) } ContractBin = common.FromHex(tmp.Bin) err = json.Unmarshal([]byte(tmp.Abi), &ContractABI) if err != nil { panic(err) } } var testTokens = sdk.NewIntWithDecimal(1000, 18) type KeeperTestSuite struct { suite.Suite ctx sdk.Context app *app.EthermintApp queryClient types.QueryClient address ethcmn.Address consAddress sdk.ConsAddress // for generate test tx clientCtx client.Context ethSigner ethtypes.Signer appCodec codec.Codec signer keyring.Signer } /// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`. func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) { checkTx := false // account key priv, err := ethsecp256k1.GenerateKey() require.NoError(t, err) suite.address = ethcmn.BytesToAddress(priv.PubKey().Address().Bytes()) suite.signer = tests.NewSigner(priv) // consensus key priv, err = ethsecp256k1.GenerateKey() require.NoError(t, err) suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) suite.app = app.Setup(checkTx) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ Height: 1, ChainID: "ethermint_9000-1", Time: time.Now().UTC(), ProposerAddress: suite.consAddress.Bytes(), }) suite.app.EvmKeeper.WithContext(suite.ctx) queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) suite.queryClient = types.NewQueryClient(queryHelper) acc := ðermint.EthAccount{ BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), CodeHash: common.BytesToHash(ethcrypto.Keccak256(nil)).String(), } suite.app.AccountKeeper.SetAccount(suite.ctx, acc) valAddr := sdk.ValAddress(suite.address.Bytes()) validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{}) err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) require.NoError(t, err) err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) require.NoError(t, err) suite.app.StakingKeeper.SetValidator(suite.ctx, validator) encodingConfig := encoding.MakeConfig(app.ModuleBasics) suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) suite.appCodec = encodingConfig.Marshaler // mint some tokens to coinbase address _, bankKeeper := suite.initKeepersWithmAccPerms() require.NoError(t, err) initCoin := sdk.NewCoins(sdk.NewCoin(suite.EvmDenom(), testTokens)) err = simapp.FundAccount(bankKeeper, suite.ctx, acc.GetAddress(), initCoin) require.NoError(t, err) } func (suite *KeeperTestSuite) SetupTest() { suite.DoSetupTest(suite.T()) } func (suite *KeeperTestSuite) EvmDenom() string { ctx := sdk.WrapSDKContext(suite.ctx) rsp, _ := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) return rsp.Params.EvmDenom } // Commit and begin new block func (suite *KeeperTestSuite) Commit() { suite.app.Commit() header := suite.ctx.BlockHeader() header.Height += 1 suite.app.BeginBlock(abci.RequestBeginBlock{ Header: header, }) // update ctx suite.ctx = suite.app.BaseApp.NewContext(false, header) suite.app.EvmKeeper.WithContext(suite.ctx) queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) suite.queryClient = types.NewQueryClient(queryHelper) } // initKeepersWithmAccPerms construct a bank keeper that can mint tokens out of thin air func (suite *KeeperTestSuite) initKeepersWithmAccPerms() (authkeeper.AccountKeeper, bankkeeper.BaseKeeper) { maccPerms := app.GetMaccPerms() maccPerms[authtypes.Burner] = []string{authtypes.Burner} maccPerms[authtypes.Minter] = []string{authtypes.Minter} authKeeper := authkeeper.NewAccountKeeper( suite.appCodec, suite.app.GetKey(types.StoreKey), suite.app.GetSubspace(types.ModuleName), authtypes.ProtoBaseAccount, maccPerms, ) keeper := bankkeeper.NewBaseKeeper( suite.appCodec, suite.app.GetKey(types.StoreKey), authKeeper, suite.app.GetSubspace(types.ModuleName), map[string]bool{}, ) return authKeeper, keeper } // DeployTestContract deploy a test erc20 contract and returns the contract address func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner common.Address, supply *big.Int) common.Address { ctx := sdk.WrapSDKContext(suite.ctx) chainID := suite.app.EvmKeeper.ChainID() ctorArgs, err := ContractABI.Pack("", owner, supply) require.NoError(t, err) data := append(ContractBin, ctorArgs...) args, err := json.Marshal(&types.CallArgs{ From: &suite.address, Data: (*hexutil.Bytes)(&data), }) require.NoError(t, err) res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ Args: args, GasCap: uint64(config.DefaultGasCap), }) require.NoError(t, err) nonce := suite.app.EvmKeeper.GetNonce(suite.address) erc20DeployTx := types.NewTxContract( chainID, nonce, nil, // amount res.Gas, // gasLimit nil, // gasPrice data, // input nil, // accesses ) erc20DeployTx.From = suite.address.Hex() err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) require.NoError(t, err) rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx) require.NoError(t, err) require.Empty(t, rsp.VmError) return crypto.CreateAddress(suite.address, nonce) } func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) }