From 3a6ac58edb17cb44b3a8cc7360832ea0f9e4ae08 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Wed, 10 Mar 2021 12:08:53 +0100 Subject: [PATCH 1/2] Replace keeper types with interfaces --- x/wasm/internal/keeper/keeper.go | 13 ++-- x/wasm/internal/keeper/query_plugins.go | 18 ++--- x/wasm/internal/types/expected_keepers.go | 90 +++++++++++++++++++++++ x/wasm/internal/types/ibc.go | 44 ----------- 4 files changed, 103 insertions(+), 62 deletions(-) create mode 100644 x/wasm/internal/types/expected_keepers.go delete mode 100644 x/wasm/internal/types/ibc.go diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index ad37f1b816c..834256aa00d 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -15,10 +15,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/log" ) @@ -60,8 +57,8 @@ type messenger interface { type Keeper struct { storeKey sdk.StoreKey cdc codec.Marshaler - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper ChannelKeeper types.ChannelKeeper portKeeper types.PortKeeper capabilityKeeper types.CapabilityKeeper @@ -82,9 +79,9 @@ func NewKeeper( storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKeeper authkeeper.AccountKeeper, - bankKeeper bankkeeper.Keeper, - stakingKeeper stakingkeeper.Keeper, - distKeeper distributionkeeper.Keeper, + bankKeeper types.BankKeeper, + stakingKeeper types.StakingKeeper, + distKeeper types.DistributionKeeper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, capabilityKeeper types.CapabilityKeeper, diff --git a/x/wasm/internal/keeper/query_plugins.go b/x/wasm/internal/keeper/query_plugins.go index 2d006f8b549..181933553b9 100644 --- a/x/wasm/internal/keeper/query_plugins.go +++ b/x/wasm/internal/keeper/query_plugins.go @@ -4,14 +4,12 @@ import ( "encoding/json" "fmt" "github.com/CosmWasm/wasmd/x/wasm/internal/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -91,7 +89,7 @@ type QueryPlugins struct { Wasm func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) } -func DefaultQueryPlugins(bank bankkeeper.ViewKeeper, staking stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper, channelKeeper types.ChannelKeeper, queryRouter GRPCQueryRouter, wasm *Keeper) QueryPlugins { +func DefaultQueryPlugins(bank types.BankViewKeeper, staking types.StakingKeeper, distKeeper types.DistributionKeeper, channelKeeper types.ChannelKeeper, queryRouter GRPCQueryRouter, wasm *Keeper) QueryPlugins { return QueryPlugins{ Bank: BankQuerier(bank), Custom: NoCustomQuerier, @@ -128,7 +126,7 @@ func (e QueryPlugins) Merge(o *QueryPlugins) QueryPlugins { return e } -func BankQuerier(bankKeeper bankkeeper.ViewKeeper) func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) { +func BankQuerier(bankKeeper types.BankViewKeeper) func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) { return func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) { if request.AllBalances != nil { addr, err := sdk.AccAddressFromBech32(request.AllBalances.Address) @@ -176,7 +174,7 @@ func IBCQuerier(wasm *Keeper, channelKeeper types.ChannelKeeper) func(ctx sdk.Co if request.ListChannels != nil { portID := request.ListChannels.PortID var channels wasmvmtypes.IBCEndpoints - channelKeeper.IterateChannels(ctx, func(ch types.IdentifiedChannel) bool { + channelKeeper.IterateChannels(ctx, func(ch channeltypes.IdentifiedChannel) bool { if portID == "" || portID == ch.PortId { newChan := wasmvmtypes.IBCEndpoint{ PortID: ch.PortId, @@ -243,7 +241,7 @@ func StargateQuerier(queryRouter GRPCQueryRouter) func(ctx sdk.Context, request } } -func StakingQuerier(keeper stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { +func StakingQuerier(keeper types.StakingKeeper, distKeeper types.DistributionKeeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { return func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { if request.BondedDenom != nil { denom := keeper.BondDenom(ctx) @@ -308,7 +306,7 @@ func StakingQuerier(keeper stakingkeeper.Keeper, distKeeper distributionkeeper.K } } -func sdkToDelegations(ctx sdk.Context, keeper stakingkeeper.Keeper, delegations []stakingtypes.Delegation) (wasmvmtypes.Delegations, error) { +func sdkToDelegations(ctx sdk.Context, keeper types.StakingKeeper, delegations []stakingtypes.Delegation) (wasmvmtypes.Delegations, error) { result := make([]wasmvmtypes.Delegation, len(delegations)) bondDenom := keeper.BondDenom(ctx) @@ -339,7 +337,7 @@ func sdkToDelegations(ctx sdk.Context, keeper stakingkeeper.Keeper, delegations return result, nil } -func sdkToFullDelegation(ctx sdk.Context, keeper stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper, delegation stakingtypes.Delegation) (*wasmvmtypes.FullDelegation, error) { +func sdkToFullDelegation(ctx sdk.Context, keeper types.StakingKeeper, distKeeper types.DistributionKeeper, delegation stakingtypes.Delegation) (*wasmvmtypes.FullDelegation, error) { delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) if err != nil { return nil, sdkerrors.Wrap(err, "delegator address") @@ -387,7 +385,7 @@ func sdkToFullDelegation(ctx sdk.Context, keeper stakingkeeper.Keeper, distKeepe // FIXME: simplify this enormously when // https://github.com/cosmos/cosmos-sdk/issues/7466 is merged -func getAccumulatedRewards(ctx sdk.Context, distKeeper distributionkeeper.Keeper, delegation stakingtypes.Delegation) ([]wasmvmtypes.Coin, error) { +func getAccumulatedRewards(ctx sdk.Context, distKeeper types.DistributionKeeper, delegation stakingtypes.Delegation) ([]wasmvmtypes.Coin, error) { // Try to get *delegator* reward info! params := distributiontypes.QueryDelegationRewardsRequest{ DelegatorAddress: delegation.DelegatorAddress, diff --git a/x/wasm/internal/types/expected_keepers.go b/x/wasm/internal/types/expected_keepers.go new file mode 100644 index 00000000000..2638f74fcdb --- /dev/null +++ b/x/wasm/internal/types/expected_keepers.go @@ -0,0 +1,90 @@ +package types + +import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// BankKeeper defines a subset of methods implemented by the cosmos-sdk bank keeper +type BankViewKeeper interface { + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins +} + +// BankKeeper defines a subset of methods implemented by the cosmos-sdk bank keeper +type BankKeeper interface { + BankViewKeeper + SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + BlockedAddr(addr sdk.AccAddress) bool + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error +} + +// AccountKeeper defines a subset of methods implemented by the cosmos-sdk account keeper +type AccountKeeper interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + // Retrieve an account from the store. + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + // Set an account in the store. + SetAccount(ctx sdk.Context, acc authtypes.AccountI) +} + +// DistributionKeeper defines a subset of methods implemented by the cosmos-sdk distribution keeper +type DistributionKeeper interface { + DelegationRewards(c context.Context, req *types.QueryDelegationRewardsRequest) (*types.QueryDelegationRewardsResponse, error) +} + +// StakingKeeper defines a subset of methods implemented by the cosmos-sdk staking keeper +type StakingKeeper interface { + // BondDenom - Bondable coin denomination + BondDenom(ctx sdk.Context) (res string) + // GetValidator get a single validator + GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool) + // GetBondedValidatorsByPower get the current group of bonded validators sorted by power-rank + GetBondedValidatorsByPower(ctx sdk.Context) []stakingtypes.Validator + // GetAllDelegatorDelegations return all delegations for a delegator + GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) []stakingtypes.Delegation + // GetDelegation return a specific delegation + GetDelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valAddr sdk.ValAddress) (delegation stakingtypes.Delegation, found bool) + // HasReceivingRedelegation check if validator is receiving a redelegation + HasReceivingRedelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool +} + +// ChannelKeeper defines the expected IBC channel keeper +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error + GetAllChannels(ctx sdk.Context) (channels []channeltypes.IdentifiedChannel) + IterateChannels(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) +} + +// ClientKeeper defines the expected IBC client keeper +type ClientKeeper interface { + GetClientConsensusState(ctx sdk.Context, clientID string) (connection ibcexported.ConsensusState, found bool) +} + +// ConnectionKeeper defines the expected IBC connection keeper +type ConnectionKeeper interface { + GetConnection(ctx sdk.Context, connectionID string) (connection connectiontypes.ConnectionEnd, found bool) +} + +// PortKeeper defines the expected IBC port keeper +type PortKeeper interface { + BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability +} + +type CapabilityKeeper interface { + GetCapability(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) + ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error + AuthenticateCapability(ctx sdk.Context, capability *capabilitytypes.Capability, name string) bool +} diff --git a/x/wasm/internal/types/ibc.go b/x/wasm/internal/types/ibc.go deleted file mode 100644 index e4f9d664aae..00000000000 --- a/x/wasm/internal/types/ibc.go +++ /dev/null @@ -1,44 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" - ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" -) - -// Much copied from ibc-transfer in the cosmos-sdk - -// ChannelKeeper defines the expected IBC channel keeper -type ChannelKeeper interface { - GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) - GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) - SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error - ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error - GetAllChannels(ctx sdk.Context) (channels []channeltypes.IdentifiedChannel) - IterateChannels(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) -} - -type IdentifiedChannel = channeltypes.IdentifiedChannel - -// ClientKeeper defines the expected IBC client keeper -type ClientKeeper interface { - GetClientConsensusState(ctx sdk.Context, clientID string) (connection ibcexported.ConsensusState, found bool) -} - -// ConnectionKeeper defines the expected IBC connection keeper -type ConnectionKeeper interface { - GetConnection(ctx sdk.Context, connectionID string) (connection connectiontypes.ConnectionEnd, found bool) -} - -// PortKeeper defines the expected IBC port keeper -type PortKeeper interface { - BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability -} - -type CapabilityKeeper interface { - GetCapability(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) - ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error - AuthenticateCapability(ctx sdk.Context, capability *capabilitytypes.Capability, name string) bool -} From 5c52ab7b6e48c02e54b4e45ca8ba9977ec627406 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Wed, 10 Mar 2021 12:59:58 +0100 Subject: [PATCH 2/2] Check coin transfer enabled; extensionpoint --- x/wasm/internal/keeper/keeper.go | 65 +++++++++++++------ x/wasm/internal/keeper/keeper_test.go | 33 ++++++++-- x/wasm/internal/keeper/options.go | 11 +++- x/wasm/internal/keeper/options_test.go | 6 ++ .../keeper/wasmtesting/coin_transferrer.go | 14 ++++ 5 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 x/wasm/internal/keeper/wasmtesting/coin_transferrer.go diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 834256aa00d..8b5b44611c3 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -53,12 +53,17 @@ type messenger interface { DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) } +type coinTransferrer interface { + // TransferCoins sends the coin amounts from the source to the destination with rules applied. + TransferCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error +} + // Keeper will have a reference to Wasmer with it's own data directory. type Keeper struct { storeKey sdk.StoreKey cdc codec.Marshaler accountKeeper types.AccountKeeper - bankKeeper types.BankKeeper + bank coinTransferrer ChannelKeeper types.ChannelKeeper portKeeper types.PortKeeper capabilityKeeper types.CapabilityKeeper @@ -108,7 +113,7 @@ func NewKeeper( cdc: cdc, wasmer: wasmer, accountKeeper: accountKeeper, - bankKeeper: bankKeeper, + bank: NewBankCoinTransferrer(bankKeeper), ChannelKeeper: channelKeeper, portKeeper: portKeeper, capabilityKeeper: capabilityKeeper, @@ -230,13 +235,10 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A // deposit initial contract funds if !deposit.IsZero() { - if k.bankKeeper.BlockedAddr(creator) { - return nil, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "blocked address can not be used") - } - sdkerr := k.bankKeeper.SendCoins(ctx, creator, contractAddress, deposit) - if sdkerr != nil { - return nil, nil, sdkerr + if err := k.bank.TransferCoins(ctx, creator, contractAddress, deposit); err != nil { + return nil, nil, err } + } else { // create an empty account (so we don't have issues later) // TODO: can we remove this? @@ -325,13 +327,8 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller // add more funds if !coins.IsZero() { - if k.bankKeeper.BlockedAddr(caller) { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "blocked address can not be used") - } - - sdkerr := k.bankKeeper.SendCoins(ctx, caller, contractAddress, coins) - if sdkerr != nil { - return nil, sdkerr + if err := k.bank.TransferCoins(ctx, caller, contractAddress, coins); err != nil { + return nil, err } } @@ -1011,18 +1008,18 @@ func addrFromUint64(id uint64) sdk.AccAddress { } // MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier -type MultipiedGasMeter struct { +type MultipliedGasMeter struct { originalMeter sdk.GasMeter } -var _ wasmvm.GasMeter = MultipiedGasMeter{} +var _ wasmvm.GasMeter = MultipliedGasMeter{} -func (m MultipiedGasMeter) GasConsumed() sdk.Gas { +func (m MultipliedGasMeter) GasConsumed() sdk.Gas { return m.originalMeter.GasConsumed() * GasMultiplier } -func gasMeter(ctx sdk.Context) MultipiedGasMeter { - return MultipiedGasMeter{ +func gasMeter(ctx sdk.Context) MultipliedGasMeter { + return MultipliedGasMeter{ originalMeter: ctx.GasMeter(), } } @@ -1031,3 +1028,31 @@ func gasMeter(ctx sdk.Context) MultipiedGasMeter { func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } + +// CoinTransferrer replicates the cosmos-sdk behaviour as in +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.4/x/bank/keeper/msg_server.go#L26 +type CoinTransferrer struct { + keeper types.BankKeeper +} + +func NewBankCoinTransferrer(keeper types.BankKeeper) CoinTransferrer { + return CoinTransferrer{ + keeper: keeper, + } +} + +// TransferCoins transfers coins from source to destination account when coin send was enabled for them and the recipient +// is not in the blocked address list. +func (c CoinTransferrer) TransferCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { + if err := c.keeper.SendEnabledCoins(ctx, amt...); err != nil { + return err + } + if c.keeper.BlockedAddr(fromAddr) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "blocked address can not be used") + } + sdkerr := c.keeper.SendCoins(ctx, fromAddr, toAddr, amt) + if sdkerr != nil { + return sdkerr + } + return nil +} diff --git a/x/wasm/internal/keeper/keeper_test.go b/x/wasm/internal/keeper/keeper_test.go index e938302521e..855ed4105a0 100644 --- a/x/wasm/internal/keeper/keeper_test.go +++ b/x/wasm/internal/keeper/keeper_test.go @@ -7,6 +7,7 @@ import ( "github.com/CosmWasm/wasmd/x/wasm/internal/keeper/wasmtesting" wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "io/ioutil" "testing" "time" @@ -515,7 +516,7 @@ func TestExecute(t *testing.T) { // make sure gas is properly deducted from ctx gasAfter := ctx.GasMeter().GasConsumed() if types.EnableGasVerification { - require.Equal(t, uint64(0x12121), gasAfter-gasBefore) + require.Equal(t, uint64(0x12963), gasAfter-gasBefore) } // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob) @@ -543,10 +544,11 @@ func TestExecuteWithDeposit(t *testing.T) { ) specs := map[string]struct { - srcActor sdk.AccAddress - beneficiary sdk.AccAddress - expError bool - fundAddr bool + srcActor sdk.AccAddress + beneficiary sdk.AccAddress + newBankParams *banktypes.Params + expError bool + fundAddr bool }{ "actor with funds": { srcActor: bob, @@ -564,6 +566,23 @@ func TestExecuteWithDeposit(t *testing.T) { beneficiary: fred, expError: true, }, + "coin transfer with all transfers disabled": { + srcActor: bob, + fundAddr: true, + beneficiary: fred, + newBankParams: &banktypes.Params{DefaultSendEnabled: false}, + expError: true, + }, + "coin transfer with transfer denom disabled": { + srcActor: bob, + fundAddr: true, + beneficiary: fred, + newBankParams: &banktypes.Params{ + DefaultSendEnabled: true, + SendEnabled: []*banktypes.SendEnabled{{Denom: "denom", Enabled: false}}, + }, + expError: true, + }, "blocked address as beneficiary": { srcActor: bob, fundAddr: true, @@ -575,7 +594,9 @@ func TestExecuteWithDeposit(t *testing.T) { t.Run(msg, func(t *testing.T) { ctx, keepers := CreateTestInput(t, false, SupportedFeatures, nil, nil) accKeeper, bankKeeper, keeper := keepers.AccountKeeper, keepers.BankKeeper, keepers.WasmKeeper - + if spec.newBankParams != nil { + bankKeeper.SetParams(ctx, *spec.newBankParams) + } if spec.fundAddr { fundAccounts(t, ctx, accKeeper, bankKeeper, spec.srcActor, sdk.NewCoins(sdk.NewInt64Coin("denom", 200))) } diff --git a/x/wasm/internal/keeper/options.go b/x/wasm/internal/keeper/options.go index 5e5d6eb607f..b26897ee027 100644 --- a/x/wasm/internal/keeper/options.go +++ b/x/wasm/internal/keeper/options.go @@ -17,8 +17,15 @@ func WithWasmEngine(x types.WasmerEngine) Option { } // WithMessageHandler is an optional constructor parameter to set a custom message handler. -func WithMessageHandler(n messenger) Option { +func WithMessageHandler(x messenger) Option { return optsFn(func(k *Keeper) { - k.messenger = n + k.messenger = x + }) +} + +// WithCoinTransferrer is an optional constructor parameter to set a custom coin transferrer +func WithCoinTransferrer(x coinTransferrer) Option { + return optsFn(func(k *Keeper) { + k.bank = x }) } diff --git a/x/wasm/internal/keeper/options_test.go b/x/wasm/internal/keeper/options_test.go index d1847779819..74ee11373df 100644 --- a/x/wasm/internal/keeper/options_test.go +++ b/x/wasm/internal/keeper/options_test.go @@ -28,6 +28,12 @@ func TestConstructorOptions(t *testing.T) { assert.IsType(t, k.messenger, &wasmtesting.MockMessageHandler{}) }, }, + "coin transferrer": { + srcOpt: WithCoinTransferrer(&wasmtesting.MockCoinTransferrer{}), + verify: func(k Keeper) { + assert.IsType(t, k.bank, &wasmtesting.MockCoinTransferrer{}) + }, + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { diff --git a/x/wasm/internal/keeper/wasmtesting/coin_transferrer.go b/x/wasm/internal/keeper/wasmtesting/coin_transferrer.go new file mode 100644 index 00000000000..497ecc747f3 --- /dev/null +++ b/x/wasm/internal/keeper/wasmtesting/coin_transferrer.go @@ -0,0 +1,14 @@ +package wasmtesting + +import sdk "github.com/cosmos/cosmos-sdk/types" + +type MockCoinTransferrer struct { + TransferCoinsFn func(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error +} + +func (m *MockCoinTransferrer) TransferCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { + if m.TransferCoinsFn == nil { + panic("not expected to be called") + } + return m.TransferCoinsFn(ctx, fromAddr, toAddr, amt) +}