From f1f216e15aa6bcc05075a7079496764820a0f545 Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Mon, 26 Aug 2024 12:50:17 -0400 Subject: [PATCH] fix: allow for no bank balances in all simulations (#137) * init * recreate and fix bug * helper * ante tests * ok * fix via gas consume call during simulation * test * revert * lint fix * retract more * replace linter * set lint to what we use --- .github/workflows/lint.yml | 2 +- .golangci.yml | 2 +- go.mod | 4 +- x/feemarket/ante/expected_keepers.go | 7 +- x/feemarket/ante/fee.go | 24 +- x/feemarket/ante/fee_test.go | 234 +++- x/feemarket/ante/mocks/mock_bank_keeper.go | 1135 +++++++++++++++++++- x/feemarket/ante/suite/suite.go | 43 +- x/feemarket/post/fee.go | 32 +- x/feemarket/post/fee_test.go | 502 ++++++++- x/feemarket/types/state.go | 2 +- 11 files changed, 1916 insertions(+), 71 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ed6676d..a31ebd3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,7 +24,7 @@ jobs: uses: golangci/golangci-lint-action@v3 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: latest + version: v1.59.1 only-new-issues: true lint-markdown: name: Lint markdown diff --git a/.golangci.yml b/.golangci.yml index 54547c3..f00a33c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,7 @@ linters: disable-all: true enable: - dogsled - - exportloopref + - copyloopvar - goconst - gocritic - gofumpt diff --git a/go.mod b/go.mod index 6c1f199..e0ef366 100644 --- a/go.mod +++ b/go.mod @@ -352,5 +352,5 @@ replace ( github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 ) -// simulation and fee UX -retract [v1.0.0, v1.0.2] +// simulation and fee UX, blackberry fix +retract [v1.0.0, v1.0.4] diff --git a/x/feemarket/ante/expected_keepers.go b/x/feemarket/ante/expected_keepers.go index 0f08ede..f0c1ba5 100644 --- a/x/feemarket/ante/expected_keepers.go +++ b/x/feemarket/ante/expected_keepers.go @@ -6,6 +6,7 @@ import ( "cosmossdk.io/core/address" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" ) @@ -35,11 +36,7 @@ type FeeGrantKeeper interface { // //go:generate mockery --name BankKeeper --filename mock_bank_keeper.go type BankKeeper interface { - IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error - SendCoins(ctx context.Context, from, to sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error - SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + bankkeeper.Keeper } // FeeMarketKeeper defines the expected feemarket keeper. diff --git a/x/feemarket/ante/fee.go b/x/feemarket/ante/fee.go index fc3ec60..6d7cc77 100644 --- a/x/feemarket/ante/fee.go +++ b/x/feemarket/ante/fee.go @@ -109,18 +109,17 @@ func (dfd feeMarketCheckDecorator) anteHandle(ctx sdk.Context, tx sdk.Tx, simula return ctx, errorsmod.Wrapf(feemarkettypes.ErrTooManyFeeCoins, "got length %d", len(feeCoins)) } - var feeCoin sdk.Coin - if simulate && len(feeCoins) == 0 { - // if simulating and user did not provider a fee - create a dummy value for them - feeCoin = sdk.NewCoin(params.FeeDenom, sdkmath.OneInt()) - } else { - feeCoin = feeCoins[0] + // if simulating - create a dummy zero value for the user + payCoin := sdk.NewCoin(params.FeeDenom, sdkmath.ZeroInt()) + if !simulate { + payCoin = feeCoins[0] } + feeGas := int64(feeTx.GetGas()) - minGasPrice, err := dfd.feemarketKeeper.GetMinGasPrice(ctx, feeCoin.GetDenom()) + minGasPrice, err := dfd.feemarketKeeper.GetMinGasPrice(ctx, payCoin.GetDenom()) if err != nil { - return ctx, errorsmod.Wrapf(err, "unable to get min gas price for denom %s", feeCoin.GetDenom()) + return ctx, errorsmod.Wrapf(err, "unable to get min gas price for denom %s", payCoin.GetDenom()) } ctx.Logger().Info("fee deduct ante handle", @@ -132,19 +131,19 @@ func (dfd feeMarketCheckDecorator) anteHandle(ctx sdk.Context, tx sdk.Tx, simula ctx = ctx.WithMinGasPrices(sdk.NewDecCoins(minGasPrice)) if !simulate { - _, _, err := CheckTxFee(ctx, minGasPrice, feeCoin, feeGas, true) + _, _, err := CheckTxFee(ctx, minGasPrice, payCoin, feeGas, true) if err != nil { return ctx, errorsmod.Wrapf(err, "error checking fee") } } // escrow the entire amount that the account provided as fee (feeCoin) - err = dfd.EscrowFunds(ctx, tx, feeCoin) + err = dfd.EscrowFunds(ctx, tx, payCoin) if err != nil { return ctx, errorsmod.Wrapf(err, "error escrowing funds") } - priorityFee, err := dfd.resolveTxPriorityCoins(ctx, feeCoin, params.FeeDenom) + priorityFee, err := dfd.resolveTxPriorityCoins(ctx, payCoin, params.FeeDenom) if err != nil { return ctx, errorsmod.Wrapf(err, "error resolving fee priority") } @@ -194,7 +193,8 @@ func (dfd feeMarketCheckDecorator) EscrowFunds(ctx sdk.Context, sdkTx sdk.Tx, pr return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") } else if !bytes.Equal(feeGranter, feePayer) { if !providedFee.IsNil() { - err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(providedFee), sdkTx.GetMsgs()) + err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(providedFee), + sdkTx.GetMsgs()) if err != nil { return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) } diff --git a/x/feemarket/ante/fee_test.go b/x/feemarket/ante/fee_test.go index 8955492..b90971a 100644 --- a/x/feemarket/ante/fee_test.go +++ b/x/feemarket/ante/fee_test.go @@ -16,7 +16,7 @@ import ( "github.com/skip-mev/feemarket/x/feemarket/types" ) -func TestAnteHandle(t *testing.T) { +func TestAnteHandleMock(t *testing.T) { // Same data for every test case gasLimit := antesuite.NewTestGasLimit() @@ -41,6 +41,7 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrOutOfGas, + Mock: true, }, // test --gas=auto flag settings // when --gas=auto is set, cosmos-sdk sets gas=0 and simulate=true @@ -60,6 +61,7 @@ func TestAnteHandle(t *testing.T) { RunPost: false, Simulate: true, ExpPass: true, + Mock: true, }, { Name: "0 gas given should fail with resolvable denom", @@ -77,6 +79,7 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrOutOfGas, + Mock: true, }, { Name: "0 gas given should pass in simulate - no fee", @@ -95,6 +98,7 @@ func TestAnteHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, + Mock: true, }, { Name: "0 gas given should pass in simulate - fee", @@ -113,6 +117,7 @@ func TestAnteHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, + Mock: true, }, { Name: "signer has enough funds, should pass", @@ -131,6 +136,7 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: true, ExpErr: nil, + Mock: true, }, { Name: "signer has enough funds in resolvable denom, should pass", @@ -149,6 +155,228 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: true, ExpErr: nil, + Mock: true, + }, + { + Name: "no fee - fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 1000000000, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: types.ErrNoFeeCoins, + Mock: true, + }, + { + Name: "no gas limit - fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrOutOfGas, + Mock: true, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.Name), func(t *testing.T) { + s := antesuite.SetupTestSuite(t, tc.Mock) + s.TxBuilder = s.ClientCtx.TxConfig.NewTxBuilder() + args := tc.Malleate(s) + + s.RunTestCase(t, tc, args) + }) + } +} + +func TestAnteHandle(t *testing.T) { + // Same data for every test case + gasLimit := antesuite.NewTestGasLimit() + + validFeeAmount := types.DefaultMinBaseGasPrice.MulInt64(int64(gasLimit)) + validFee := sdk.NewCoins(sdk.NewCoin("stake", validFeeAmount.TruncateInt())) + validFeeDifferentDenom := sdk.NewCoins(sdk.NewCoin("atom", math.Int(validFeeAmount))) + + testCases := []antesuite.TestCase{ + { + Name: "0 gas given should fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrOutOfGas, + Mock: false, + }, + // test --gas=auto flag settings + // when --gas=auto is set, cosmos-sdk sets gas=0 and simulate=true + { + Name: "--gas=auto behaviour test - no balance", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: true, + ExpPass: true, + Mock: false, + }, + { + Name: "0 gas given should fail with resolvable denom", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFeeDifferentDenom, + } + }, + RunAnte: true, + RunPost: false, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrOutOfGas, + Mock: false, + }, + { + Name: "0 gas given should pass in simulate - no fee", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: false, + Simulate: true, + ExpPass: true, + ExpErr: nil, + Mock: false, + }, + { + Name: "0 gas given should pass in simulate - fee", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: true, + ExpPass: true, + ExpErr: nil, + Mock: false, + }, + { + Name: "signer has enough funds, should pass", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: false, + ExpPass: true, + ExpErr: nil, + Mock: false, + }, + { + Name: "signer has insufficient funds, should fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + // no balance + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: false, + }, + { + Name: "signer has enough funds in resolvable denom, should pass", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validFeeDifferentDenom, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFeeDifferentDenom, + } + }, + RunAnte: true, + RunPost: false, + Simulate: false, + ExpPass: true, + ExpErr: nil, + Mock: false, }, { Name: "no fee - fail", @@ -166,6 +394,7 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: types.ErrNoFeeCoins, + Mock: false, }, { Name: "no gas limit - fail", @@ -183,12 +412,13 @@ func TestAnteHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrOutOfGas, + Mock: false, }, } for _, tc := range testCases { t.Run(fmt.Sprintf("Case %s", tc.Name), func(t *testing.T) { - s := antesuite.SetupTestSuite(t, true) + s := antesuite.SetupTestSuite(t, tc.Mock) s.TxBuilder = s.ClientCtx.TxConfig.NewTxBuilder() args := tc.Malleate(s) diff --git a/x/feemarket/ante/mocks/mock_bank_keeper.go b/x/feemarket/ante/mocks/mock_bank_keeper.go index 8121443..3251f31 100644 --- a/x/feemarket/ante/mocks/mock_bank_keeper.go +++ b/x/feemarket/ante/mocks/mock_bank_keeper.go @@ -5,17 +5,709 @@ package mocks import ( context "context" - types "github.com/cosmos/cosmos-sdk/types" + cosmos_sdktypes "github.com/cosmos/cosmos-sdk/types" + keeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + mock "github.com/stretchr/testify/mock" + + query "github.com/cosmos/cosmos-sdk/types/query" + + types "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// BankKeeper is an autogenerated mock type for the BankKeeper type -type BankKeeper struct { - mock.Mock +// BankKeeper is an autogenerated mock type for the BankKeeper type +type BankKeeper struct { + mock.Mock +} + +// AllBalances provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) AllBalances(_a0 context.Context, _a1 *types.QueryAllBalancesRequest) (*types.QueryAllBalancesResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for AllBalances") + } + + var r0 *types.QueryAllBalancesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllBalancesRequest) (*types.QueryAllBalancesResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllBalancesRequest) *types.QueryAllBalancesResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryAllBalancesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryAllBalancesRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AppendSendRestriction provides a mock function with given fields: restriction +func (_m *BankKeeper) AppendSendRestriction(restriction types.SendRestrictionFn) { + _m.Called(restriction) +} + +// Balance provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) Balance(_a0 context.Context, _a1 *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for Balance") + } + + var r0 *types.QueryBalanceResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryBalanceRequest) *types.QueryBalanceResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryBalanceResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryBalanceRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockedAddr provides a mock function with given fields: addr +func (_m *BankKeeper) BlockedAddr(addr cosmos_sdktypes.AccAddress) bool { + ret := _m.Called(addr) + + if len(ret) == 0 { + panic("no return value specified for BlockedAddr") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(cosmos_sdktypes.AccAddress) bool); ok { + r0 = rf(addr) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// BurnCoins provides a mock function with given fields: ctx, moduleName, amt +func (_m *BankKeeper) BurnCoins(ctx context.Context, moduleName string, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, moduleName, amt) + + if len(ret) == 0 { + panic("no return value specified for BurnCoins") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, moduleName, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ClearSendRestriction provides a mock function with given fields: +func (_m *BankKeeper) ClearSendRestriction() { + _m.Called() +} + +// DelegateCoins provides a mock function with given fields: ctx, delegatorAddr, moduleAccAddr, amt +func (_m *BankKeeper) DelegateCoins(ctx context.Context, delegatorAddr cosmos_sdktypes.AccAddress, moduleAccAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, delegatorAddr, moduleAccAddr, amt) + + if len(ret) == 0 { + panic("no return value specified for DelegateCoins") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, delegatorAddr, moduleAccAddr, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DelegateCoinsFromAccountToModule provides a mock function with given fields: ctx, senderAddr, recipientModule, amt +func (_m *BankKeeper) DelegateCoinsFromAccountToModule(ctx context.Context, senderAddr cosmos_sdktypes.AccAddress, recipientModule string, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, senderAddr, recipientModule, amt) + + if len(ret) == 0 { + panic("no return value specified for DelegateCoinsFromAccountToModule") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, senderAddr, recipientModule, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteSendEnabled provides a mock function with given fields: ctx, denoms +func (_m *BankKeeper) DeleteSendEnabled(ctx context.Context, denoms ...string) { + _va := make([]interface{}, len(denoms)) + for _i := range denoms { + _va[_i] = denoms[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx) + _ca = append(_ca, _va...) + _m.Called(_ca...) +} + +// DenomMetadata provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) DenomMetadata(_a0 context.Context, _a1 *types.QueryDenomMetadataRequest) (*types.QueryDenomMetadataResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for DenomMetadata") + } + + var r0 *types.QueryDenomMetadataResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataRequest) (*types.QueryDenomMetadataResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataRequest) *types.QueryDenomMetadataResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryDenomMetadataResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryDenomMetadataRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DenomMetadataByQueryString provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) DenomMetadataByQueryString(_a0 context.Context, _a1 *types.QueryDenomMetadataByQueryStringRequest) (*types.QueryDenomMetadataByQueryStringResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for DenomMetadataByQueryString") + } + + var r0 *types.QueryDenomMetadataByQueryStringResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataByQueryStringRequest) (*types.QueryDenomMetadataByQueryStringResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataByQueryStringRequest) *types.QueryDenomMetadataByQueryStringResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryDenomMetadataByQueryStringResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryDenomMetadataByQueryStringRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DenomOwners provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) DenomOwners(_a0 context.Context, _a1 *types.QueryDenomOwnersRequest) (*types.QueryDenomOwnersResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for DenomOwners") + } + + var r0 *types.QueryDenomOwnersResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersRequest) (*types.QueryDenomOwnersResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersRequest) *types.QueryDenomOwnersResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryDenomOwnersResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryDenomOwnersRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DenomOwnersByQuery provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) DenomOwnersByQuery(_a0 context.Context, _a1 *types.QueryDenomOwnersByQueryRequest) (*types.QueryDenomOwnersByQueryResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for DenomOwnersByQuery") + } + + var r0 *types.QueryDenomOwnersByQueryResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersByQueryRequest) (*types.QueryDenomOwnersByQueryResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersByQueryRequest) *types.QueryDenomOwnersByQueryResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryDenomOwnersByQueryResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryDenomOwnersByQueryRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DenomsMetadata provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) DenomsMetadata(_a0 context.Context, _a1 *types.QueryDenomsMetadataRequest) (*types.QueryDenomsMetadataResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for DenomsMetadata") + } + + var r0 *types.QueryDenomsMetadataResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomsMetadataRequest) (*types.QueryDenomsMetadataResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomsMetadataRequest) *types.QueryDenomsMetadataResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryDenomsMetadataResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryDenomsMetadataRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ExportGenesis provides a mock function with given fields: _a0 +func (_m *BankKeeper) ExportGenesis(_a0 context.Context) *types.GenesisState { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for ExportGenesis") + } + + var r0 *types.GenesisState + if rf, ok := ret.Get(0).(func(context.Context) *types.GenesisState); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.GenesisState) + } + } + + return r0 +} + +// GetAccountsBalances provides a mock function with given fields: ctx +func (_m *BankKeeper) GetAccountsBalances(ctx context.Context) []types.Balance { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAccountsBalances") + } + + var r0 []types.Balance + if rf, ok := ret.Get(0).(func(context.Context) []types.Balance); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Balance) + } + } + + return r0 +} + +// GetAllBalances provides a mock function with given fields: ctx, addr +func (_m *BankKeeper) GetAllBalances(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for GetAllBalances") + } + + var r0 cosmos_sdktypes.Coins + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { + r0 = rf(ctx, addr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cosmos_sdktypes.Coins) + } + } + + return r0 +} + +// GetAllDenomMetaData provides a mock function with given fields: ctx +func (_m *BankKeeper) GetAllDenomMetaData(ctx context.Context) []types.Metadata { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAllDenomMetaData") + } + + var r0 []types.Metadata + if rf, ok := ret.Get(0).(func(context.Context) []types.Metadata); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Metadata) + } + } + + return r0 +} + +// GetAllSendEnabledEntries provides a mock function with given fields: ctx +func (_m *BankKeeper) GetAllSendEnabledEntries(ctx context.Context) []types.SendEnabled { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetAllSendEnabledEntries") + } + + var r0 []types.SendEnabled + if rf, ok := ret.Get(0).(func(context.Context) []types.SendEnabled); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.SendEnabled) + } + } + + return r0 +} + +// GetAuthority provides a mock function with given fields: +func (_m *BankKeeper) GetAuthority() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetAuthority") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetBalance provides a mock function with given fields: ctx, addr, denom +func (_m *BankKeeper) GetBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress, denom string) cosmos_sdktypes.Coin { + ret := _m.Called(ctx, addr, denom) + + if len(ret) == 0 { + panic("no return value specified for GetBalance") + } + + var r0 cosmos_sdktypes.Coin + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string) cosmos_sdktypes.Coin); ok { + r0 = rf(ctx, addr, denom) + } else { + r0 = ret.Get(0).(cosmos_sdktypes.Coin) + } + + return r0 +} + +// GetBlockedAddresses provides a mock function with given fields: +func (_m *BankKeeper) GetBlockedAddresses() map[string]bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetBlockedAddresses") + } + + var r0 map[string]bool + if rf, ok := ret.Get(0).(func() map[string]bool); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]bool) + } + } + + return r0 +} + +// GetDenomMetaData provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) GetDenomMetaData(ctx context.Context, denom string) (types.Metadata, bool) { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for GetDenomMetaData") + } + + var r0 types.Metadata + var r1 bool + if rf, ok := ret.Get(0).(func(context.Context, string) (types.Metadata, bool)); ok { + return rf(ctx, denom) + } + if rf, ok := ret.Get(0).(func(context.Context, string) types.Metadata); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(types.Metadata) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) bool); ok { + r1 = rf(ctx, denom) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// GetPaginatedTotalSupply provides a mock function with given fields: ctx, pagination +func (_m *BankKeeper) GetPaginatedTotalSupply(ctx context.Context, pagination *query.PageRequest) (cosmos_sdktypes.Coins, *query.PageResponse, error) { + ret := _m.Called(ctx, pagination) + + if len(ret) == 0 { + panic("no return value specified for GetPaginatedTotalSupply") + } + + var r0 cosmos_sdktypes.Coins + var r1 *query.PageResponse + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *query.PageRequest) (cosmos_sdktypes.Coins, *query.PageResponse, error)); ok { + return rf(ctx, pagination) + } + if rf, ok := ret.Get(0).(func(context.Context, *query.PageRequest) cosmos_sdktypes.Coins); ok { + r0 = rf(ctx, pagination) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cosmos_sdktypes.Coins) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *query.PageRequest) *query.PageResponse); ok { + r1 = rf(ctx, pagination) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*query.PageResponse) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, *query.PageRequest) error); ok { + r2 = rf(ctx, pagination) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetParams provides a mock function with given fields: ctx +func (_m *BankKeeper) GetParams(ctx context.Context) types.Params { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetParams") + } + + var r0 types.Params + if rf, ok := ret.Get(0).(func(context.Context) types.Params); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(types.Params) + } + + return r0 +} + +// GetSendEnabledEntry provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) GetSendEnabledEntry(ctx context.Context, denom string) (types.SendEnabled, bool) { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for GetSendEnabledEntry") + } + + var r0 types.SendEnabled + var r1 bool + if rf, ok := ret.Get(0).(func(context.Context, string) (types.SendEnabled, bool)); ok { + return rf(ctx, denom) + } + if rf, ok := ret.Get(0).(func(context.Context, string) types.SendEnabled); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(types.SendEnabled) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) bool); ok { + r1 = rf(ctx, denom) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// GetSupply provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) GetSupply(ctx context.Context, denom string) cosmos_sdktypes.Coin { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for GetSupply") + } + + var r0 cosmos_sdktypes.Coin + if rf, ok := ret.Get(0).(func(context.Context, string) cosmos_sdktypes.Coin); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(cosmos_sdktypes.Coin) + } + + return r0 +} + +// HasBalance provides a mock function with given fields: ctx, addr, amt +func (_m *BankKeeper) HasBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coin) bool { + ret := _m.Called(ctx, addr, amt) + + if len(ret) == 0 { + panic("no return value specified for HasBalance") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coin) bool); ok { + r0 = rf(ctx, addr, amt) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// HasDenomMetaData provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) HasDenomMetaData(ctx context.Context, denom string) bool { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for HasDenomMetaData") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// HasSupply provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) HasSupply(ctx context.Context, denom string) bool { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for HasSupply") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// InitGenesis provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) InitGenesis(_a0 context.Context, _a1 *types.GenesisState) { + _m.Called(_a0, _a1) +} + +// InputOutputCoins provides a mock function with given fields: ctx, input, outputs +func (_m *BankKeeper) InputOutputCoins(ctx context.Context, input types.Input, outputs []types.Output) error { + ret := _m.Called(ctx, input, outputs) + + if len(ret) == 0 { + panic("no return value specified for InputOutputCoins") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, types.Input, []types.Output) error); ok { + r0 = rf(ctx, input, outputs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// IsSendEnabledCoin provides a mock function with given fields: ctx, coin +func (_m *BankKeeper) IsSendEnabledCoin(ctx context.Context, coin cosmos_sdktypes.Coin) bool { + ret := _m.Called(ctx, coin) + + if len(ret) == 0 { + panic("no return value specified for IsSendEnabledCoin") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.Coin) bool); ok { + r0 = rf(ctx, coin) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 } // IsSendEnabledCoins provides a mock function with given fields: ctx, coins -func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...types.Coin) error { +func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...cosmos_sdktypes.Coin) error { _va := make([]interface{}, len(coins)) for _i := range coins { _va[_i] = coins[_i] @@ -30,7 +722,7 @@ func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...types.Coi } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, ...types.Coin) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, ...cosmos_sdktypes.Coin) error); ok { r0 = rf(ctx, coins...) } else { r0 = ret.Error(0) @@ -39,17 +731,133 @@ func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...types.Coi return r0 } -// SendCoins provides a mock function with given fields: ctx, from, to, amt -func (_m *BankKeeper) SendCoins(ctx context.Context, from types.AccAddress, to types.AccAddress, amt types.Coins) error { - ret := _m.Called(ctx, from, to, amt) +// IsSendEnabledDenom provides a mock function with given fields: ctx, denom +func (_m *BankKeeper) IsSendEnabledDenom(ctx context.Context, denom string) bool { + ret := _m.Called(ctx, denom) + + if len(ret) == 0 { + panic("no return value specified for IsSendEnabledDenom") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(ctx, denom) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// IterateAccountBalances provides a mock function with given fields: ctx, addr, cb +func (_m *BankKeeper) IterateAccountBalances(ctx context.Context, addr cosmos_sdktypes.AccAddress, cb func(cosmos_sdktypes.Coin) bool) { + _m.Called(ctx, addr, cb) +} + +// IterateAllBalances provides a mock function with given fields: ctx, cb +func (_m *BankKeeper) IterateAllBalances(ctx context.Context, cb func(cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coin) bool) { + _m.Called(ctx, cb) +} + +// IterateAllDenomMetaData provides a mock function with given fields: ctx, cb +func (_m *BankKeeper) IterateAllDenomMetaData(ctx context.Context, cb func(types.Metadata) bool) { + _m.Called(ctx, cb) +} + +// IterateSendEnabledEntries provides a mock function with given fields: ctx, cb +func (_m *BankKeeper) IterateSendEnabledEntries(ctx context.Context, cb func(string, bool) bool) { + _m.Called(ctx, cb) +} + +// IterateTotalSupply provides a mock function with given fields: ctx, cb +func (_m *BankKeeper) IterateTotalSupply(ctx context.Context, cb func(cosmos_sdktypes.Coin) bool) { + _m.Called(ctx, cb) +} + +// LockedCoins provides a mock function with given fields: ctx, addr +func (_m *BankKeeper) LockedCoins(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for LockedCoins") + } + + var r0 cosmos_sdktypes.Coins + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { + r0 = rf(ctx, addr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cosmos_sdktypes.Coins) + } + } + + return r0 +} + +// MintCoins provides a mock function with given fields: ctx, moduleName, amt +func (_m *BankKeeper) MintCoins(ctx context.Context, moduleName string, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, moduleName, amt) + + if len(ret) == 0 { + panic("no return value specified for MintCoins") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, moduleName, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Params provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) Params(_a0 context.Context, _a1 *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for Params") + } + + var r0 *types.QueryParamsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryParamsRequest) (*types.QueryParamsResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryParamsRequest) *types.QueryParamsResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryParamsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryParamsRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PrependSendRestriction provides a mock function with given fields: restriction +func (_m *BankKeeper) PrependSendRestriction(restriction types.SendRestrictionFn) { + _m.Called(restriction) +} + +// SendCoins provides a mock function with given fields: ctx, fromAddr, toAddr, amt +func (_m *BankKeeper) SendCoins(ctx context.Context, fromAddr cosmos_sdktypes.AccAddress, toAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, fromAddr, toAddr, amt) if len(ret) == 0 { panic("no return value specified for SendCoins") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, types.AccAddress, types.Coins) error); ok { - r0 = rf(ctx, from, to, amt) + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, fromAddr, toAddr, amt) } else { r0 = ret.Error(0) } @@ -58,7 +866,7 @@ func (_m *BankKeeper) SendCoins(ctx context.Context, from types.AccAddress, to t } // SendCoinsFromAccountToModule provides a mock function with given fields: ctx, senderAddr, recipientModule, amt -func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error { +func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr cosmos_sdktypes.AccAddress, recipientModule string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderAddr, recipientModule, amt) if len(ret) == 0 { @@ -66,7 +874,7 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAd } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, string, types.Coins) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderAddr, recipientModule, amt) } else { r0 = ret.Error(0) @@ -76,7 +884,7 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAd } // SendCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt -func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { +func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderModule, recipientAddr, amt) if len(ret) == 0 { @@ -84,7 +892,7 @@ func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderMo } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.AccAddress, types.Coins) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderModule, recipientAddr, amt) } else { r0 = ret.Error(0) @@ -94,7 +902,7 @@ func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderMo } // SendCoinsFromModuleToModule provides a mock function with given fields: ctx, senderModule, recipientModule, amt -func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt types.Coins) error { +func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderModule, recipientModule, amt) if len(ret) == 0 { @@ -102,7 +910,7 @@ func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderMod } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, types.Coins) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderModule, recipientModule, amt) } else { r0 = ret.Error(0) @@ -111,6 +919,299 @@ func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderMod return r0 } +// SendEnabled provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) SendEnabled(_a0 context.Context, _a1 *types.QuerySendEnabledRequest) (*types.QuerySendEnabledResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for SendEnabled") + } + + var r0 *types.QuerySendEnabledResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySendEnabledRequest) (*types.QuerySendEnabledResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySendEnabledRequest) *types.QuerySendEnabledResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QuerySendEnabledResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QuerySendEnabledRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SetAllSendEnabled provides a mock function with given fields: ctx, sendEnableds +func (_m *BankKeeper) SetAllSendEnabled(ctx context.Context, sendEnableds []*types.SendEnabled) { + _m.Called(ctx, sendEnableds) +} + +// SetDenomMetaData provides a mock function with given fields: ctx, denomMetaData +func (_m *BankKeeper) SetDenomMetaData(ctx context.Context, denomMetaData types.Metadata) { + _m.Called(ctx, denomMetaData) +} + +// SetParams provides a mock function with given fields: ctx, params +func (_m *BankKeeper) SetParams(ctx context.Context, params types.Params) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for SetParams") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, types.Params) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetSendEnabled provides a mock function with given fields: ctx, denom, value +func (_m *BankKeeper) SetSendEnabled(ctx context.Context, denom string, value bool) { + _m.Called(ctx, denom, value) +} + +// SpendableBalanceByDenom provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) SpendableBalanceByDenom(_a0 context.Context, _a1 *types.QuerySpendableBalanceByDenomRequest) (*types.QuerySpendableBalanceByDenomResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for SpendableBalanceByDenom") + } + + var r0 *types.QuerySpendableBalanceByDenomResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalanceByDenomRequest) (*types.QuerySpendableBalanceByDenomResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalanceByDenomRequest) *types.QuerySpendableBalanceByDenomResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QuerySpendableBalanceByDenomResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QuerySpendableBalanceByDenomRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SpendableBalances provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) SpendableBalances(_a0 context.Context, _a1 *types.QuerySpendableBalancesRequest) (*types.QuerySpendableBalancesResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for SpendableBalances") + } + + var r0 *types.QuerySpendableBalancesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalancesRequest) (*types.QuerySpendableBalancesResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalancesRequest) *types.QuerySpendableBalancesResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QuerySpendableBalancesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QuerySpendableBalancesRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SpendableCoin provides a mock function with given fields: ctx, addr, denom +func (_m *BankKeeper) SpendableCoin(ctx context.Context, addr cosmos_sdktypes.AccAddress, denom string) cosmos_sdktypes.Coin { + ret := _m.Called(ctx, addr, denom) + + if len(ret) == 0 { + panic("no return value specified for SpendableCoin") + } + + var r0 cosmos_sdktypes.Coin + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string) cosmos_sdktypes.Coin); ok { + r0 = rf(ctx, addr, denom) + } else { + r0 = ret.Get(0).(cosmos_sdktypes.Coin) + } + + return r0 +} + +// SpendableCoins provides a mock function with given fields: ctx, addr +func (_m *BankKeeper) SpendableCoins(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for SpendableCoins") + } + + var r0 cosmos_sdktypes.Coins + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { + r0 = rf(ctx, addr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cosmos_sdktypes.Coins) + } + } + + return r0 +} + +// SupplyOf provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) SupplyOf(_a0 context.Context, _a1 *types.QuerySupplyOfRequest) (*types.QuerySupplyOfResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for SupplyOf") + } + + var r0 *types.QuerySupplyOfResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySupplyOfRequest) (*types.QuerySupplyOfResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySupplyOfRequest) *types.QuerySupplyOfResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QuerySupplyOfResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QuerySupplyOfRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TotalSupply provides a mock function with given fields: _a0, _a1 +func (_m *BankKeeper) TotalSupply(_a0 context.Context, _a1 *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for TotalSupply") + } + + var r0 *types.QueryTotalSupplyResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.QueryTotalSupplyRequest) *types.QueryTotalSupplyResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.QueryTotalSupplyResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.QueryTotalSupplyRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UndelegateCoins provides a mock function with given fields: ctx, moduleAccAddr, delegatorAddr, amt +func (_m *BankKeeper) UndelegateCoins(ctx context.Context, moduleAccAddr cosmos_sdktypes.AccAddress, delegatorAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, moduleAccAddr, delegatorAddr, amt) + + if len(ret) == 0 { + panic("no return value specified for UndelegateCoins") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, moduleAccAddr, delegatorAddr, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UndelegateCoinsFromModuleToAccount provides a mock function with given fields: ctx, senderModule, recipientAddr, amt +func (_m *BankKeeper) UndelegateCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { + ret := _m.Called(ctx, senderModule, recipientAddr, amt) + + if len(ret) == 0 { + panic("no return value specified for UndelegateCoinsFromModuleToAccount") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { + r0 = rf(ctx, senderModule, recipientAddr, amt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ValidateBalance provides a mock function with given fields: ctx, addr +func (_m *BankKeeper) ValidateBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress) error { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for ValidateBalance") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) error); ok { + r0 = rf(ctx, addr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WithMintCoinsRestriction provides a mock function with given fields: _a0 +func (_m *BankKeeper) WithMintCoinsRestriction(_a0 types.MintingRestrictionFn) keeper.BaseKeeper { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for WithMintCoinsRestriction") + } + + var r0 keeper.BaseKeeper + if rf, ok := ret.Get(0).(func(types.MintingRestrictionFn) keeper.BaseKeeper); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(keeper.BaseKeeper) + } + + return r0 +} + // NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewBankKeeper(t interface { diff --git a/x/feemarket/ante/suite/suite.go b/x/feemarket/ante/suite/suite.go index ad0449d..5113539 100644 --- a/x/feemarket/ante/suite/suite.go +++ b/x/feemarket/ante/suite/suite.go @@ -3,7 +3,9 @@ package suite import ( "testing" - feemarketkeeper "github.com/skip-mev/feemarket/x/feemarket/keeper" + storetypes "cosmossdk.io/store/types" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" @@ -21,10 +23,13 @@ import ( authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + feemarketkeeper "github.com/skip-mev/feemarket/x/feemarket/keeper" + testkeeper "github.com/skip-mev/feemarket/testutils/keeper" feemarketante "github.com/skip-mev/feemarket/x/feemarket/ante" "github.com/skip-mev/feemarket/x/feemarket/ante/mocks" @@ -43,7 +48,7 @@ type TestSuite struct { AccountKeeper feemarketante.AccountKeeper FeeMarketKeeper *feemarketkeeper.Keeper - BankKeeper feemarketante.BankKeeper + BankKeeper bankkeeper.Keeper FeeGrantKeeper feemarketante.FeeGrantKeeper MockBankKeeper *mocks.BankKeeper @@ -59,7 +64,14 @@ type TestAccount struct { Priv cryptotypes.PrivKey } +type TestAccountBalance struct { + TestAccount + sdk.Coins +} + func (s *TestSuite) CreateTestAccounts(numAccs int) []TestAccount { + s.T().Helper() + var accounts []TestAccount for i := 0; i < numAccs; i++ { @@ -76,6 +88,23 @@ func (s *TestSuite) CreateTestAccounts(numAccs int) []TestAccount { return accounts } +func (s *TestSuite) SetAccountBalances(accounts []TestAccountBalance) { + s.T().Helper() + + oldState := s.BankKeeper.ExportGenesis(s.Ctx) + + balances := make([]banktypes.Balance, len(accounts)) + for i, acc := range accounts { + balances[i] = banktypes.Balance{ + Address: acc.Account.GetAddress().String(), + Coins: acc.Coins, + } + } + + oldState.Balances = balances + s.BankKeeper.InitGenesis(s.Ctx, oldState) +} + // SetupTestSuite setups a new test, with new app, context, and anteHandler. func SetupTestSuite(t *testing.T, mock bool) *TestSuite { s := &TestSuite{} @@ -86,6 +115,9 @@ func SetupTestSuite(t *testing.T, mock bool) *TestSuite { s.AccountKeeper = testKeepers.AccountKeeper s.FeeMarketKeeper = testKeepers.FeeMarketKeeper + s.BankKeeper = testKeepers.BankKeeper + s.FeeGrantKeeper = testKeepers.FeeGrantKeeper + s.MockBankKeeper = mocks.NewBankKeeper(t) s.MockFeeGrantKeeper = mocks.NewFeeGrantKeeper(t) @@ -97,6 +129,9 @@ func SetupTestSuite(t *testing.T, mock bool) *TestSuite { s.SetupHandlers(mock) s.SetT(t) + + s.BankKeeper.InitGenesis(s.Ctx, &banktypes.GenesisState{}) + return s } @@ -152,6 +187,7 @@ type TestCase struct { ExpPass bool ExpErr error ExpectConsumedGas uint64 + Mock bool } type TestCaseArgs struct { @@ -192,6 +228,9 @@ func (s *TestSuite) RunTestCase(t *testing.T, tc TestCase, args TestCaseArgs) { postErr error ) + // reset gas meter + s.Ctx = s.Ctx.WithGasMeter(storetypes.NewGasMeter(NewTestGasLimit())) + if tc.RunAnte { newCtx, anteErr = s.AnteHandler(s.Ctx, tx, tc.Simulate) } diff --git a/x/feemarket/post/fee.go b/x/feemarket/post/fee.go index 8022691..108af3e 100644 --- a/x/feemarket/post/fee.go +++ b/x/feemarket/post/fee.go @@ -15,6 +15,9 @@ import ( feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" ) +// BankSendGasConsumption is the gas consumption of the bank sends that occur during feemarket handler execution. +const BankSendGasConsumption = 12490 + // FeeMarketDeductDecorator deducts fees from the fee payer based off of the current state of the feemarket. // The fee payer is the fee granter (if specified) or first signer of the tx. // If the fee payer does not have the funds to pay for the fees, return an InsufficientFunds error. @@ -90,24 +93,20 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul return ctx, errorsmod.Wrapf(feemarkettypes.ErrTooManyFeeCoins, "got length %d", len(feeCoins)) } - var feeCoin sdk.Coin - if simulate && len(feeCoins) == 0 { - // if simulating and user did not provider a fee - create a dummy value for them - feeCoin = sdk.NewCoin(params.FeeDenom, math.OneInt()) - } else { - feeCoin = feeCoins[0] + // if simulating and user did not provider a fee - create a dummy value for them + var ( + tip = sdk.NewCoin(params.FeeDenom, math.ZeroInt()) + payCoin = sdk.NewCoin(params.FeeDenom, math.ZeroInt()) + ) + if !simulate { + payCoin = feeCoins[0] } feeGas := int64(feeTx.GetGas()) - var ( - tip = sdk.NewCoin(feeCoin.Denom, math.ZeroInt()) - payCoin = feeCoin - ) - - minGasPrice, err := dfd.feemarketKeeper.GetMinGasPrice(ctx, feeCoin.GetDenom()) + minGasPrice, err := dfd.feemarketKeeper.GetMinGasPrice(ctx, payCoin.GetDenom()) if err != nil { - return ctx, errorsmod.Wrapf(err, "unable to get min gas price for denom %s", feeCoins[0].GetDenom()) + return ctx, errorsmod.Wrapf(err, "unable to get min gas price for denom %s", payCoin.GetDenom()) } ctx.Logger().Info("fee deduct post handle", @@ -116,7 +115,7 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul ) if !simulate { - payCoin, tip, err = ante.CheckTxFee(ctx, minGasPrice, feeCoin, feeGas, false) + payCoin, tip, err = ante.CheckTxFee(ctx, minGasPrice, payCoin, feeGas, false) if err != nil { return ctx, err } @@ -141,6 +140,11 @@ func (dfd FeeMarketDeductDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simul return ctx, errorsmod.Wrapf(err, "unable to set fee market state") } + if simulate { + // consume the gas that would be consumed during normal execution + ctx.GasMeter().ConsumeGas(BankSendGasConsumption, "simulation send gas consumption") + } + return next(ctx, tx, simulate, success) } diff --git a/x/feemarket/post/fee_test.go b/x/feemarket/post/fee_test.go index af1cc8e..af67585 100644 --- a/x/feemarket/post/fee_test.go +++ b/x/feemarket/post/fee_test.go @@ -4,13 +4,12 @@ import ( "fmt" "testing" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/stretchr/testify/mock" antesuite "github.com/skip-mev/feemarket/x/feemarket/ante/suite" @@ -149,13 +148,14 @@ func TestSendTip(t *testing.T) { } } -func TestPostHandle(t *testing.T) { +func TestPostHandleMock(t *testing.T) { // Same data for every test case const ( - baseDenom = "stake" - resolvableDenom = "atom" - expectedConsumedGas = 35117 - gasLimit = expectedConsumedGas + baseDenom = "stake" + resolvableDenom = "atom" + expectedConsumedGas = 10631 + expectedConsumedSimGas = expectedConsumedGas + post.BankSendGasConsumption + gasLimit = expectedConsumedSimGas ) validFeeAmount := types.DefaultMinBaseGasPrice.MulInt64(int64(gasLimit)) @@ -184,6 +184,7 @@ func TestPostHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: true, }, { Name: "signer has no funds - simulate", @@ -203,6 +204,7 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: false, ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: true, }, { Name: "0 gas given should fail", @@ -220,6 +222,7 @@ func TestPostHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrOutOfGas, + Mock: true, }, { Name: "0 gas given should pass - simulate", @@ -240,7 +243,8 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: expectedConsumedGas, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, }, { Name: "signer has enough funds, should pass, no tip", @@ -262,6 +266,7 @@ func TestPostHandle(t *testing.T) { ExpPass: true, ExpErr: nil, ExpectConsumedGas: expectedConsumedGas, + Mock: true, }, { Name: "signer has enough funds, should pass with tip", @@ -283,6 +288,7 @@ func TestPostHandle(t *testing.T) { ExpPass: true, ExpErr: nil, ExpectConsumedGas: expectedConsumedGas, + Mock: true, }, { Name: "signer has enough funds, should pass with tip - simulate", @@ -303,7 +309,8 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: expectedConsumedGas, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, }, { Name: "fee market is enabled during the transaction - should pass and skip deduction until next block", @@ -346,7 +353,8 @@ func TestPostHandle(t *testing.T) { Simulate: false, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: 44976, // extra gas consumed because msg server is run + ExpectConsumedGas: 15340, // extra gas consumed because msg server is run, but deduction is skipped + Mock: true, }, { Name: "signer has enough funds, should pass, no tip - resolvable denom", @@ -369,6 +377,7 @@ func TestPostHandle(t *testing.T) { ExpPass: true, ExpErr: nil, ExpectConsumedGas: expectedConsumedGas, + Mock: true, }, { Name: "signer has enough funds, should pass, no tip - resolvable denom - simulate", @@ -389,7 +398,8 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: expectedConsumedGas, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, }, { Name: "signer has enough funds, should pass with tip - resolvable denom", @@ -411,6 +421,7 @@ func TestPostHandle(t *testing.T) { ExpPass: true, ExpErr: nil, ExpectConsumedGas: expectedConsumedGas, + Mock: true, }, { Name: "signer has enough funds, should pass with tip - resolvable denom - simulate", @@ -431,7 +442,8 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: expectedConsumedGas, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, }, { Name: "0 gas given should pass in simulate - no fee", @@ -450,7 +462,8 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, - ExpectConsumedGas: expectedConsumedGas, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, }, { Name: "0 gas given should pass in simulate - fee", @@ -469,8 +482,467 @@ func TestPostHandle(t *testing.T) { Simulate: true, ExpPass: true, ExpErr: nil, + ExpectConsumedGas: expectedConsumedSimGas, + Mock: true, + }, + { + Name: "no fee - fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 1000000000, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: types.ErrNoFeeCoins, + Mock: true, + }, + { + Name: "no gas limit - fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrOutOfGas, + Mock: true, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.Name), func(t *testing.T) { + s := antesuite.SetupTestSuite(t, tc.Mock) + s.TxBuilder = s.ClientCtx.TxConfig.NewTxBuilder() + args := tc.Malleate(s) + + s.RunTestCase(t, tc, args) + }) + } +} + +func TestPostHandle(t *testing.T) { + // Same data for every test case + const ( + baseDenom = "stake" + resolvableDenom = "atom" + expectedConsumedGas = 36650 + + expectedConsumedGasResolve = 36524 // slight difference due to denom resolver + + gasLimit = 100000 + ) + + validFeeAmount := types.DefaultMinBaseGasPrice.MulInt64(int64(gasLimit)) + validFeeAmountWithTip := validFeeAmount.Add(math.LegacyNewDec(100)) + validFee := sdk.NewCoins(sdk.NewCoin(baseDenom, validFeeAmount.TruncateInt())) + validFeeWithTip := sdk.NewCoins(sdk.NewCoin(baseDenom, validFeeAmountWithTip.TruncateInt())) + validResolvableFee := sdk.NewCoins(sdk.NewCoin(resolvableDenom, validFeeAmount.TruncateInt())) + validResolvableFeeWithTip := sdk.NewCoins(sdk.NewCoin(resolvableDenom, validFeeAmountWithTip.TruncateInt())) + + testCases := []antesuite.TestCase{ + { + Name: "signer has no funds", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: false, + }, + { + Name: "signer has no funds - simulate - pass", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + Mock: false, ExpectConsumedGas: expectedConsumedGas, }, + { + Name: "0 gas given should fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrOutOfGas, + Mock: false, + }, + { + Name: "0 gas given should pass - simulate", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "signer has enough funds, should pass, no tip", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: 36650, + Mock: false, + }, + { + Name: "signer has does not have enough funds for fee and tip - fail", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: false, + }, + { + Name: "signer has enough funds, should pass with tip", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validFeeWithTip, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: 36650, + Mock: false, + }, + { + Name: "signer has enough funds, should pass with tip - simulate", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "fee market is enabled during the transaction - should pass and skip deduction until next block", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validResolvableFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + // disable fee market before tx + s.Ctx = s.Ctx.WithBlockHeight(10) + disabledParams := types.DefaultParams() + disabledParams.Enabled = false + err := s.FeeMarketKeeper.SetParams(s.Ctx, disabledParams) + s.Require().NoError(err) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFee, + } + }, + StateUpdate: func(s *antesuite.TestSuite) { + // enable the fee market + enabledParams := types.DefaultParams() + req := &types.MsgParams{ + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Params: enabledParams, + } + + _, err := s.MsgServer.Params(s.Ctx, req) + s.Require().NoError(err) + + height, err := s.FeeMarketKeeper.GetEnabledHeight(s.Ctx) + s.Require().NoError(err) + s.Require().Equal(int64(10), height) + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: 15340, // extra gas consumed because msg server is run, but bank keepers are skipped + Mock: false, + }, + { + Name: "signer has enough funds, should pass, no tip - resolvable denom", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validResolvableFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGasResolve, + Mock: false, + }, + { + Name: "signer has enough funds, should pass, no tip - resolvable denom - simulate", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validResolvableFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "signer has no balance, should pass, no tip - resolvable denom - simulate", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFee, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "signer does not have enough funds, fail - resolvable denom", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validResolvableFee, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: false, + ExpErr: sdkerrors.ErrInsufficientFunds, + Mock: false, + }, + { + Name: "signer has enough funds, should pass with tip - resolvable denom", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + balance := antesuite.TestAccountBalance{ + TestAccount: accs[0], + Coins: validResolvableFeeWithTip, + } + s.SetAccountBalances([]antesuite.TestAccountBalance{balance}) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: false, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGasResolve, + Mock: false, + }, + { + Name: "signer has enough funds, should pass with tip - resolvable denom - simulate", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: gasLimit, + FeeAmount: validResolvableFeeWithTip, + } + }, + RunAnte: true, + RunPost: true, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "0 gas given should pass in simulate - no fee", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: nil, + } + }, + RunAnte: true, + RunPost: false, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, + { + Name: "0 gas given should pass in simulate - fee", + Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { + accs := s.CreateTestAccounts(1) + + return antesuite.TestCaseArgs{ + Msgs: []sdk.Msg{testdata.NewTestMsg(accs[0].Account.GetAddress())}, + GasLimit: 0, + FeeAmount: validFee, + } + }, + RunAnte: true, + RunPost: false, + Simulate: true, + ExpPass: true, + ExpErr: nil, + ExpectConsumedGas: expectedConsumedGas, + Mock: false, + }, { Name: "no fee - fail", Malleate: func(s *antesuite.TestSuite) antesuite.TestCaseArgs { @@ -487,6 +959,7 @@ func TestPostHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: types.ErrNoFeeCoins, + Mock: false, }, { Name: "no gas limit - fail", @@ -504,12 +977,13 @@ func TestPostHandle(t *testing.T) { Simulate: false, ExpPass: false, ExpErr: sdkerrors.ErrOutOfGas, + Mock: false, }, } for _, tc := range testCases { t.Run(fmt.Sprintf("Case %s", tc.Name), func(t *testing.T) { - s := antesuite.SetupTestSuite(t, true) + s := antesuite.SetupTestSuite(t, tc.Mock) s.TxBuilder = s.ClientCtx.TxConfig.NewTxBuilder() args := tc.Malleate(s) diff --git a/x/feemarket/types/state.go b/x/feemarket/types/state.go index d76ff20..bedd715 100644 --- a/x/feemarket/types/state.go +++ b/x/feemarket/types/state.go @@ -156,7 +156,7 @@ func (s *State) GetAverageUtilization(params Params) math.LegacyDec { // ValidateBasic performs basic validation on the state. func (s *State) ValidateBasic() error { - if s.Window == nil || len(s.Window) == 0 { + if s.Window == nil { return fmt.Errorf("block utilization window cannot be nil or empty") }