diff --git a/CHANGELOG.md b/CHANGELOG.md index 6081d958d..6623c519f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,7 +86,11 @@ needed to include double quotes around the hexadecimal string. - [#2170](https://github.com/NibiruChain/nibiru/pull/2170) - chore: Remove redundant allowUnprotectedTxs - [#2172](https://github.com/NibiruChain/nibiru/pull/2172) - chore: close iterator in IterateEpochInfo - [#2173](https://github.com/NibiruChain/nibiru/pull/2173) - fix(evm): clear `StateDB` between calls - +- [#2177](https://github.com/NibiruChain/nibiru/pull/2177) - fix(cmd): Continue from #2127 and unwire vesting flags and logic from genaccounts.go +- [#2176](https://github.com/NibiruChain/nibiru/pull/2176) - tests(evm): add dirty state tests from code4rena audit +- [#2180](https://github.com/NibiruChain/nibiru/pull/2180) - fix(evm): apply gas consumption across the entire EVM codebase at `CallContractWithInput` +- [#2183](https://github.com/NibiruChain/nibiru/pull/2183) - fix(evm): bank keeper extension gas meter type +- #### Nibiru EVM | Before Audit 2 - 2024-12-06 The codebase went through a third-party [Code4rena @@ -298,7 +302,7 @@ about the expected resulting balance for the transfer recipient. - Bump `actions/setup-go` from 4 to 5 ([#1696](https://github.com/NibiruChain/nibiru/pull/1696)) - Bump `golang` from 1.19 to 1.21 ([#1698](https://github.com/NibiruChain/nibiru/pull/1698)) - [#1678](https://github.com/NibiruChain/nibiru/pull/1678) - chore(deps): collections to v0.4.0 for math.Int value encoder -- Bump `golang.org/x/net` from 0.0.0-20220607020251-c690dde0001d to 0.23.0 in /geth ([#1849](https://github.com/NibiruChain/nibiru/pull/1849)) +- Bump `golang.org/x/net` from 0.0.0-20220607020251-c690dde0001d to 0.33.0 ([#1849](https://github.com/NibiruChain/nibiru/pull/1849), [#2175](https://github.com/NibiruChain/nibiru/pull/2175)) - Bump `golang.org/x/net` from 0.20.0 to 0.23.0 ([#1850](https://github.com/NibiruChain/nibiru/pull/1850)) - Bump `github.com/supranational/blst` from 0.3.8-0.20220526154634-513d2456b344 to 0.3.11 ([#1851](https://github.com/NibiruChain/nibiru/pull/1851)) - Bump `golangci/golangci-lint-action` from 4 to 6 ([#1854](https://github.com/NibiruChain/nibiru/pull/1854), [#1867](https://github.com/NibiruChain/nibiru/pull/1867)) @@ -310,6 +314,7 @@ about the expected resulting balance for the transfer recipient. - Bump `github.com/CosmWasm/wasmvm` from 1.5.0 to 1.5.5 ([#2047](https://github.com/NibiruChain/nibiru/pull/2047)) - Bump `docker/build-push-action` from 5 to 6 ([#1924](https://github.com/NibiruChain/nibiru/pull/1924)) - Bump `codecov/codecov-action` from 4 to 5 ([#2112](https://github.com/NibiruChain/nibiru/pull/2112)) +- Bump `undici` from 5.28.4 to 5.28.5 ([#2174](https://github.com/NibiruChain/nibiru/pull/2174)) ## [v1.5.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.5.0) - 2024-06-21 diff --git a/cmd/nibid/cmd/genaccounts.go b/cmd/nibid/cmd/genaccounts.go index 6b79b1d87..53fca441e 100644 --- a/cmd/nibid/cmd/genaccounts.go +++ b/cmd/nibid/cmd/genaccounts.go @@ -3,9 +3,9 @@ package cmd import ( "bufio" "encoding/json" - "errors" "fmt" + "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -14,28 +14,21 @@ import ( "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -const ( - flagVestingStart = "vesting-start-time" - flagVestingEnd = "vesting-end-time" - flagVestingAmt = "vesting-amount" -) - // AddGenesisAccountCmd returns add-genesis-account cobra Command. func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", Short: "Add a genesis account to genesis.json", - Long: `Add a genesis account to genesis.json. The provided account must specify + Long: heredoc.Doc(`Add a genesis account to genesis.json. The provided account must specify the account address or key name and a list of initial coins. If a key name is given, the address will be looked up in the local Keybase. The list of initial tokens must -contain valid denominations. Accounts may optionally be supplied with vesting parameters. -`, +contain valid denominations. +`), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) @@ -74,42 +67,11 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa return fmt.Errorf("failed to parse coins: %w", err) } - vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart) - vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd) - vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt) - - vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) - if err != nil { - return fmt.Errorf("failed to parse vesting amount: %w", err) - } - // create concrete account type based on input parameters var genAccount authtypes.GenesisAccount balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} - baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) - - if !vestingAmt.IsZero() { - baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) - - if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || - baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { - return errors.New("vesting amount cannot be greater than total amount") - } - - switch { - case vestingStart != 0 && vestingEnd != 0: - genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) - - case vestingEnd != 0: - genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) - - default: - return errors.New("invalid vesting parameters; must supply start and end time or end time") - } - } else { - genAccount = baseAccount - } + genAccount = authtypes.NewBaseAccount(addr, nil, 0, 0) if err := genAccount.Validate(); err != nil { return fmt.Errorf("failed to validate new genesis account: %w", err) @@ -174,9 +136,6 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") - cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") - cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") - cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") flags.AddQueryFlagsToCmd(cmd) return cmd diff --git a/eth/rpc/backend/backend_suite_test.go b/eth/rpc/backend/backend_suite_test.go index 16637a86c..dec06920f 100644 --- a/eth/rpc/backend/backend_suite_test.go +++ b/eth/rpc/backend/backend_suite_test.go @@ -252,7 +252,7 @@ func (s *BackendSuite) buildContractCallTx( nonce uint64, gasLimit uint64, ) gethcore.Transaction { - //recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381) + // recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381) transferAmount := big.NewInt(100) packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack( diff --git a/go.mod b/go.mod index 0d5688fe2..270b09c5a 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.23.0 + golang.org/x/net v0.33.0 golang.org/x/text v0.21.0 ) diff --git a/go.sum b/go.sum index d3fe3717d..f4364ff51 100644 --- a/go.sum +++ b/go.sum @@ -1446,8 +1446,9 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/x/common/testutil/testapp/test_util.go b/x/common/testutil/testapp/test_util.go index c4eecceea..d94318da1 100644 --- a/x/common/testutil/testapp/test_util.go +++ b/x/common/testutil/testapp/test_util.go @@ -16,6 +16,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" nibiruapp "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" ) // GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts @@ -41,7 +42,7 @@ func GenesisStateWithSingleValidator(codec codec.Codec, genesisState nibiruapp.G balances = append(balances, banktypes.Balance{ Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000000000000))), + Coins: sdk.NewCoins(sdk.NewCoin(appconst.BondDenom, math.NewIntFromUint64(1e14))), }) genesisState, err = genesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) @@ -93,7 +94,9 @@ func genesisStateWithValSet( delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), math.LegacyOneDec())) } // set validators and delegations - stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + stakingParams := stakingtypes.DefaultParams() + stakingParams.BondDenom = appconst.BondDenom + stakingGenesis := stakingtypes.NewGenesisState(stakingParams, validators, delegations) genesisState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingGenesis) totalSupply := sdk.NewCoins() @@ -104,13 +107,13 @@ func genesisStateWithValSet( for range delegations { // add delegated tokens to total supply - totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) + totalSupply = totalSupply.Add(sdk.NewCoin(appconst.BondDenom, bondAmt)) } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + Coins: sdk.Coins{sdk.NewCoin(appconst.BondDenom, bondAmt)}, }) // update total supply diff --git a/x/common/testutil/testapp/testapp.go b/x/common/testutil/testapp/testapp.go index d8507b099..24f39951c 100644 --- a/x/common/testutil/testapp/testapp.go +++ b/x/common/testutil/testapp/testapp.go @@ -17,6 +17,7 @@ import ( "github.com/NibiruChain/nibiru/v2/app" "github.com/NibiruChain/nibiru/v2/app/appconst" + cryptocodec "github.com/NibiruChain/nibiru/v2/eth/crypto/codec" "github.com/NibiruChain/nibiru/v2/x/common/asset" "github.com/NibiruChain/nibiru/v2/x/common/denoms" "github.com/NibiruChain/nibiru/v2/x/common/testutil" @@ -126,6 +127,7 @@ func NewNibiruTestApp(gen app.GenesisState, baseAppOptions ...func(*baseapp.Base logger := log.NewNopLogger() encoding := app.MakeEncodingConfig() + cryptocodec.RegisterInterfaces(encoding.InterfaceRegistry) SetDefaultSudoGenesis(gen) app := app.NewNibiruApp( diff --git a/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json new file mode 100644 index 000000000..3907e082f --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json @@ -0,0 +1,47 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestDirtyStateAttack4", + "sourceName": "contracts/TestDirtyStateAttack4.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "wasmAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + } + ], + "name": "attack", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040526000805561084a806100176000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806333c889971461003b5780638ada066e14610057575b600080fd5b610055600480360381019061005091906102d6565b610075565b005b61005f6101e4565b60405161006c9190610370565b60405180910390f35b600080815480929190610087906103ba565b91905055506000600167ffffffffffffffff8111156100a9576100a8610402565b5b6040519080825280602002602001820160405280156100e257816020015b6100cf6101ed565b8152602001906001900390816100c75790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001620f42408152508160008151811061014657610145610431565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b8152600401610194959493929190610689565b6000604051808303816000875af11580156101b3573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101dc91906107cb565b505050505050565b60008054905090565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102405761023f61021b565b5b8235905067ffffffffffffffff81111561025d5761025c610220565b5b60208301915083600182028301111561027957610278610225565b5b9250929050565b60008083601f8401126102965761029561021b565b5b8235905067ffffffffffffffff8111156102b3576102b2610220565b5b6020830191508360018202830111156102cf576102ce610225565b5b9250929050565b600080600080604085870312156102f0576102ef610211565b5b600085013567ffffffffffffffff81111561030e5761030d610216565b5b61031a8782880161022a565b9450945050602085013567ffffffffffffffff81111561033d5761033c610216565b5b61034987828801610280565b925092505092959194509250565b6000819050919050565b61036a81610357565b82525050565b60006020820190506103856000830184610361565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006103c582610357565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103f7576103f661038b565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061049d8385610460565b93506104aa838584610471565b6104b383610480565b840190509392505050565b600082825260208201905092915050565b60006104db83856104be565b93506104e8838584610471565b6104f183610480565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610562578082015181840152602081019050610547565b60008484015250505050565b600061057982610528565b6105838185610533565b9350610593818560208601610544565b61059c81610480565b840191505092915050565b6105b081610357565b82525050565b600060408301600083015184820360008601526105d3828261056e565b91505060208301516105e860208601826105a7565b508091505092915050565b60006105ff83836105b6565b905092915050565b6000602082019050919050565b600061061f826104fc565b6106298185610507565b93508360208202850161063b85610518565b8060005b85811015610677578484038952815161065885826105f3565b945061066383610607565b925060208a0199505060018101905061063f565b50829750879550505050505092915050565b600060608201905081810360008301526106a4818789610491565b905081810360208301526106b98185876104cf565b905081810360408301526106cd8184610614565b90509695505050505050565b600080fd5b6106e782610480565b810181811067ffffffffffffffff8211171561070657610705610402565b5b80604052505050565b6000610719610207565b905061072582826106de565b919050565b600067ffffffffffffffff82111561074557610744610402565b5b61074e82610480565b9050602081019050919050565b600061076e6107698461072a565b61070f565b90508281526020810184848401111561078a576107896106d9565b5b610795848285610544565b509392505050565b600082601f8301126107b2576107b161021b565b5b81516107c284826020860161075b565b91505092915050565b6000602082840312156107e1576107e0610211565b5b600082015167ffffffffffffffff8111156107ff576107fe610216565b5b61080b8482850161079d565b9150509291505056fea26469706673582212208caac82ecde5bb31fb5b34bd6b569b73277f17eb9b132d65942a718f83178c9c64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806333c889971461003b5780638ada066e14610057575b600080fd5b610055600480360381019061005091906102d6565b610075565b005b61005f6101e4565b60405161006c9190610370565b60405180910390f35b600080815480929190610087906103ba565b91905055506000600167ffffffffffffffff8111156100a9576100a8610402565b5b6040519080825280602002602001820160405280156100e257816020015b6100cf6101ed565b8152602001906001900390816100c75790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001620f42408152508160008151811061014657610145610431565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b8152600401610194959493929190610689565b6000604051808303816000875af11580156101b3573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101dc91906107cb565b505050505050565b60008054905090565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102405761023f61021b565b5b8235905067ffffffffffffffff81111561025d5761025c610220565b5b60208301915083600182028301111561027957610278610225565b5b9250929050565b60008083601f8401126102965761029561021b565b5b8235905067ffffffffffffffff8111156102b3576102b2610220565b5b6020830191508360018202830111156102cf576102ce610225565b5b9250929050565b600080600080604085870312156102f0576102ef610211565b5b600085013567ffffffffffffffff81111561030e5761030d610216565b5b61031a8782880161022a565b9450945050602085013567ffffffffffffffff81111561033d5761033c610216565b5b61034987828801610280565b925092505092959194509250565b6000819050919050565b61036a81610357565b82525050565b60006020820190506103856000830184610361565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006103c582610357565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103f7576103f661038b565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061049d8385610460565b93506104aa838584610471565b6104b383610480565b840190509392505050565b600082825260208201905092915050565b60006104db83856104be565b93506104e8838584610471565b6104f183610480565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610562578082015181840152602081019050610547565b60008484015250505050565b600061057982610528565b6105838185610533565b9350610593818560208601610544565b61059c81610480565b840191505092915050565b6105b081610357565b82525050565b600060408301600083015184820360008601526105d3828261056e565b91505060208301516105e860208601826105a7565b508091505092915050565b60006105ff83836105b6565b905092915050565b6000602082019050919050565b600061061f826104fc565b6106298185610507565b93508360208202850161063b85610518565b8060005b85811015610677578484038952815161065885826105f3565b945061066383610607565b925060208a0199505060018101905061063f565b50829750879550505050505092915050565b600060608201905081810360008301526106a4818789610491565b905081810360208301526106b98185876104cf565b905081810360408301526106cd8184610614565b90509695505050505050565b600080fd5b6106e782610480565b810181811067ffffffffffffffff8211171561070657610705610402565b5b80604052505050565b6000610719610207565b905061072582826106de565b919050565b600067ffffffffffffffff82111561074557610744610402565b5b61074e82610480565b9050602081019050919050565b600061076e6107698461072a565b61070f565b90508281526020810184848401111561078a576107896106d9565b5b610795848285610544565b509392505050565b600082601f8301126107b2576107b161021b565b5b81516107c284826020860161075b565b91505092915050565b6000602082840312156107e1576107e0610211565b5b600082015167ffffffffffffffff8111156107ff576107fe610216565b5b61080b8482850161079d565b9150509291505056fea26469706673582212208caac82ecde5bb31fb5b34bd6b569b73277f17eb9b132d65942a718f83178c9c64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json new file mode 100644 index 000000000..80d00a824 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json @@ -0,0 +1,34 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestDirtyStateAttack5", + "sourceName": "contracts/TestDirtyStateAttack5.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "wasmAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + } + ], + "name": "attack", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6080604052610760806100136000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806333c8899714610030575b600080fd5b61004a6004803603810190610045919061028d565b61004c565b005b6000600167ffffffffffffffff8111156100695761006861030e565b5b6040519080825280602002602001820160405280156100a257816020015b61008f6101a4565b8152602001906001900390816100875790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001624c4b40815250816000815181106101065761010561033d565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b815260040161015495949392919061059f565b6000604051808303816000875af1158015610173573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061019c91906106e1565b505050505050565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101f7576101f66101d2565b5b8235905067ffffffffffffffff811115610214576102136101d7565b5b6020830191508360018202830111156102305761022f6101dc565b5b9250929050565b60008083601f84011261024d5761024c6101d2565b5b8235905067ffffffffffffffff81111561026a576102696101d7565b5b602083019150836001820283011115610286576102856101dc565b5b9250929050565b600080600080604085870312156102a7576102a66101c8565b5b600085013567ffffffffffffffff8111156102c5576102c46101cd565b5b6102d1878288016101e1565b9450945050602085013567ffffffffffffffff8111156102f4576102f36101cd565b5b61030087828801610237565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006103a9838561036c565b93506103b683858461037d565b6103bf8361038c565b840190509392505050565b600082825260208201905092915050565b60006103e783856103ca565b93506103f483858461037d565b6103fd8361038c565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561046e578082015181840152602081019050610453565b60008484015250505050565b600061048582610434565b61048f818561043f565b935061049f818560208601610450565b6104a88161038c565b840191505092915050565b6000819050919050565b6104c6816104b3565b82525050565b600060408301600083015184820360008601526104e9828261047a565b91505060208301516104fe60208601826104bd565b508091505092915050565b600061051583836104cc565b905092915050565b6000602082019050919050565b600061053582610408565b61053f8185610413565b93508360208202850161055185610424565b8060005b8581101561058d578484038952815161056e8582610509565b94506105798361051d565b925060208a01995050600181019050610555565b50829750879550505050505092915050565b600060608201905081810360008301526105ba81878961039d565b905081810360208301526105cf8185876103db565b905081810360408301526105e3818461052a565b90509695505050505050565b600080fd5b6105fd8261038c565b810181811067ffffffffffffffff8211171561061c5761061b61030e565b5b80604052505050565b600061062f6101be565b905061063b82826105f4565b919050565b600067ffffffffffffffff82111561065b5761065a61030e565b5b6106648261038c565b9050602081019050919050565b600061068461067f84610640565b610625565b9050828152602081018484840111156106a05761069f6105ef565b5b6106ab848285610450565b509392505050565b600082601f8301126106c8576106c76101d2565b5b81516106d8848260208601610671565b91505092915050565b6000602082840312156106f7576106f66101c8565b5b600082015167ffffffffffffffff811115610715576107146101cd565b5b610721848285016106b3565b9150509291505056fea2646970667358221220035e628d5c38012c486ab00ce495fa418401de59d5a1cbfe1e48245374f7d1be64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c806333c8899714610030575b600080fd5b61004a6004803603810190610045919061028d565b61004c565b005b6000600167ffffffffffffffff8111156100695761006861030e565b5b6040519080825280602002602001820160405280156100a257816020015b61008f6101a4565b8152602001906001900390816100875790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001624c4b40815250816000815181106101065761010561033d565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b815260040161015495949392919061059f565b6000604051808303816000875af1158015610173573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061019c91906106e1565b505050505050565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101f7576101f66101d2565b5b8235905067ffffffffffffffff811115610214576102136101d7565b5b6020830191508360018202830111156102305761022f6101dc565b5b9250929050565b60008083601f84011261024d5761024c6101d2565b5b8235905067ffffffffffffffff81111561026a576102696101d7565b5b602083019150836001820283011115610286576102856101dc565b5b9250929050565b600080600080604085870312156102a7576102a66101c8565b5b600085013567ffffffffffffffff8111156102c5576102c46101cd565b5b6102d1878288016101e1565b9450945050602085013567ffffffffffffffff8111156102f4576102f36101cd565b5b61030087828801610237565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006103a9838561036c565b93506103b683858461037d565b6103bf8361038c565b840190509392505050565b600082825260208201905092915050565b60006103e783856103ca565b93506103f483858461037d565b6103fd8361038c565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561046e578082015181840152602081019050610453565b60008484015250505050565b600061048582610434565b61048f818561043f565b935061049f818560208601610450565b6104a88161038c565b840191505092915050565b6000819050919050565b6104c6816104b3565b82525050565b600060408301600083015184820360008601526104e9828261047a565b91505060208301516104fe60208601826104bd565b508091505092915050565b600061051583836104cc565b905092915050565b6000602082019050919050565b600061053582610408565b61053f8185610413565b93508360208202850161055185610424565b8060005b8581101561058d578484038952815161056e8582610509565b94506105798361051d565b925060208a01995050600181019050610555565b50829750879550505050505092915050565b600060608201905081810360008301526105ba81878961039d565b905081810360208301526105cf8185876103db565b905081810360408301526105e3818461052a565b90509695505050505050565b600080fd5b6105fd8261038c565b810181811067ffffffffffffffff8211171561061c5761061b61030e565b5b80604052505050565b600061062f6101be565b905061063b82826105f4565b919050565b600067ffffffffffffffff82111561065b5761065a61030e565b5b6106648261038c565b9050602081019050919050565b600061068461067f84610640565b610625565b9050828152602081018484840111156106a05761069f6105ef565b5b6106ab848285610450565b509392505050565b600082601f8301126106c8576106c76101d2565b5b81516106d8848260208601610671565b91505092915050565b6000602082840312156106f7576106f66101c8565b5b600082015167ffffffffffffffff811115610715576107146101cd565b5b610721848285016106b3565b9150509291505056fea2646970667358221220035e628d5c38012c486ab00ce495fa418401de59d5a1cbfe1e48245374f7d1be64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/contracts/TestDirtyStateAttack4.sol b/x/evm/embeds/contracts/TestDirtyStateAttack4.sol new file mode 100644 index 000000000..c9dc1d1df --- /dev/null +++ b/x/evm/embeds/contracts/TestDirtyStateAttack4.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; +import "./Wasm.sol"; +import "./NibiruEvmUtils.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestDirtyStateAttack4 { + uint counter = 0; + + constructor() payable {} + + function attack(string calldata wasmAddr, bytes calldata msgArgs) external { + counter++; + + INibiruEvm.BankCoin[] memory funds = new INibiruEvm.BankCoin[](1); + funds[0] = INibiruEvm.BankCoin({denom: "unibi", amount: 1e6}); // 1 NIBI + + WASM_PRECOMPILE.execute(wasmAddr, msgArgs, funds); + } + + function getCounter() external view returns (uint) { + return counter; + } +} diff --git a/x/evm/embeds/contracts/TestDirtyStateAttack5.sol b/x/evm/embeds/contracts/TestDirtyStateAttack5.sol new file mode 100644 index 000000000..443738520 --- /dev/null +++ b/x/evm/embeds/contracts/TestDirtyStateAttack5.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; +import "./Wasm.sol"; +import "./NibiruEvmUtils.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestDirtyStateAttack5 { + constructor() payable {} + + function attack(string calldata wasmAddr, bytes calldata msgArgs) external { + INibiruEvm.BankCoin[] memory funds = new INibiruEvm.BankCoin[](1); + funds[0] = INibiruEvm.BankCoin({denom: "unibi", amount: 5e6}); // 5 NIBI + + WASM_PRECOMPILE.execute(wasmAddr, msgArgs, funds); + } +} diff --git a/x/evm/embeds/embeds.go b/x/evm/embeds/embeds.go index f08e26b5d..1f0763311 100644 --- a/x/evm/embeds/embeds.go +++ b/x/evm/embeds/embeds.go @@ -47,6 +47,10 @@ var ( testMetadataBytes32 []byte //go:embed artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json testPrecompileSendToBankThenERC20Transfer []byte + //go:embed artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json + testDirtyStateAttack4 []byte + //go:embed artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json + testDirtyStateAttack5 []byte ) var ( @@ -118,7 +122,6 @@ var ( Name: "TestERC20TransferThenPrecompileSend.sol", EmbedJSON: testERC20TransferThenPrecompileSendJson, } - // SmartContract_TestPrecompileSelfCallRevert is a test contract // that creates another instance of itself, calls the precompile method and then force reverts. // It tests a race condition where the state DB commit @@ -150,12 +153,21 @@ var ( Name: "MKR.sol", EmbedJSON: testMetadataBytes32, } - // SmartContract_TestPrecompileSendToBankThenERC20Transfer is a test contract that sends to bank then calls ERC20 transfer SmartContract_TestPrecompileSendToBankThenERC20Transfer = CompiledEvmContract{ Name: "TestPrecompileSendToBankThenERC20Transfer.sol", EmbedJSON: testPrecompileSendToBankThenERC20Transfer, } + // SmartContract_TestDirtyStateAttack4 is a test contract that composes manual send and funtoken sendToBank with a reversion + SmartContract_TestDirtyStateAttack4 = CompiledEvmContract{ + Name: "TestDirtyStateAttack4.sol", + EmbedJSON: testDirtyStateAttack4, + } + // SmartContract_TestDirtyStateAttack5 is a test contract that calls a wasm contract with 5 NIBI + SmartContract_TestDirtyStateAttack5 = CompiledEvmContract{ + Name: "TestDirtyStateAttack5.sol", + EmbedJSON: testDirtyStateAttack5, + } ) func init() { @@ -175,6 +187,8 @@ func init() { SmartContract_TestRandom.MustLoad() SmartContract_TestBytes32Metadata.MustLoad() SmartContract_TestPrecompileSendToBankThenERC20Transfer.MustLoad() + SmartContract_TestDirtyStateAttack4.MustLoad() + SmartContract_TestDirtyStateAttack5.MustLoad() } type CompiledEvmContract struct { diff --git a/x/evm/embeds/package-lock.json b/x/evm/embeds/package-lock.json index 99bb2520b..eef9850fe 100644 --- a/x/evm/embeds/package-lock.json +++ b/x/evm/embeds/package-lock.json @@ -7453,9 +7453,9 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/x/evm/keeper/bank_extension.go b/x/evm/keeper/bank_extension.go index aa4fe101c..5531b9c5c 100644 --- a/x/evm/keeper/bank_extension.go +++ b/x/evm/keeper/bank_extension.go @@ -89,6 +89,38 @@ func (bk NibiruBankKeeper) UndelegateCoins( ) } +func (bk NibiruBankKeeper) DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.DelegateCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(amt) { + bk.SyncStateDBWithAccount(ctx, senderAddr) + moduleBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + }, + ) +} + +func (bk NibiruBankKeeper) UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.UndelegateCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(amt) { + moduleBech32Addr := auth.NewModuleAddress(senderModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientAddr) + } + }, + ) +} + func (bk NibiruBankKeeper) MintCoins( ctx sdk.Context, moduleName string, @@ -153,24 +185,20 @@ func (bk NibiruBankKeeper) ForceGasInvariant( // Assign vars for the tx gas meter gasMeterBefore := ctx.GasMeter() // Tx gas meter MUST be defined gasConsumedBefore := gasMeterBefore.GasConsumed() - // Don't modify the "ctx.BlockGasMeter()" directly because this is - // handled in "BaseApp.runTx" - - // Start baseGasConsumed at 0 in case we panic before BaseOp completes and - // baseGasConsumed gets a value assignment baseOpGasConsumed := uint64(0) defer func() { + // NOTE: we have to refund the entire gasMeterBefore because it's modified by AfterOp + // stateDB.getStateObject() reads from state using the local root ctx which affects the gas meter gasMeterBefore.RefundGas(gasMeterBefore.GasConsumed(), "") gasMeterBefore.ConsumeGas(gasConsumedBefore+baseOpGasConsumed, "NibiruBankKeeper invariant") }() - // Note that because the ctx gas meter uses private variables to track gas, - // we have to branch off with a new gas meter instance to avoid mutating the - // "true" gas meter (called GasMeterBefore here). - // We use an infinite gas meter because we consume gas in the deferred function - // and gasMeterBefore will panic if we consume too much gas. - ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + // We keep the same gas meter type but reset the gas consumed prior to measuring the base op + // We need the same gas meter type because we use a custom FixedGasMeter for oracle votes in the AnteHandler + // In the defer function, we reset the gas meter again and then add the gasConsumedBefore to baseOpGasConsumed, + // so any modifications to the gasMeterBefore after this point will be inconsequential. + ctx.GasMeter().RefundGas(gasConsumedBefore, "reset gas meter before measuring base op") err := BaseOp(ctx) baseOpGasConsumed = ctx.GasMeter().GasConsumed() diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index f9fdc2c8b..032d30bd7 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -39,7 +39,7 @@ func (k Keeper) CallContractWithInput( ) (evmResp *evm.MsgEthereumTxResponse, err error) { // This is a `defer` pattern to add behavior that runs in the case that the // error is non-nil, creating a concise way to add extra information. - defer HandleOutOfGasPanic(&err, "CallContractError") + defer HandleOutOfGasPanic(&err, "CallContractError")() nonce := k.GetAccNonce(ctx, fromAcc) unusedBigInt := big.NewInt(0) @@ -61,11 +61,13 @@ func (k Keeper) CallContractWithInput( // sent by a user txConfig := k.TxConfig(ctx, gethcommon.BigToHash(big.NewInt(0))) evmResp, err = k.ApplyEvmMsg( - ctx, evmMsg, evmObj, evm.NewNoOpTracer(), commit, txConfig.TxHash, true, + ctx, evmMsg, evmObj, evm.NewNoOpTracer(), commit, txConfig.TxHash, ) + if evmResp != nil { + ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "CallContractWithInput") + } if err != nil { - err = errors.Wrap(err, "failed to apply ethereum core message") - return + return nil, errors.Wrap(err, "failed to apply ethereum core message") } if evmResp.Failed() { diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index f26bee952..3851e06dd 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -196,7 +196,7 @@ func (e erc20Calls) loadERC20String( if err != nil { return out, err } - res, err := e.Keeper.CallContractWithInput( + evmResp, err := e.Keeper.CallContractWithInput( ctx, evmObj, evm.EVM_MODULE_ADDRESS, @@ -211,13 +211,13 @@ func (e erc20Calls) loadERC20String( erc20Val := new(ERC20String) if err := erc20Abi.UnpackIntoInterface( - erc20Val, methodName, res.Ret, + erc20Val, methodName, evmResp.Ret, ); err == nil { return erc20Val.Value, err } erc20Bytes32Val := new(ERC20Bytes32) - if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, res.Ret); err == nil { + if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, evmResp.Ret); err == nil { return bytes32ToString(erc20Bytes32Val.Value), nil } @@ -239,7 +239,7 @@ func (e erc20Calls) loadERC20Uint8( if err != nil { return out, err } - res, err := e.Keeper.CallContractWithInput( + evmResp, err := e.Keeper.CallContractWithInput( ctx, evmObj, evm.EVM_MODULE_ADDRESS, @@ -254,14 +254,14 @@ func (e erc20Calls) loadERC20Uint8( erc20Val := new(ERC20Uint8) if err := erc20Abi.UnpackIntoInterface( - erc20Val, methodName, res.Ret, + erc20Val, methodName, evmResp.Ret, ); err == nil { return erc20Val.Value, err } erc20Uint256Val := new(ERC20BigInt) if err := erc20Abi.UnpackIntoInterface( - erc20Uint256Val, methodName, res.Ret, + erc20Uint256Val, methodName, evmResp.Ret, ); err == nil { // We can safely cast to uint8 because it's nonsense for decimals to be larger than 255 return uint8(erc20Uint256Val.Value.Uint64()), err diff --git a/x/evm/keeper/funtoken_from_coin.go b/x/evm/keeper/funtoken_from_coin.go index 294d72964..6cb4977d5 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) - evmResp, err := k.CallContractWithInput( + _, err = k.CallContractWithInput( ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy, ) if err != nil { @@ -114,7 +114,5 @@ func (k *Keeper) deployERC20ForBankCoin( return gethcommon.Address{}, errors.Wrap(err, "failed to commit stateDB") } - ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "deploy erc20 funtoken contract") - return erc20Addr, nil } diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index 7a75afca6..32563b002 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -82,6 +82,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { }) s.Run("happy: CreateFunToken for the bank coin", func() { + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, @@ -97,6 +98,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { }, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) s.Equal( createFuntokenResp.FuntokenMapping, @@ -167,6 +169,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()) _, err := deps.EvmKeeper.ConvertCoinToEvm( sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ @@ -178,6 +181,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { }, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) s.T().Log("Check typed event") testutil.RequireContainsTypedEvent( @@ -226,6 +230,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { deps.Sender.NibiruAddr.String(), ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() _, err = deps.EvmKeeper.CallContractWithInput( deps.Ctx, @@ -237,6 +242,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) // Check 1: module balance moduleBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) @@ -252,6 +258,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { s.Require().Equal("0", balance.String()) s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() _, err = deps.EvmKeeper.CallContractWithInput( deps.Ctx, @@ -263,6 +270,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().ErrorContains(err, "transfer amount exceeds balance") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) } // TestNativeSendThenPrecompileSend tests a race condition where the state DB @@ -362,6 +370,7 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { newSendAmtSendToBank, /*amount*/ ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, @@ -373,6 +382,9 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") s.Empty(evmResp.VmError) gasUsedFor2Ops := evmResp.GasUsed @@ -404,6 +416,7 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { newSendAmtSendToBank, /*amount*/ ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() evmResp, err = deps.EvmKeeper.CallContractWithInput( deps.Ctx, @@ -415,6 +428,9 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { evmtest.DefaultEthCallGasLimit, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") s.Empty(evmResp.VmError) gasUsedFor1Op := evmResp.GasUsed @@ -517,8 +533,9 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { big.NewInt(9e6), /*amount*/ ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, // from @@ -528,6 +545,9 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { 10_000_000, // gas limit ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") evmtest.FunTokenBalanceAssert{ FunToken: funToken, @@ -620,6 +640,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() { charles := evmtest.NewEthPrivAcc() s.T().Log("call test contract") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ = deps.NewEVM() contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack( "selfCallTransferFunds", @@ -629,7 +650,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() { big.NewInt(9e6), ) s.Require().NoError(err) - _, err = deps.EvmKeeper.CallContractWithInput( + evpResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -639,6 +660,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") evmtest.FunTokenBalanceAssert{ FunToken: funToken, @@ -726,8 +750,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() { "attack", ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evpResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -737,6 +762,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") evmtest.FunTokenBalanceAssert{ FunToken: funToken, @@ -751,7 +779,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() { Account: bob.EthAddr, BalanceBank: big.NewInt(0), BalanceERC20: big.NewInt(0), - Description: "Charles has 0 NIBI / 0 WNIBI", + Description: "Bob has 0 NIBI / 0 WNIBI", }.Assert(s.T(), deps, evmObj) evmtest.FunTokenBalanceAssert{ diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index 438b928ba..86960a2f3 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -42,7 +42,6 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { s.Require().Equal(expectedERC20Addr, deployResp.ContractAddr) evmObj, _ := deps.NewEVM() - actualMetadata, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, evmObj, deployResp.ContractAddr, nil) s.Require().NoError(err) s.Require().Equal(metadata, *actualMetadata) @@ -75,6 +74,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), )) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) resp, err := deps.EvmKeeper.CreateFunToken( sdk.WrapSDKContext(deps.Ctx), &evm.MsgCreateFunToken{ @@ -83,6 +83,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { }, ) s.Require().NoError(err, "erc20 %s", erc20Addr) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) expectedBankDenom := fmt.Sprintf("erc20/%s", expectedERC20Addr.String()) s.Equal( @@ -199,79 +200,100 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { s.Require().NoError(err, "erc20 %s", deployResp.ContractAddr) bankDemon := resp.FuntokenMapping.BankDenom - s.T().Logf("mint erc20 tokens to %s", deps.Sender.EthAddr.String()) - contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) - s.Require().NoError(err) - evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, /*from*/ - &deployResp.ContractAddr, /*to*/ - true, /*commit*/ - contractInput, - keeper.Erc20GasLimitExecute, - ) - s.Require().NoError(err) + s.Run("happy: mint erc20 tokens", func() { + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &deployResp.ContractAddr, /*to*/ + true, /*commit*/ + contractInput, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) randomAcc := testutil.AccAddress() + s.Run("happy: send erc20 tokens to Bank", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(1), randomAcc.String()) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &precompile.PrecompileAddr_FunToken, /*to*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) - s.T().Log("happy: send erc20 tokens to Bank") - contractInput, err = embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(1), randomAcc.String()) - s.Require().NoError(err) - evmObj, _ = deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, /*from*/ - &precompile.PrecompileAddr_FunToken, /*to*/ - true, /*commit*/ - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().NoError(err) - - s.T().Log("check balances") - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_419), "expect nonzero balance") - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(1), "expect nonzero balance") - s.Require().Equal(sdk.NewInt(1), - deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount, - ) + s.Run("happy: check balances", func() { + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_419), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(1), "expect nonzero balance") + s.Require().Equal(sdk.NewInt(1), + deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount, + ) + }) - s.T().Log("sad: send too many erc20 tokens to Bank") - contractInput, err = embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(70_000), randomAcc.String()) - s.Require().NoError(err) - evmObj, _ = deps.NewEVM() - evmResp, err := deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, /*from*/ - &precompile.PrecompileAddr_FunToken, /*to*/ - true, /*commit*/ - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().Error(err, evmResp.String()) + s.Run("sad: send too many erc20 tokens to Bank", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(70_000), randomAcc.String()) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &precompile.PrecompileAddr_FunToken, /*to*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().Error(err, evmResp.String()) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) - s.T().Log("send Bank tokens back to erc20") - _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - ToEthAddr: eth.EIP55Addr{ - Address: deps.Sender.EthAddr, + s.Run("happy: send Bank tokens back to erc20", func() { + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, + Sender: randomAcc.String(), + BankCoin: sdk.NewCoin(bankDemon, sdk.NewInt(1)), }, - Sender: randomAcc.String(), - BankCoin: sdk.NewCoin(bankDemon, sdk.NewInt(1)), - }, - ) - s.Require().NoError(err) + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + }) s.T().Log("check balances") - evmObj, _ = deps.NewEVM() - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_420), "expect nonzero balance") - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect nonzero balance") - s.Require().True( - deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount.Equal(sdk.NewInt(0)), - ) + s.Run("happy: check balances", func() { + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_420), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect nonzero balance") + s.Require().True( + deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount.Equal(sdk.NewInt(0)), + ) + }) s.T().Log("sad: send too many Bank tokens back to erc20") _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), @@ -368,8 +390,9 @@ func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { s.T().Log("send erc20 tokens to cosmos") input, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(1), randomAcc.String()) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, evm.EVM_MODULE_ADDRESS, @@ -379,6 +402,9 @@ func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().ErrorContains(err, "gas required exceeds allowance") + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) } // TestFunTokenInfiniteRecursionERC20 creates a funtoken from a contract @@ -422,7 +448,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { contractInput, err := embeds.SmartContract_TestInfiniteRecursionERC20.ABI.Pack("attackBalance") s.Require().NoError(err) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, /*from*/ @@ -432,12 +458,15 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { 10_000_000, ) s.Require().NoError(err) + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) s.T().Log("sad: call attackTransfer()") contractInput, err = embeds.SmartContract_TestInfiniteRecursionERC20.ABI.Pack("attackTransfer") s.Require().NoError(err) evmObj, _ = deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err = deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, /*from*/ @@ -447,6 +476,9 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { 10_000_000, ) s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) } // TestSendERC20WithFee creates a funtoken from a malicious contract which charges a 10% fee on any transfer. @@ -494,8 +526,9 @@ func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { randomAcc.String(), /*to*/ ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, /*from*/ @@ -505,6 +538,9 @@ func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) s.T().Log("check balances") evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(900), "expect 900 balance") @@ -569,8 +605,9 @@ func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { ) s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -579,8 +616,10 @@ func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { contractInput, evmtest.FunTokenGasLimitSendToEvm, ) - s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, evmObj, deployResp.ContractAddr, embeds.SmartContract_TestBytes32Metadata.ABI) s.Require().NoError(err) diff --git a/x/evm/keeper/gas_fees.go b/x/evm/keeper/gas_fees.go index d5712c067..8b8a6a495 100644 --- a/x/evm/keeper/gas_fees.go +++ b/x/evm/keeper/gas_fees.go @@ -74,16 +74,16 @@ func (k *Keeper) RefundGas( return nil } -// GasToRefund calculates the amount of gas the state machine should refund to -// the sender. It is capped by the refund quotient value. Note that passing a -// jrefundQuotient of 0 will cause problems. -func GasToRefund(availableRefund, gasConsumed, refundQuotient uint64) uint64 { - // Apply refund counter - refund := gasConsumed / refundQuotient - if refund > availableRefund { - return availableRefund +// gasToRefund calculates the amount of gas the state machine should refund to +// the sender. +// EIP-3529: refunds are capped to gasUsed / 5 +func gasToRefund(availableRefundAmount, gasUsed uint64) uint64 { + refundAmount := gasUsed / params.RefundQuotientEIP3529 + if refundAmount > availableRefundAmount { + // Apply refundAmount counter + return availableRefundAmount } - return refund + return refundAmount } // CheckSenderBalance validates that the tx cost value is positive and that the diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 3255b8f73..da2274d50 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -286,7 +286,7 @@ func (k *Keeper) EthCall( // pass false to not commit StateDB stateDB := statedb.New(ctx, k, txConfig) evm := k.NewEVM(ctx, msg, evmCfg, nil /*tracer*/, stateDB) - res, err := k.ApplyEvmMsg(ctx, msg, evm, nil /*tracer*/, false /*commit*/, txConfig.TxHash, false /*fullRefundLeftoverGas*/) + res, err := k.ApplyEvmMsg(ctx, msg, evm, nil /*tracer*/, false /*commit*/, txConfig.TxHash) if err != nil { return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) } @@ -421,7 +421,7 @@ func (k Keeper) EstimateGasForEvmCallType( txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())) stateDB := statedb.New(ctx, &k, txConfig) evmObj := k.NewEVM(tmpCtx, evmMsg, evmCfg, nil /*tracer*/, stateDB) - rsp, err = k.ApplyEvmMsg(tmpCtx, evmMsg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash, false /*fullRefundLeftoverGas*/) + rsp, err = k.ApplyEvmMsg(tmpCtx, evmMsg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash) if err != nil { if errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit @@ -516,7 +516,7 @@ func (k Keeper) TraceTx( WithTransientKVGasConfig(storetypes.GasConfig{}) stateDB := statedb.New(ctx, &k, txConfig) evmObj := k.NewEVM(ctx, msg, evmCfg, nil /*tracer*/, stateDB) - rsp, err := k.ApplyEvmMsg(ctx, msg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash, false /*fullRefundLeftoverGas*/) + rsp, err := k.ApplyEvmMsg(ctx, msg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash) if err != nil { continue } @@ -790,7 +790,7 @@ func (k *Keeper) TraceEthTxMsg( WithTransientKVGasConfig(storetypes.GasConfig{}) stateDB := statedb.New(ctx, k, txConfig) evmObj := k.NewEVM(ctx, msg, evmCfg, tracer, stateDB) - res, err := k.ApplyEvmMsg(ctx, msg, evmObj, tracer, false /*commit*/, txConfig.TxHash, false /*fullRefundLeftoverGas*/) + res, err := k.ApplyEvmMsg(ctx, msg, evmObj, tracer, false /*commit*/, txConfig.TxHash) if err != nil { return nil, 0, grpcstatus.Error(grpccodes.Internal, err.Error()) } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index ff36cd6cb..19de7266d 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -128,13 +128,12 @@ func HandleOutOfGasPanic(err *error, format string) func() { if r := recover(); r != nil { switch r.(type) { case sdk.ErrorOutOfGas: - *err = vm.ErrOutOfGas default: panic(r) } } - if err != nil && format != "" { + if err != nil && *err != nil && format != "" { *err = fmt.Errorf("%s: %w", format, *err) } } diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 94acd33ac..e5421725c 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -10,7 +10,6 @@ import ( "strconv" "cosmossdk.io/errors" - "cosmossdk.io/math" tmbytes "github.com/cometbft/cometbft/libs/bytes" tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -64,7 +63,17 @@ func (k *Keeper) EthereumTx( k.Bank.StateDB = nil }() evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB) - evmResp, err = k.ApplyEvmMsg(ctx, evmMsg, evmObj, nil /*tracer*/, true /*commit*/, txConfig.TxHash, false /*fullRefundLeftoverGas*/) + evmResp, err = k.ApplyEvmMsg( + ctx, + evmMsg, + evmObj, + nil, /*tracer*/ + true, /*commit*/ + txConfig.TxHash, + ) + if evmResp != nil { + ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "execute ethereum tx") + } if err != nil { return nil, errors.Wrap(err, "error applying ethereum core message") } @@ -81,7 +90,6 @@ func (k *Keeper) EthereumTx( if err = k.RefundGas(ctx, evmMsg.From(), refundGas, weiPerGas); err != nil { return nil, errors.Wrapf(err, "error refunding leftover gas to sender %s", evmMsg.From()) } - ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "execute ethereum tx") err = k.EmitEthereumTxEvents(ctx, tx.To(), tx.Type(), evmMsg, evmResp) if err != nil { @@ -251,23 +259,21 @@ func (k *Keeper) ApplyEvmMsg( tracer vm.EVMLogger, commit bool, txHash gethcommon.Hash, - fullRefundLeftoverGas bool, ) (resp *evm.MsgEthereumTxResponse, err error) { - leftoverGas := msg.Gas() + gasRemaining := msg.Gas() // Allow the tracer captures the tx level events, mainly the gas consumption. vmCfg := evmObj.Config if vmCfg.Debug { - vmCfg.Tracer.CaptureTxStart(leftoverGas) + vmCfg.Tracer.CaptureTxStart(gasRemaining) defer func() { - vmCfg.Tracer.CaptureTxEnd(leftoverGas) + vmCfg.Tracer.CaptureTxEnd(gasRemaining) }() } - sender := vm.AccountRef(msg.From()) contractCreation := msg.To() == nil - intrinsicGas, err := core.IntrinsicGas( + intrinsicGasCost, err := core.IntrinsicGas( msg.Data(), msg.AccessList(), contractCreation, true, true, ) @@ -282,15 +288,15 @@ func (k *Keeper) ApplyEvmMsg( // // Should check again even if it is checked on Ante Handler, because eth_call // don't go through Ante Handler. - if leftoverGas < intrinsicGas { + if gasRemaining < intrinsicGasCost { // eth_estimateGas will check for this exact error return nil, errors.Wrapf( core.ErrIntrinsicGas, "ApplyEvmMsg: provided msg.Gas (%d) is less than intrinsic gas cost (%d)", - leftoverGas, intrinsicGas, + gasRemaining, intrinsicGasCost, ) } - leftoverGas = leftoverGas - intrinsicGas + gasRemaining -= intrinsicGasCost // access list preparation is moved from ante handler to here, because it's // needed when `ApplyMessage` is called under contexts where ante handlers @@ -310,28 +316,28 @@ func (k *Keeper) ApplyEvmMsg( // take over the nonce management from evm: // - reset sender's nonce to msg.Nonce() before calling evm. // - increase sender's nonce by one no matter the result. - evmObj.StateDB.SetNonce(sender.Address(), msg.Nonce()) + evmObj.StateDB.SetNonce(msg.From(), msg.Nonce()) - var ret []byte + var returnBz []byte var vmErr error if contractCreation { - ret, _, leftoverGas, vmErr = evmObj.Create( - sender, + returnBz, _, gasRemaining, vmErr = evmObj.Create( + vm.AccountRef(msg.From()), msg.Data(), - leftoverGas, + gasRemaining, msgWei, ) } else { - ret, leftoverGas, vmErr = evmObj.Call( - sender, + returnBz, gasRemaining, vmErr = evmObj.Call( + vm.AccountRef(msg.From()), *msg.To(), msg.Data(), - leftoverGas, + gasRemaining, msgWei, ) } // Increment nonce after processing the message - evmObj.StateDB.SetNonce(sender.Address(), msg.Nonce()+1) + evmObj.StateDB.SetNonce(msg.From(), msg.Nonce()+1) // EVM execution error needs to be available for the JSON-RPC client var vmError string @@ -339,57 +345,34 @@ func (k *Keeper) ApplyEvmMsg( vmError = vmErr.Error() } - // The dirty states in `StateDB` is either committed or discarded after return - if commit { - if err := evmObj.StateDB.(*statedb.StateDB).Commit(); err != nil { - return nil, errors.Wrap(err, "ApplyEvmMsg: failed to commit stateDB") - } - } - // Rare case of uint64 gas overflow - if msg.Gas() < leftoverGas { - return nil, errors.Wrapf(core.ErrGasUintOverflow, "ApplyEvmMsg: message gas limit (%d) < leftover gas (%d)", msg.Gas(), leftoverGas) - } + // process gas refunds (we refund a portion of the unused gas) + gasUsed := msg.Gas() - gasRemaining + // please see https://eips.ethereum.org/EIPS/eip-3529 for why we do refunds + refundAmount := gasToRefund(evmObj.StateDB.GetRefund(), gasUsed) + gasRemaining += refundAmount + gasUsed -= refundAmount - // TODO: UD-DEBUG: Clarify text below. - // GAS REFUND - // If msg.Gas() > gasUsed, we need to refund extra gas. - // leftoverGas = amount of extra (not used) gas. - // If the msg comes from user, we apply refundQuotient capping the refund to 20% of used gas - // If msg is internal (funtoken), we refund 100% - // - // EIP-3529: refunds are capped to gasUsed / 5 - // We evaluate "fullRefundLeftoverGas" and use only the gas consumed (not the - // gas limit) if the `ApplyEvmMsg` call originated from a state transition - // where the chain set the gas limit and not an end-user. - refundQuotient := params.RefundQuotientEIP3529 - if fullRefundLeftoverGas { - refundQuotient = 1 // 100% refund + evmResp := &evm.MsgEthereumTxResponse{ + GasUsed: gasUsed, + VmError: vmError, + Ret: returnBz, + Logs: evm.NewLogsFromEth(evmObj.StateDB.(*statedb.StateDB).Logs()), + Hash: txHash.Hex(), } - temporaryGasUsed := msg.Gas() - leftoverGas - refund := GasToRefund(evmObj.StateDB.GetRefund(), temporaryGasUsed, refundQuotient) - // update leftoverGas and temporaryGasUsed with refund amount - leftoverGas += refund - temporaryGasUsed -= refund - if msg.Gas() < leftoverGas { - return nil, errors.Wrapf(core.ErrGasUintOverflow, "ApplyEvmMsg: message gas limit (%d) < leftover gas (%d)", msg.Gas(), leftoverGas) + if gasRemaining > msg.Gas() { // rare case of overflow + evmResp.GasUsed = msg.Gas() // cap the gas used to the original gas limit + return evmResp, errors.Wrapf(core.ErrGasUintOverflow, "ApplyEvmMsg: message gas limit (%d) < leftover gas (%d)", msg.Gas(), gasRemaining) } - // Min gas used is a % of gasLimit - gasUsed := math.LegacyNewDec(int64(temporaryGasUsed)).TruncateInt().Uint64() - - // This resulting "leftoverGas" is used by the tracer. This happens as a - // result of the defer statement near the beginning of the function with - // "vm.Tracer". - leftoverGas = msg.Gas() - gasUsed + // The dirty states in `StateDB` is either committed or discarded after return + if commit { + if err := evmObj.StateDB.(*statedb.StateDB).Commit(); err != nil { + return evmResp, errors.Wrap(err, "ApplyEvmMsg: failed to commit stateDB") + } + } - return &evm.MsgEthereumTxResponse{ - GasUsed: gasUsed, - VmError: vmError, - Ret: ret, - Logs: evm.NewLogsFromEth(evmObj.StateDB.(*statedb.StateDB).Logs()), - Hash: txHash.Hex(), - }, nil + return evmResp, nil } func ParseWeiAsMultipleOfMicronibi(weiInt *big.Int) (newWeiInt *big.Int, err error) { @@ -552,6 +535,7 @@ func (k Keeper) convertCoinToEvmBornCoin( defer func() { k.Bank.StateDB = nil }() + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) evmResp, err := k.CallContractWithInput( ctx, @@ -565,15 +549,13 @@ func (k Keeper) convertCoinToEvmBornCoin( if err != nil { return nil, err } - ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "mint erc20 tokens") if evmResp.Failed() { return nil, fmt.Errorf("failed to mint erc-20 tokens of contract %s", erc20Addr.String()) } - err = stateDB.Commit() - if err != nil { + if err = stateDB.Commit(); err != nil { return nil, errors.Wrap(err, "failed to commit stateDB") } diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go index 6a1aa210b..82739c612 100644 --- a/x/evm/precompile/funtoken.go +++ b/x/evm/precompile/funtoken.go @@ -143,8 +143,7 @@ func (p precompileFunToken) sendToBank( erc20, amount, to, err := p.parseArgsSendToBank(args) if err != nil { - err = ErrInvalidArgs(err) - return + return nil, ErrInvalidArgs(err) } var evmResponses []*evm.MsgEthereumTxResponse @@ -191,10 +190,9 @@ 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, e := p.evmKeeper.ERC20().Burn(erc20, evm.EVM_MODULE_ADDRESS, gotAmount, ctx, evmObj) - if e != nil { - err = fmt.Errorf("ERC20.Burn: %w", e) - return + burnResp, 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 { diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go index 2cd2bd34b..be9a33714 100644 --- a/x/evm/precompile/funtoken_test.go +++ b/x/evm/precompile/funtoken_test.go @@ -119,15 +119,14 @@ func (s *FuntokenSuite) TestHappyPath() { deps := evmtest.NewTestDeps() s.T().Log("Create FunToken mapping and ERC20") - bankDenom := "unibi" - funtoken := evmtest.CreateFunTokenForBankCoin(deps, bankDenom, &s.Suite) + funtoken := evmtest.CreateFunTokenForBankCoin(deps, evm.EVMBankDenom, &s.Suite) erc20 := funtoken.Erc20Addr.Address s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, - sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewInt(69_420))), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), )) s.Run("IFunToken.bankBalance()", func() { @@ -152,17 +151,25 @@ func (s *FuntokenSuite) TestHappyPath() { s.Equal(deps.Sender.NibiruAddr.String(), bech32Addr) }) - _, err := deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(bankDenom, sdk.NewInt(69_420)), - ToEthAddr: eth.EIP55Addr{ - Address: deps.Sender.EthAddr, + s.Run("ConvertCoinToEvm", func() { + _, err := deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420)), + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, }, - }, - ) - s.Require().NoError(err) + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_420), "expect 69420 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, deps.Sender.EthAddr, big.NewInt(0), "expect the sender to have zero balance") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect x/evm module to escrow all tokens") + }) s.Run("Mint tokens - Fail from non-owner", func() { contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) @@ -187,7 +194,6 @@ func (s *FuntokenSuite) TestHappyPath() { s.NoError(err) evmObj, _ := deps.NewEVM() - ethTxResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, @@ -199,15 +205,19 @@ func (s *FuntokenSuite) TestHappyPath() { ) s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError) - s.True(deps.App.BankKeeper == deps.App.EvmKeeper.Bank) evmtest.AssertERC20BalanceEqualWithDescription( - s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_000), "expect 69000 balance", + s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_000), "expect 69000 balance remaining", ) evmtest.AssertERC20BalanceEqualWithDescription( s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", ) - s.Require().True(deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, funtoken.BankDenom).Amount.Equal(sdk.NewInt(420))) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, eth.NibiruAddrToEthAddr(randomAcc), big.NewInt(420), "expect 420 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_000), "expect 69000 balance", + ) s.T().Log("Parse the response contract addr and response bytes") var sentAmt *big.Int @@ -220,8 +230,9 @@ func (s *FuntokenSuite) TestHappyPath() { }) s.Run("IFuntoken.balance", func() { - contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("balance", deps.Sender.EthAddr, erc20) + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack(string(precompile.FunTokenMethod_balance), deps.Sender.EthAddr, erc20) s.Require().NoError(err) + evmObj, _ := deps.NewEVM() evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, @@ -256,13 +267,14 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { s.Require().NoError(err) contractAddr := deployResp.ContractAddr - s.T().Log("Fund sender's wallet") - s.Require().NoError(testapp.FundAccount( - deps.App.BankKeeper, - deps.Ctx, - deps.Sender.NibiruAddr, - sdk.NewCoins(sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(1000))), - )) + s.Run("Fund sender's wallet", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(1000))), + )) + }) s.Run("Fund contract with erc20 coins", func() { _, err = deps.EvmKeeper.ConvertCoinToEvm( @@ -286,7 +298,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { ) s.Require().NoError(err) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + resp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -296,6 +308,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) + s.Require().NotZero(resp.GasUsed) }) s.Run("Happy: callBankSend with local gas - sufficient gas amount", func() { @@ -307,7 +320,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { ) s.Require().NoError(err) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + resp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -317,6 +330,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call ) s.Require().NoError(err) + s.Require().NotZero(resp.GasUsed) }) s.Run("Sad: callBankSend with local gas - insufficient gas amount", func() { @@ -328,7 +342,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { ) s.Require().NoError(err) evmObj, _ := deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( + resp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -338,6 +352,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call ) s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(resp.GasUsed) }) } @@ -363,7 +378,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { s.Run("Call sendToEvm(string bankDenom, uint256 amount, string to)", func() { contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( - "sendToEvm", + string(precompile.FunTokenMethod_sendToEvm), bankDenom, big.NewInt(1000), deps.Sender.EthAddr.Hex(), @@ -382,20 +397,19 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError, "sendToEvm VMError") - s.T().Log("4) The response returns the actual minted/unescrowed amount") + s.T().Log("The response returns the actual minted/unescrowed amount") var amountSent *big.Int err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( - &amountSent, "sendToEvm", ethTxResp.Ret, + &amountSent, string(precompile.FunTokenMethod_sendToEvm), ethTxResp.Ret, ) s.Require().NoError(err) s.Require().EqualValues(1000, amountSent.Int64(), "expect 1000 minted to EVM") s.T().Log("Check the user lost 1000 ulibi in bank") - bankBal := deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, bankDenom).Amount.BigInt() - s.EqualValues(big.NewInt(234), bankBal, "did user lose 1000 ulibi from bank?") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, bankDenom, deps.Sender.EthAddr, big.NewInt(234), "did user lose 1000 ulibi from bank?") - // check the evm module account balance - s.EqualValues(big.NewInt(1000), deps.App.BankKeeper.GetBalance(deps.Ctx, evm.EVM_MODULE_ADDRESS_NIBI, bankDenom).Amount.BigInt()) + s.T().Log("Check the module account has 1000 ulibi") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(1000), "expect 1000 balance") s.T().Log("Check the user gained 1000 in ERC20 representation") evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, deps.Sender.EthAddr, big.NewInt(1000), "expect 1000 balance") @@ -407,16 +421,17 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { // We'll pick a brand new random account to receive them. s.Run("Sending 400 tokens back from EVM to Cosmos bank => recipient:", func() { - recipient := testutil.AccAddress() + randomRecipient := testutil.AccAddress() + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( string(precompile.FunTokenMethod_sendToBank), erc20Addr, big.NewInt(400), - recipient.String(), + randomRecipient.String(), ) s.Require().NoError(err) - ethExResp, err := deps.EvmKeeper.CallContractWithInput( + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -426,13 +441,13 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { evmtest.FunTokenGasLimitSendToEvm, ) s.Require().NoError(err) - s.Require().Empty(ethExResp.VmError, "sendToBank VMError") + s.Require().Empty(ethTxResp.VmError, "sendToBank VMError") s.T().Log("Parse the returned amount from `sendToBank`") var actualSent *big.Int err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( &actualSent, string(precompile.FunTokenMethod_sendToBank), - ethExResp.Ret, + ethTxResp.Ret, ) s.Require().NoError(err) s.Require().EqualValues(big.NewInt(400), actualSent, "expect 400 minted back to bank") @@ -450,10 +465,24 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { ) s.T().Log("Check the bank side got 400 more") - s.Require().EqualValues(big.NewInt(400), deps.App.BankKeeper.GetBalance(deps.Ctx, recipient, bankDenom).Amount.BigInt(), "did the recipient get 400?") + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), + deps, + bankDenom, + eth.NibiruAddrToEthAddr(randomRecipient), + big.NewInt(400), + "did the recipient get 400?", + ) s.T().Log("Confirm module account doesn't keep them (burn or escrow) for bank-based tokens") - s.Require().EqualValues(big.NewInt(600), deps.App.BankKeeper.GetBalance(deps.Ctx, evm.EVM_MODULE_ADDRESS[:], bankDenom).Amount.BigInt(), "module should now have 600 left escrowed") + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), + deps, + bankDenom, + evm.EVM_MODULE_ADDRESS, + big.NewInt(600), + "module should now have 600 left escrowed", + ) }) } @@ -476,7 +505,6 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { // - unescrow erc20 token deps := evmtest.NewTestDeps() - evmObj, _ := deps.NewEVM() alice := evmtest.NewEthPrivAcc() bob := evmtest.NewEthPrivAcc() @@ -492,9 +520,6 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { s.Require().NoError(err, "failed to deploy test ERC20") erc20Addr := erc20Resp.ContractAddr - // the initial supply was sent to the deployer - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, deps.Sender.EthAddr, bigTokens(1_000_000), "expect nonzero balance") - // create fun token from that erc20 _, err = deps.EvmKeeper.CreateFunToken( sdk.WrapSDKContext(deps.Ctx), @@ -506,67 +531,73 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { s.Require().NoError(err) // Transfer 500 tokens to bob => 500 * 10^18 raw - contractInput, err := embeds.SmartContract_TestERC20.ABI.Pack( - "transfer", - bob.EthAddr, - bigTokens(500), // 500 in human sense - ) - s.Require().NoError(err) - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &erc20Addr, - true, - contractInput, - keeper.Erc20GasLimitExecute, - ) - s.Require().NoError(err) + s.Run("Transfer 500 tokens to bob", func() { + contractInput, err := embeds.SmartContract_TestERC20.ABI.Pack( + "transfer", + bob.EthAddr, + bigTokens(500), // 500 in human sense + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &erc20Addr, + true, + contractInput, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) - // Now user should have 500 tokens => raw is 500 * 10^18 - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + // Now user should have 500 tokens => raw is 500 * 10^18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + }) // sendToBank: e.g. 100 tokens => 100 * 1e18 raw // expects to escrow on EVM side and mint on cosmos side - contractInput, err = embeds.SmartContract_FunToken.ABI.Pack( - string(precompile.FunTokenMethod_sendToBank), - erc20Addr, // address - bigTokens(100), - alice.NibiruAddr.String(), - ) - s.Require().NoError(err) - resp, err := deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - bob.EthAddr, /* from */ - &precompile.PrecompileAddr_FunToken, /* to */ - true, /* commit */ - contractInput, - evmtest.FunTokenGasLimitSendToEvm, /* gasLimit */ - ) - s.Require().NoError(err) - s.Require().Empty(resp.VmError) + s.Run("send 100 tokens to alice", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + string(precompile.FunTokenMethod_sendToBank), + erc20Addr, // address + bigTokens(100), + alice.NibiruAddr.String(), + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + bob.EthAddr, /* from */ + &precompile.PrecompileAddr_FunToken, /* to */ + true, /* commit */ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, /* gasLimit */ + ) + s.Require().NoError(err) + s.Require().Empty(resp.VmError) - // Bank side should see 100 - bankBal := deps.App.BankKeeper.GetBalance(deps.Ctx, alice.NibiruAddr, "erc20/"+erc20Addr.Hex()) - s.Require().EqualValues(bigTokens(100), bankBal.Amount.BigInt()) + // Bank side should see 100 + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), alice.EthAddr, bigTokens(100), "expect 100 balance") - // Expect user to have 400 tokens => 400 * 10^18 - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(400), "expect Bob's balance to be 400") + // Expect user to have 400 tokens => 400 * 10^18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(400), "expect Bob's balance to be 400") - // 100 tokens are escrowed - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(100), "expect EVM module to escrow 100 tokens") + // 100 tokens are escrowed + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(100), "expect EVM module to escrow 100 tokens") + }) // Finally sendToEvm(100) -> (expects to burn on cosmos side and unescrow in the EVM side) s.Run("send 100 tokens back to Bob", func() { contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( "sendToEvm", - bankBal.Denom, + "erc20/"+erc20Addr.Hex(), bigTokens(100), bob.EthAddr.Hex(), ) s.Require().NoError(err) - resp, err = deps.EvmKeeper.CallContractWithInput( + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, alice.EthAddr, @@ -577,25 +608,20 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { ) s.Require().NoError(err) s.Require().Empty(resp.VmError) - }) - // no bank side left for alice - balAfter := deps.App.BankKeeper.GetBalance(deps.Ctx, alice.NibiruAddr, bankBal.Denom).Amount.BigInt() - s.Require().EqualValues(bigTokens(0), balAfter) + // no bank side left for alice + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), alice.EthAddr, bigTokens(0), "expect 0 balance") - // check bob has 500 tokens again => 500 * 1e18 - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + // check bob has 500 tokens again => 500 * 1e18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") - // check evm module account's balance, it should have escrowed some tokens - // unescrow the tokens - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(0), "expect zero balance") + // check evm module account's balance, it should have escrowed some tokens + // unescrow the tokens + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(0), "expect zero balance") - // burns the bank tokens - evmBal2 := deps.App.BankKeeper.GetBalance(deps.Ctx, evm.EVM_MODULE_ADDRESS[:], bankBal.Denom).Amount.BigInt() - s.Require().EqualValues(bigTokens(0), evmBal2) - - // user has 500 tokens again => 500 * 1e18 - evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + // burns the bank tokens + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), evm.EVM_MODULE_ADDRESS, bigTokens(0), "expect 0 balance") + }) } // FunTokenWhoAmIReturn holds the return values from the "IFuntoken.whoAmI" diff --git a/x/evm/precompile/test/bank_transfer.wasm b/x/evm/precompile/test/bank_transfer.wasm new file mode 100644 index 000000000..7f1fa8f6f Binary files /dev/null and b/x/evm/precompile/test/bank_transfer.wasm differ diff --git a/x/evm/precompile/test/counter.wasm b/x/evm/precompile/test/counter.wasm new file mode 100644 index 000000000..0e1cba209 Binary files /dev/null and b/x/evm/precompile/test/counter.wasm differ diff --git a/x/evm/precompile/test/export.go b/x/evm/precompile/test/export.go index e37c99b09..01104f0bf 100644 --- a/x/evm/precompile/test/export.go +++ b/x/evm/precompile/test/export.go @@ -2,7 +2,6 @@ package test import ( "encoding/json" - "math/big" "os" "os/exec" "path" @@ -15,7 +14,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/v2/app" - serverconfig "github.com/NibiruChain/nibiru/v2/app/server/config" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" "github.com/NibiruChain/nibiru/v2/x/evm/precompile" @@ -30,7 +28,7 @@ const ( // SetupWasmContracts stores all Wasm bytecode and has the "deps.Sender" // instantiate each Wasm contract using the precompile. -func SetupWasmContracts(deps *evmtest.TestDeps, evmObj *vm.EVM, s *suite.Suite) ( +func SetupWasmContracts(deps *evmtest.TestDeps, s *suite.Suite) ( contracts []sdk.AccAddress, ) { wasmCodes := deployWasmBytecode(s, deps.Ctx, deps.Sender.NibiruAddr, deps.App) @@ -47,48 +45,29 @@ func SetupWasmContracts(deps *evmtest.TestDeps, evmObj *vm.EVM, s *suite.Suite) InstantiateMsg: []byte(`{"count": 0}`), Label: "https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter", }, + { + InstantiateMsg: []byte(`{}`), + Label: "https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/bank-transfer/", + }, + { + InstantiateMsg: []byte(`{}`), + Label: "https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/staking/", + }, } for i, wasmCode := range wasmCodes { - s.T().Logf("Instantiate using Wasm precompile: %s", wasmCode.binPath) - - m := wasm.MsgInstantiateContract{ - Admin: "", - CodeID: wasmCode.codeId, - Label: instantiateArgs[i].Label, - Msg: instantiateArgs[i].InstantiateMsg, - } + s.T().Logf("Instantiate %s", wasmCode.binPath) - msgArgsBz, err := json.Marshal(m.Msg) - s.NoError(err) - - contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( - string(precompile.WasmMethod_instantiate), - m.Admin, m.CodeID, msgArgsBz, m.Label, []precompile.WasmBankCoin{}, - ) - s.NoError(err) - - ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + wasmPermissionedKeeper := wasmkeeper.NewDefaultPermissionKeeper(deps.App.WasmKeeper) + contractAddr, _, err := wasmPermissionedKeeper.Instantiate( deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &precompile.PrecompileAddr_Wasm, - true, - contractInput, - WasmGasLimitInstantiate, - ) - - s.Require().NoError(err) - s.Require().NotEmpty(ethTxResp.Ret) - - s.T().Log("Parse the response contract addr and response bytes") - vals, err := embeds.SmartContract_Wasm.ABI.Unpack( - string(precompile.WasmMethod_instantiate), - ethTxResp.Ret, + wasmCode.codeId, + deps.Sender.NibiruAddr, + deps.Sender.NibiruAddr, + instantiateArgs[i].InstantiateMsg, + instantiateArgs[i].Label, + sdk.Coins{}, ) - s.Require().NoError(err) - - contractAddr, err := sdk.AccAddressFromBech32(vals[0].(string)) s.NoError(err) contracts = append(contracts, contractAddr) } @@ -102,7 +81,7 @@ func deployWasmBytecode( s *suite.Suite, ctx sdk.Context, sender sdk.AccAddress, - nibiru *app.NibiruApp, + app *app.NibiruApp, ) (codeIds []struct { codeId uint64 binPath string @@ -121,7 +100,15 @@ func deployWasmBytecode( // hello_world_counter.wasm is a compiled version of: // https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter - "x/evm/precompile/hello_world_counter.wasm", + "x/evm/precompile/test/hello_world_counter.wasm", + + // bank_transfer.wasm is a compiled version of: + // https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/bank-transfer/ + "x/evm/precompile/test/bank_transfer.wasm", + + // staking.wasm is a compiled version of: + // https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/staking/ + "x/evm/precompile/test/staking.wasm", // Add other wasm bytecode here if needed... } { @@ -135,12 +122,11 @@ func deployWasmBytecode( // The "Create" fn is private on the nibiru.WasmKeeper. By placing it as the // decorated keeper in PermissionedKeeper type, we can access "Create" as a // public fn. - wasmPermissionedKeeper := wasmkeeper.NewDefaultPermissionKeeper(nibiru.WasmKeeper) - instantiateAccess := &wasm.AccessConfig{ - Permission: wasm.AccessTypeEverybody, - } + wasmPermissionedKeeper := wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper) codeId, _, err := wasmPermissionedKeeper.Create( - ctx, sender, wasmBytecode, instantiateAccess, + ctx, sender, wasmBytecode, &wasm.AccessConfig{ + Permission: wasm.AccessTypeEverybody, + }, ) s.Require().NoError(err) codeIds = append(codeIds, struct { @@ -163,7 +149,6 @@ func deployWasmBytecode( func AssertWasmCounterState( s *suite.Suite, deps evmtest.TestDeps, - evmObj *vm.EVM, wasmContract sdk.AccAddress, wantCount int64, ) { @@ -173,6 +158,30 @@ func AssertWasmCounterState( } `) + resp, err := deps.App.WasmKeeper.QuerySmart(deps.Ctx, wasmContract, msgArgsBz) + s.Require().NoError(err) + s.Require().NotEmpty(resp) + + var typedResp QueryMsgCountResp + s.NoError(json.Unmarshal(resp, &typedResp)) + + s.EqualValues(wantCount, typedResp.Count) + s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +func AssertWasmCounterStateWithEvm( + s *suite.Suite, + deps evmtest.TestDeps, + evmObj *vm.EVM, + wasmContract sdk.AccAddress, + wantCount int64, +) { + msgArgsBz := []byte(` + { + "count": {} + } + `) + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( string(precompile.WasmMethod_query), wasmContract.String(), @@ -180,7 +189,7 @@ func AssertWasmCounterState( ) s.Require().NoError(err) - ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + evmResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, @@ -190,26 +199,14 @@ func AssertWasmCounterState( WasmGasLimitQuery, ) s.Require().NoError(err) - s.Require().NotEmpty(ethTxResp.Ret) - - s.T().Log("Parse the response contract addr and response bytes") - s.T().Logf("ethTxResp.Ret: %s", ethTxResp.Ret) + s.Require().NotEmpty(evmResp.Ret) var queryResp []byte - err = embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( - // Since there's only one return value, don't unpack as a slice. - // If there were two or more return values, we'd use - // &[]any{...} + s.NoError(embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( &queryResp, string(precompile.WasmMethod_query), - ethTxResp.Ret, - ) - s.Require().NoError(err) - s.T().Logf("queryResp: %s", queryResp) - - var wasmMsg wasm.RawContractMessage - s.NoError(json.Unmarshal(queryResp, &wasmMsg)) - s.NoError(wasmMsg.ValidateBasic()) + evmResp.Ret, + )) var typedResp QueryMsgCountResp s.NoError(json.Unmarshal(queryResp, &typedResp)) @@ -275,36 +272,24 @@ func IncrementWasmCounterWithExecuteMulti( } `) - // Parse funds argument. - var funds []precompile.WasmBankCoin // blank funds - fundsJson, err := json.Marshal(funds) - s.NoErrorf(err, "fundsJson: %s", fundsJson) - err = json.Unmarshal(fundsJson, &funds) - s.Require().NoError(err, "fundsJson %s, funds %s", fundsJson, funds) - // The "times" arg determines the number of messages in the executeMsgs slice executeMsgs := []struct { ContractAddr string `json:"contractAddr"` MsgArgs []byte `json:"msgArgs"` Funds []precompile.WasmBankCoin `json:"funds"` - }{ - {wasmContract.String(), msgArgsBz, funds}, - } - if times == 0 { - executeMsgs = executeMsgs[:0] // force empty - } else { - for i := uint(1); i < times; i++ { - executeMsgs = append(executeMsgs, executeMsgs[0]) - } - } - s.Require().Len(executeMsgs, int(times)) // sanity check assertion + }{} - callArgs := []any{ - executeMsgs, + for i := uint(0); i < times; i++ { + executeMsgs = append(executeMsgs, struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []precompile.WasmBankCoin `json:"funds"` + }{wasmContract.String(), msgArgsBz, []precompile.WasmBankCoin{}}) } - input, err := embeds.SmartContract_Wasm.ABI.Pack( + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( string(precompile.WasmMethod_executeMulti), - callArgs..., + executeMsgs, ) s.Require().NoError(err) @@ -314,60 +299,9 @@ func IncrementWasmCounterWithExecuteMulti( deps.Sender.EthAddr, &precompile.PrecompileAddr_Wasm, commit, - input, + contractInput, WasmGasLimitExecute, ) s.Require().NoError(err) s.Require().NotEmpty(ethTxResp.Ret) } - -func IncrementWasmCounterWithExecuteMultiViaVMCall( - s *suite.Suite, - deps *evmtest.TestDeps, - wasmContract sdk.AccAddress, - times int, - finalizeTx bool, - evmObj *vm.EVM, -) error { - msgArgsBz := []byte(` - { - "increment": {} - } - `) - - // Parse funds argument. - var funds []precompile.WasmBankCoin // blank funds - fundsJson, err := json.Marshal(funds) - s.NoErrorf(err, "fundsJson: %s", fundsJson) - err = json.Unmarshal(fundsJson, &funds) - s.Require().NoError(err, "fundsJson %s, funds %s", fundsJson, funds) - - // The "times" arg determines the number of messages in the executeMsgs slice - executeMsgs := []struct { - ContractAddr string `json:"contractAddr"` - MsgArgs []byte `json:"msgArgs"` - Funds []precompile.WasmBankCoin `json:"funds"` - }{} - for i := 0; i < times; i++ { - executeMsgs = append(executeMsgs, struct { - ContractAddr string `json:"contractAddr"` - MsgArgs []byte `json:"msgArgs"` - Funds []precompile.WasmBankCoin `json:"funds"` - }{wasmContract.String(), msgArgsBz, funds}) - } - - contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( - string(precompile.WasmMethod_executeMulti), - executeMsgs, - ) - s.Require().NoError(err) - - _, _, err = evmObj.Call( - vm.AccountRef(deps.Sender.EthAddr), - precompile.PrecompileAddr_Wasm, - contractInput, - serverconfig.DefaultEthCallGasLimit, - big.NewInt(0), - ) - return err -} diff --git a/x/evm/precompile/hello_world_counter.wasm b/x/evm/precompile/test/hello_world_counter.wasm similarity index 100% rename from x/evm/precompile/hello_world_counter.wasm rename to x/evm/precompile/test/hello_world_counter.wasm diff --git a/x/evm/precompile/test/infinite_loop.wasm b/x/evm/precompile/test/infinite_loop.wasm new file mode 100644 index 000000000..d58ca4100 Binary files /dev/null and b/x/evm/precompile/test/infinite_loop.wasm differ diff --git a/x/evm/precompile/test/staking.wasm b/x/evm/precompile/test/staking.wasm new file mode 100644 index 000000000..d902c74af Binary files /dev/null and b/x/evm/precompile/test/staking.wasm differ diff --git a/x/evm/precompile/test/wasteful_gas.wasm b/x/evm/precompile/test/wasteful_gas.wasm new file mode 100644 index 000000000..873a07113 Binary files /dev/null and b/x/evm/precompile/test/wasteful_gas.wasm differ diff --git a/x/evm/precompile/wasm_test.go b/x/evm/precompile/wasm_test.go index 3bedd115b..6698e6439 100644 --- a/x/evm/precompile/wasm_test.go +++ b/x/evm/precompile/wasm_test.go @@ -6,18 +6,22 @@ import ( "math/big" "testing" + "cosmossdk.io/math" wasm "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" + "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/common/testutil" "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" "github.com/NibiruChain/nibiru/v2/x/evm/precompile" "github.com/NibiruChain/nibiru/v2/x/evm/precompile/test" tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" ) // rough gas limits for wasm execution - used in tests only @@ -34,11 +38,48 @@ func TestWasmSuite(t *testing.T) { suite.Run(t, new(WasmSuite)) } -func (s *WasmSuite) TestExecuteHappy() { +func (s *WasmSuite) TestInstantiate() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + test.SetupWasmContracts(&deps, &s.Suite) + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_instantiate), + "", // admin + uint64(1), // codeId + []byte(`{}`), // instantiateMsg + "some non-empty label", // label + []precompile.WasmBankCoin{}, // funds + ) + s.Require().NoError(err) + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(ethTxResp.Ret) + + vals, err := embeds.SmartContract_Wasm.ABI.Unpack( + string(precompile.WasmMethod_instantiate), + ethTxResp.Ret, + ) + s.Require().NoError(err) + s.Require().NotEmpty(vals[0].(string)) +} + +func (s *WasmSuite) TestExecute() { deps := evmtest.NewTestDeps() evmObj, _ := deps.NewEVM() - wasmContracts := test.SetupWasmContracts(&deps, evmObj, &s.Suite) + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) wasmContract := wasmContracts[0] // nibi_stargate.wasm s.Run("create denom", func() { @@ -50,17 +91,11 @@ func (s *WasmSuite) TestExecuteHappy() { } `) - var funds []precompile.WasmBankCoin - fundsJson, err := json.Marshal(funds) - s.NoErrorf(err, "fundsJson: %s", fundsJson) - err = json.Unmarshal(fundsJson, &funds) - s.Require().NoError(err, "fundsJson %s, funds %s", fundsJson, funds) - contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( string(precompile.WasmMethod_execute), wasmContract.String(), msgArgsBz, - funds, + []precompile.WasmBankCoin{}, ) s.Require().NoError(err) ethTxResp, err := deps.EvmKeeper.CallContractWithInput( @@ -76,7 +111,6 @@ func (s *WasmSuite) TestExecuteHappy() { s.Require().NotEmpty(ethTxResp.Ret) }) - s.T().Log("Execute: mint tokens") s.Run("mint tokens", func() { coinDenom := tokenfactory.TFDenom{ Creator: wasmContract.String(), @@ -98,6 +132,7 @@ func (s *WasmSuite) TestExecuteHappy() { ) s.Require().NoError(err) + evmObj, _ = deps.NewEVM() ethTxResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, @@ -115,82 +150,104 @@ func (s *WasmSuite) TestExecuteHappy() { }) } -func (s *WasmSuite) TestExecuteMultiHappy() { +func (s *WasmSuite) TestExecuteMulti() { deps := evmtest.NewTestDeps() evmObj, _ := deps.NewEVM() - wasmContracts := test.SetupWasmContracts(&deps, evmObj, &s.Suite) + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) wasmContract := wasmContracts[1] // hello_world_counter.wasm // count = 0 - test.AssertWasmCounterState(&s.Suite, deps, evmObj, wasmContract, 0) + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) // count += 2 test.IncrementWasmCounterWithExecuteMulti( &s.Suite, &deps, evmObj, wasmContract, 2, true) // count = 2 - test.AssertWasmCounterState(&s.Suite, deps, evmObj, wasmContract, 2) - s.assertWasmCounterStateRaw(deps, wasmContract, 2) + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 2) // count += 67 test.IncrementWasmCounterWithExecuteMulti( &s.Suite, &deps, evmObj, wasmContract, 67, true) // count = 69 - test.AssertWasmCounterState(&s.Suite, deps, evmObj, wasmContract, 69) - s.assertWasmCounterStateRaw(deps, wasmContract, 69) + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 69) } -// From IWasm.query of Wasm.sol: -// -// ```solidity -// function queryRaw( -// string memory contractAddr, -// bytes memory key -// ) external view returns (bytes memory response); -// ``` -func (s *WasmSuite) assertWasmCounterStateRaw( - deps evmtest.TestDeps, - wasmContract sdk.AccAddress, - wantCount int64, -) { +func (s *WasmSuite) TestQueryRaw() { + deps := evmtest.NewTestDeps() + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( string(precompile.WasmMethod_queryRaw), wasmContract.String(), []byte(`state`), ) s.Require().NoError(err) - evmObj, _ := deps.NewEVM() - ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + evmObj, _ := deps.NewEVM() + queryResp, err := deps.EvmKeeper.CallContractWithInput( deps.Ctx, evmObj, deps.Sender.EthAddr, &precompile.PrecompileAddr_Wasm, - true, + false, // commit contractInput, WasmGasLimitQuery, ) - s.Require().NoError(err) - s.Require().NotEmpty(ethTxResp.Ret) - s.T().Log("Parse the response contract addr and response bytes") - s.T().Logf("ethTxResp.Ret: %s", ethTxResp.Ret) + s.Require().NoError(err) + s.Require().NotEmpty(queryResp.Ret) - var queryResp []byte + var respBz []byte err = embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( - &queryResp, + &respBz, string(precompile.WasmMethod_queryRaw), - ethTxResp.Ret, + queryResp.Ret, + ) + s.Require().NoError(err, "ethTxResp: %s", queryResp) + + var typedResp test.QueryMsgCountResp + s.NoError(json.Unmarshal(respBz, &typedResp)) + s.EqualValues(0, typedResp.Count) + s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +func (s *WasmSuite) TestQuerySmart() { + deps := evmtest.NewTestDeps() + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_query), + wasmContract.String(), + []byte(`{"count": {}}`), ) s.Require().NoError(err) - s.T().Logf("queryResp: %s", queryResp) - var wasmMsg wasm.RawContractMessage - s.NoError(wasmMsg.UnmarshalJSON(queryResp), queryResp) - s.T().Logf("wasmMsg: %s", wasmMsg) - s.NoError(wasmMsg.ValidateBasic()) + evmObj, _ := deps.NewEVM() + queryResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + false, // commit + contractInput, + WasmGasLimitQuery, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(queryResp.Ret) + + var respBz []byte + err = embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( + &respBz, + string(precompile.WasmMethod_query), + queryResp.Ret, + ) + s.Require().NoError(err, "ethTxResp: %s", queryResp) var typedResp test.QueryMsgCountResp - s.NoError(json.Unmarshal(wasmMsg, &typedResp)) - s.EqualValues(wantCount, typedResp.Count) - s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) + s.Require().NoError(json.Unmarshal(respBz, &typedResp)) + s.Require().EqualValues(0, typedResp.Count) + s.Require().EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) } func (s *WasmSuite) TestSadArgsCount() { @@ -357,24 +414,23 @@ type WasmExecuteMsg struct { func (s *WasmSuite) TestExecuteMultiValidation() { deps := evmtest.NewTestDeps() - evmObj, _ := deps.NewEVM() s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(100))), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(100))), )) - wasmContracts := test.SetupWasmContracts(&deps, evmObj, &s.Suite) + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) wasmContract := wasmContracts[1] // hello_world_counter.wasm - invalidMsgArgsBz := []byte(`{"invalid": "json"}`) // Invalid message format validMsgArgsBz := []byte(`{"increment": {}}`) // Valid increment message + invalidMsgArgsBz := []byte(`{"invalid": "json"}`) // Invalid message format var emptyFunds []precompile.WasmBankCoin validFunds := []precompile.WasmBankCoin{{ - Denom: "unibi", + Denom: evm.EVMBankDenom, Amount: big.NewInt(100), }} invalidFunds := []precompile.WasmBankCoin{{ @@ -500,11 +556,11 @@ func (s *WasmSuite) TestExecuteMultiPartialExecution() { deps := evmtest.NewTestDeps() evmObj, _ := deps.NewEVM() - wasmContracts := test.SetupWasmContracts(&deps, evmObj, &s.Suite) + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) wasmContract := wasmContracts[1] // hello_world_counter.wasm // First verify initial state is 0 - test.AssertWasmCounterState(&s.Suite, deps, evmObj, wasmContract, 0) + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) // Create a batch where the second message will fail validation executeMsgs := []WasmExecuteMsg{ @@ -540,5 +596,177 @@ func (s *WasmSuite) TestExecuteMultiPartialExecution() { s.Require().Contains(err.Error(), "unknown variant") // Verify that no state changes occurred - test.AssertWasmCounterState(&s.Suite, deps, evmObj, wasmContract, 0) + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) +} + +// TestDirtyStateAttack4 +// 1. Deploy a simple wasm contract that bank transfers NIBI to a recipient (Alice) +// 2. Calls the test contract +// a. call the wasm precompile which calls the wasm contract with a bank transfer +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI +// CONTRACT CALL: +// - Sends 1 NIBI to Alice via wasm precompile +// EXPECTED: +// - Test contract funds: 9 NIBI +// - Alice: 1 NIBI +func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack4() { + deps := evmtest.NewTestDeps() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[2] // bank_transfer.wasm + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestDirtyStateAttack4, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.Run("Send 10 NIBI to test contract manually", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + }) + + alice := evmtest.NewEthPrivAcc() + + s.Run("call test contract", func() { + msgArgsBz := []byte(fmt.Sprintf(` + { + "bank_transfer": { + "recipient": "%s" + } + } + `, alice.NibiruAddr)) + contractInput, err := embeds.SmartContract_TestDirtyStateAttack4.ABI.Pack( + "attack", + wasmContract.String(), + msgArgsBz, + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + + balanceAlice := deps.App.BankKeeper.GetBalance(deps.Ctx, alice.NibiruAddr, evm.EVMBankDenom) + s.Require().Equal(balanceAlice.Amount.BigInt(), big.NewInt(1e6)) + + balanceTestContract := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(testContractAddr), evm.EVMBankDenom) + s.Require().Equal(balanceTestContract.Amount.BigInt(), big.NewInt(9e6)) + }) +} + +// TestDirtyStateAttack5 +// 1. Deploy a simple wasm contract that stakes NIBI +// 2. Calls the test contract +// a. call the wasm precompile which calls the wasm contract that stakes 5 NIBI +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI +// CONTRACT CALL: +// - Sends 5 NIBI to the wasm contract +// - The wasm contract stakes 5 NIBI +// EXPECTED: +// - Test contract funds: 5 NIBI +// - Staked NIBI from wasm contract: 5 NIBI +// - Wasm contract: 0 NIBI +func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack5() { + deps := evmtest.NewTestDeps() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[3] // staking.wasm + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestDirtyStateAttack5, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.Run("Mint 10 NIBI to test contract", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + }) + + validator := evmtest.NewEthPrivAcc() + s.Run("create validator", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + validator.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(validator.NibiruAddr), + validator.PrivKey.PubKey(), + sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + stakingtypes.NewDescription("validator0", "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1)), + math.OneInt(), + ) + s.Require().NoError(err) + + stakingMsgServer := stakingkeeper.NewMsgServerImpl(deps.App.StakingKeeper) + resp, err := stakingMsgServer.CreateValidator(deps.Ctx, createValMsg) + s.Require().NoError(err) + s.Require().NotNil(resp) + }) + + s.Run("call test contract", func() { + msgArgsBz := []byte(fmt.Sprintf(`{"run": {"validator": "%s"}}`, sdk.ValAddress(validator.NibiruAddr).String())) + contractInput, err := embeds.SmartContract_TestDirtyStateAttack5.ABI.Pack( + "attack", + wasmContract.String(), + msgArgsBz, + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + + testContractBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(testContractAddr), evm.EVMBankDenom) + s.Require().Equal(testContractBalance.Amount.BigInt(), big.NewInt(5e6)) + + wasmContractBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, wasmContract, evm.EVMBankDenom) + s.Require().Equal(wasmContractBalance.Amount.BigInt(), big.NewInt(0)) + + delegations := deps.App.StakingKeeper.GetAllDelegatorDelegations(deps.Ctx, wasmContract) + s.Require().Equal(len(delegations), 1) + s.Require().Equal(sdk.ValAddress(validator.NibiruAddr).String(), delegations[0].ValidatorAddress) + + // after converting the wasm contract address to an eth address, check the balances + wasmContractEthAddr := eth.NibiruAddrToEthAddr(wasmContract) + balance := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(wasmContractEthAddr), evm.EVMBankDenom) + s.Require().Equal(big.NewInt(0), balance.Amount.BigInt()) + }) } diff --git a/x/evm/statedb/debug.go b/x/evm/statedb/debug.go index c2b5fb968..3927e4a5d 100644 --- a/x/evm/statedb/debug.go +++ b/x/evm/statedb/debug.go @@ -22,12 +22,6 @@ func (s *StateDB) DebugDirties() map[common.Address]int { return s.Journal.dirties } -// DebugEntries is a test helper that returns the sequence of [JournalChange] -// objects added during execution. -func (s *StateDB) DebugEntries() []JournalChange { - return s.Journal.entries -} - // DebugStateObjects is a test helper that returns returns a copy of the // [StateDB.stateObjects] map. func (s *StateDB) DebugStateObjects() map[common.Address]*stateObject { diff --git a/x/evm/statedb/journal_test.go b/x/evm/statedb/journal_test.go index 27582e69f..e10505a00 100644 --- a/x/evm/statedb/journal_test.go +++ b/x/evm/statedb/journal_test.go @@ -161,8 +161,6 @@ func (s *Suite) TestContractCallsAnotherContract() { func (s *Suite) TestJournalReversion() { deps := evmtest.NewTestDeps() - evmObj, _ := deps.NewEVM() - stateDB := evmObj.StateDB.(*statedb.StateDB) s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, @@ -171,11 +169,12 @@ func (s *Suite) TestJournalReversion() { )) s.T().Log("Set up helloworldcounter.wasm") - wasmContracts := test.SetupWasmContracts(&deps, evmObj, &s.Suite) + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) helloWorldCounterWasm := wasmContracts[1] fmt.Printf("wasmContract: %s\n", helloWorldCounterWasm) s.T().Log("commitEvmTx=true, expect 0 dirty journal entries") + evmObj, stateDB := deps.NewEVM() test.IncrementWasmCounterWithExecuteMulti( &s.Suite, &deps, evmObj, helloWorldCounterWasm, 7, true, ) @@ -185,6 +184,7 @@ func (s *Suite) TestJournalReversion() { } s.T().Log("commitEvmTx=false, expect dirty journal entries") + evmObj, stateDB = deps.NewEVM() test.IncrementWasmCounterWithExecuteMulti( &s.Suite, &deps, evmObj, helloWorldCounterWasm, 5, false, ) @@ -194,24 +194,27 @@ func (s *Suite) TestJournalReversion() { s.FailNowf("statedb dirty count mismatch", "expected 1 dirty journal change, but instead got: %d", stateDB.DebugDirtiesCount()) } - s.T().Log("Expect to see the pending changes included") + s.T().Log("Expect to see the pending changes included in the EVM context") + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5, + ) + s.T().Log("Expect to see the pending changes not included in cosmos ctx") test.AssertWasmCounterState( - &s.Suite, deps, evmObj, helloWorldCounterWasm, 12, // 12 = 7 + 5 + &s.Suite, deps, helloWorldCounterWasm, 7, ) // NOTE: that the [StateDB.Commit] fn has not been called yet. We're still // mid-transaction. s.T().Log("EVM revert operation should bring about the old state") - err := test.IncrementWasmCounterWithExecuteMultiViaVMCall( - &s.Suite, &deps, helloWorldCounterWasm, 50, false, evmObj, + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, helloWorldCounterWasm, 50, false, ) - s.Require().NoError(err) s.T().Log(heredoc.Doc(`At this point, 2 precompile calls have succeeded. One that increments the counter to 7 + 5, and another for +50. The StateDB has not been committed. We expect to be able to revert to both snapshots and see the prior states.`)) - test.AssertWasmCounterState( + test.AssertWasmCounterStateWithEvm( &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5+50, ) @@ -221,21 +224,20 @@ snapshots and see the prior states.`)) }) s.Require().ErrorContains(errFn(), "revision id 9000 cannot be reverted") - stateDB.RevertToSnapshot(5) - test.AssertWasmCounterState( + stateDB.RevertToSnapshot(2) + test.AssertWasmCounterStateWithEvm( &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5, ) - stateDB.RevertToSnapshot(3) - test.AssertWasmCounterState( - &s.Suite, deps, evmObj, helloWorldCounterWasm, 7, // state before precompile called + stateDB.RevertToSnapshot(0) + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7, ) - err = stateDB.Commit() - s.Require().NoError(err) + s.Require().NoError(stateDB.Commit()) s.Require().EqualValues(0, stateDB.DebugDirtiesCount()) test.AssertWasmCounterState( - &s.Suite, deps, evmObj, helloWorldCounterWasm, 7, // state before precompile called + &s.Suite, deps, helloWorldCounterWasm, 7, ) } diff --git a/x/inflation/keeper/inflation_test.go b/x/inflation/keeper/inflation_test.go index 00b8b9870..6831f656f 100644 --- a/x/inflation/keeper/inflation_test.go +++ b/x/inflation/keeper/inflation_test.go @@ -137,7 +137,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { }{ { "no epochs per period", - sdk.TokensFromConsensusPower(400_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(400_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { nibiruApp.InflationKeeper.Params.Set(ctx, types.Params{ EpochsPerPeriod: 0, @@ -150,7 +150,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { }, { "high supply", - sdk.TokensFromConsensusPower(800_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(800_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { params := nibiruApp.InflationKeeper.GetParams(ctx) params.InflationEnabled = true @@ -160,7 +160,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { }, { "low supply", - sdk.TokensFromConsensusPower(400_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(400_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { params := nibiruApp.InflationKeeper.GetParams(ctx) params.InflationEnabled = true @@ -184,7 +184,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { require.NoError(t, err) circulatingSupply := nibiruApp.InflationKeeper.GetCirculatingSupply(ctx, denoms.NIBI) - require.EqualValues(t, tc.supply, circulatingSupply) + require.EqualValues(t, tc.supply.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), circulatingSupply) inflationRate := nibiruApp.InflationKeeper.GetInflationRate(ctx, denoms.NIBI) require.Equal(t, tc.expInflationRate, inflationRate) diff --git a/x/inflation/keeper/keeper_test.go b/x/inflation/keeper/keeper_test.go index 46f1619fc..b3f24b2ee 100644 --- a/x/inflation/keeper/keeper_test.go +++ b/x/inflation/keeper/keeper_test.go @@ -55,17 +55,17 @@ func TestBurn(t *testing.T) { ) supply := nibiruApp.BankKeeper.GetSupply(ctx, "unibi") - require.Equal(t, tc.mintCoin.Amount, supply.Amount) + require.Equal(t, tc.mintCoin.Amount.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), supply.Amount) // Burn coins err := nibiruApp.InflationKeeper.Burn(ctx, sdk.NewCoins(tc.burnCoin), tc.sender) supply = nibiruApp.BankKeeper.GetSupply(ctx, "unibi") if tc.expectedErr != nil { require.EqualError(t, err, tc.expectedErr.Error()) - require.Equal(t, tc.mintCoin.Amount, supply.Amount) + require.Equal(t, tc.mintCoin.Amount.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), supply.Amount) } else { require.NoError(t, err) - require.Equal(t, math.ZeroInt(), supply.Amount) + require.Equal(t, sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction), supply.Amount) } }) } diff --git a/x/tokenfactory/keeper/msg_server_test.go b/x/tokenfactory/keeper/msg_server_test.go index 0d7ce4f04..a403a1618 100644 --- a/x/tokenfactory/keeper/msg_server_test.go +++ b/x/tokenfactory/keeper/msg_server_test.go @@ -662,7 +662,7 @@ func (s *TestSuite) TestBurnNative() { }, PostHook: func(ctx sdk.Context, bapp *app.NibiruApp) { s.Equal( - math.NewInt(0), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, + sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, ) s.Equal( @@ -696,7 +696,7 @@ func (s *TestSuite) TestBurnNative() { }, PostHook: func(ctx sdk.Context, bapp *app.NibiruApp) { s.Equal( - math.NewInt(123), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, + math.NewInt(123).Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, ) s.Equal(