This document specifies the bank module of the Cosmos SDK.
The bank module is responsible for handling multi-asset coin transfers between accounts and tracking special-case pseudo-transfers which must work differently with particular kinds of accounts (notably delegating/undelegating for vesting accounts). It exposes several interfaces with varying capabilities for secure interaction with other modules which must alter user balances.
In addition, the bank module tracks and provides query support for the total supply of all assets used in the application.
Supply
The supply functionality:
passively tracks the total supply of coins within a chain,
provides a pattern for modules to hold/interact with Coins, and
introduces the invariant check to verify a chain's total supply.
Total Supply
The total Supply of the network is equal to the sum of all coins from the account. The total supply is updated every time a Coin is minted (eg: as part of the inflation mechanism) or burned (eg: due to slashing or if a governance proposal is vetoed).
Module Accounts
The supply functionality introduces a new type of auth.Account which can be used by modules to allocate tokens and in special cases mint or burn tokens. At a base level these module accounts are capable of sending/receiving tokens to and from auth.Accounts and other module accounts. This design replaces previous alternative designs where, to hold tokens, modules would burn the incoming tokens from the sender account, and then track those tokens internally. Later, in order to send tokens, the module would need to effectively mint tokens within a destination account. The new design removes duplicate logic between modules to perform this accounting.
The ModuleAccount interface is defined as follows:
type ModuleAccount interface {
auth.Account // same methods as the Account interface
GetName() string // name of the module; used to obtain the address
GetPermissions() []string // permissions of module account
HasPermission(string) bool
}
WARNING! Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed).
The supply Keeper also introduces new wrapper functions for the auth Keeper and the bank Keeper that are related to ModuleAccounts in order to be able to:
Get and set ModuleAccounts by providing the Name.
Send coins from and to other ModuleAccounts or standard Accounts (BaseAccount or VestingAccount) by passing only the Name.
Mint or Burn coins for a ModuleAccount (restricted to its permissions).
Permissions
Each ModuleAccount has a different set of permissions that provide different object capabilities to perform certain actions. Permissions need to be registered upon the creation of the supply Keeper so that every time a ModuleAccount calls the allowed functions, the Keeper can lookup the permissions to that specific account and perform or not perform the action.
The available permissions are:
Minter: allows for a module to mint a specific amount of coins.
Burner: allows for a module to burn a specific amount of coins.
Staking: allows for a module to delegate and undelegate a specific amount of coins.
State
The x/bank module keeps state of the following primary objects:
Account balances
Denomination metadata
The total supply of all balances
Information on which denominations are allowed to be sent.
In addition, the x/bank module keeps the following indexes to manage the aforementioned state:
The bank module stores it's params in state with the prefix of 0x05, it can be updated with governance or the address with authority.
Params: 0x05 | ProtocolBuffer(Params)
proto/cosmos/bank/v1beta1/bank.proto
// Params defines the parameters for the bank module.
message Params {
option (amino.name) = "cosmos-sdk/x/bank/Params";
option (gogoproto.goproto_stringer) = false;
// Deprecated: Use of SendEnabled in params is deprecated.
// For genesis, use the newly added send_enabled field in the genesis object.
// Storage, lookup, and manipulation of this information is now in the keeper.
//
// As of cosmos-sdk 0.47, this only exists for backwards compatibility of genesis files.
repeated SendEnabled send_enabled = 1 [deprecated = true];
bool default_send_enabled = 2;
}
The bank module provides these exported keeper interfaces that can be passed to other modules that read or update account balances. Modules should use the least-permissive interface that provides the functionality they require.
Best practices dictate careful review of bank module code to ensure that permissions are limited in the way that you expect.
Denied Addresses
The x/bank module accepts a map of addresses that are considered blocklisted from directly and explicitly receiving funds through means such as MsgSend and MsgMultiSend and direct API calls like SendCoinsFromModuleToAccount.
Typically, these addresses are module accounts. If these addresses receive funds outside the expected rules of the state machine, invariants are likely to be broken and could result in a halted network.
By providing the x/bank module with a blocklisted set of addresses, an error occurs for the operation if a user or client attempts to directly or indirectly send funds to a blocklisted account, for example, by using IBC.
The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins.
Restricted permission to mint per module could be achieved by using baseKeeper with WithMintCoinsRestriction to give specific restrictions to mint (e.g. only minting certain denom).
// Keeper defines a module interface that facilitates the transfer of coins
// between accounts.
type Keeper interface {
SendKeeper
WithMintCoinsRestriction(MintingRestrictionFn) BaseKeeper
InitGenesis(context.Context, *types.GenesisState)
ExportGenesis(context.Context) *types.GenesisState
GetSupply(ctx context.Context, denom string) sdk.Coin
HasSupply(ctx context.Context, denom string) bool
GetPaginatedTotalSupply(ctx context.Context, pagination *query.PageRequest) (sdk.Coins, *query.PageResponse, error)
IterateTotalSupply(ctx context.Context, cb func(sdk.Coin) bool)
GetDenomMetaData(ctx context.Context, denom string) (types.Metadata, bool)
HasDenomMetaData(ctx context.Context, denom string) bool
SetDenomMetaData(ctx context.Context, denomMetaData types.Metadata)
IterateAllDenomMetaData(ctx context.Context, cb func(types.Metadata) bool)
SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
DelegateCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
UndelegateCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
DelegateCoins(ctx context.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
UndelegateCoins(ctx context.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error
// GetAuthority gets the address capable of executing governance proposal messages. Usually the gov module account.
GetAuthority() string
types.QueryServer
}
SendKeeper
The send keeper provides access to account balances and the ability to transfer coins between accounts. The send keeper does not alter the total supply (mint or burn coins).
// SendKeeper defines a module interface that facilitates the transfer of coins
// between accounts without the possibility of creating coins.
type SendKeeper interface {
ViewKeeper
AppendSendRestriction(restriction SendRestrictionFn)
PrependSendRestriction(restriction SendRestrictionFn)
ClearSendRestriction()
InputOutputCoins(ctx context.Context, input types.Input, outputs []types.Output) error
SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error
GetParams(ctx context.Context) types.Params
SetParams(ctx context.Context, params types.Params) error
IsSendEnabledDenom(ctx context.Context, denom string) bool
SetSendEnabled(ctx context.Context, denom string, value bool)
SetAllSendEnabled(ctx context.Context, sendEnableds []*types.SendEnabled)
DeleteSendEnabled(ctx context.Context, denom string)
IterateSendEnabledEntries(ctx context.Context, cb func(denom string, sendEnabled bool) (stop bool))
GetAllSendEnabledEntries(ctx context.Context) []types.SendEnabled
IsSendEnabledCoin(ctx context.Context, coin sdk.Coin) bool
IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error
BlockedAddr(addr sdk.AccAddress) bool
}
Send Restrictions
The SendKeeper applies a SendRestrictionFn before each transfer of funds.
// A SendRestrictionFn can restrict sends and/or provide a new receiver address.
type SendRestrictionFn func(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (newToAddr sdk.AccAddress, err error)
After the SendKeeper (or BaseKeeper) has been created, send restrictions can be added to it using the AppendSendRestriction or PrependSendRestriction functions. Both functions compose the provided restriction with any previously provided restrictions. AppendSendRestriction adds the provided restriction to be run after any previously provided send restrictions. PrependSendRestriction adds the restriction to be run before any previously provided send restrictions. The composition will short-circuit when an error is encountered. I.e. if the first one returns an error, the second is not run.
During SendCoins, the send restriction is applied after coins are removed from the from address, but before adding them to the to address. During InputOutputCoins, the send restriction is applied after the input coins are removed and once for each output before the funds are added.
A send restriction function should make use of a custom value in the context to allow bypassing that specific restriction.
For example, in your module's keeper package, you'd define the send restriction function:
var _ banktypes.SendRestrictionFn = Keeper{}.SendRestrictionFn
func (k Keeper) SendRestrictionFn(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.AccAddress, error) {
// Bypass if the context says to.
if mymodule.HasBypass(ctx) {
return toAddr, nil
}
// Your custom send restriction logic goes here.
return nil, errors.New("not implemented")
}
The bank keeper should be provided to your keeper's constructor so the send restriction can be added to it:
Then, in the mymodule package, define the context helpers:
const bypassKey = "bypass-mymodule-restriction"
// WithBypass returns a new context that will cause the mymodule bank send restriction to be skipped.
func WithBypass(ctx context.Context) context.Context {
return sdk.UnwrapSDKContext(ctx).WithValue(bypassKey, true)
}
// WithoutBypass returns a new context that will cause the mymodule bank send restriction to not be skipped.
func WithoutBypass(ctx context.Context) context.Context {
return sdk.UnwrapSDKContext(ctx).WithValue(bypassKey, false)
}
// HasBypass checks the context to see if the mymodule bank send restriction should be skipped.
func HasBypass(ctx context.Context) bool {
bypassValue := ctx.Value(bypassKey)
if bypassValue == nil {
return false
}
bypass, isBool := bypassValue.(bool)
return isBool && bypass
}
Now, anywhere where you want to use SendCoins or InputOutputCoins, but you don't want your send restriction applied:
The view keeper provides read-only access to account balances. The view keeper does not have balance alteration functionality. All balance lookups are O(1).
The message will fail under the following conditions:
The coins do not have sending enabled
The to address is restricted
MsgMultiSend
Send coins from one sender and to a series of different address. If any of the receiving addresses do not correspond to an existing account, a new account is created.
proto/cosmos/bank/v1beta1/tx.proto
// MsgMultiSend represents an arbitrary multi-in, multi-out send message.
message MsgMultiSend {
option (cosmos.msg.v1.signer) = "inputs";
option (amino.name) = "cosmos-sdk/MsgMultiSend";
option (gogoproto.equal) = false;
// Inputs, despite being `repeated`, only allows one sender input. This is
// checked in MsgMultiSend's ValidateBasic.
repeated Input inputs = 1 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
repeated Output outputs = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}
The message will fail under the following conditions:
Any of the coins do not have sending enabled
Any of the to addresses are restricted
Any of the coins are locked
The inputs and outputs do not correctly correspond to one another
MsgUpdateParams
The bank module params can be updated through MsgUpdateParams, which can be done using governance proposal. The signer will always be the gov module account address.
proto/cosmos/bank/v1beta1/tx.proto
// MsgUpdateParams is the Msg/UpdateParams request type.
//
// Since: cosmos-sdk 0.47
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
// authority is the address that controls the module (defaults to x/gov unless overwritten).
string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
option (amino.name) = "cosmos-sdk/x/bank/MsgUpdateParams";
// params defines the x/bank parameters to update.
//
// NOTE: All parameters must be supplied.
Params params = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}
Used with the x/gov module to set create/edit SendEnabled entries.
proto/cosmos/bank/v1beta1/tx.proto
// MsgSetSendEnabled is the Msg/SetSendEnabled request type.
//
// Only entries to add/update/delete need to be included.
// Existing SendEnabled entries that are not included in this
// message are left unchanged.
//
// Since: cosmos-sdk 0.47
message MsgSetSendEnabled {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "cosmos-sdk/MsgSetSendEnabled";
string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// send_enabled is the list of entries to add or update.
repeated SendEnabled send_enabled = 2;
// use_default_for is a list of denoms that should use the params.default_send_enabled value.
// Denoms listed here will have their SendEnabled entries deleted.
// If a denom is included that doesn't have a SendEnabled entry,
// it will be ignored.
repeated string use_default_for = 3;
}
This message will fail under the following conditions:
The signer is not present
The coins are not spendable
The coins are not positive
The coins are not valid
Events
The bank module emits the following events:
Message Events
MsgSend
Type
Attribute Key
Attribute Value
transfer
recipient
{recipientAddress}
transfer
amount
{amount}
message
module
bank
message
action
send
message
sender
{senderAddress}
MsgMultiSend
Type
Attribute Key
Attribute Value
transfer
recipient
{recipientAddress}
transfer
amount
{amount}
message
module
bank
message
action
multisend
message
sender
{senderAddress}
Keeper Events
In addition to message events, the bank keeper will produce events when the following methods are called (or any method which ends up calling them)
MintCoins
{
"type": "coinbase",
"attributes": [
{
"key": "minter",
"value": "{{sdk.AccAddress of the module minting coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being minted}}",
"index": true
}
]
}
{
"type": "coin_received",
"attributes": [
{
"key": "receiver",
"value": "{{sdk.AccAddress of the module minting coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being received}}",
"index": true
}
]
}
BurnCoins
{
"type": "burn",
"attributes": [
{
"key": "burner",
"value": "{{sdk.AccAddress of the module burning coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being burned}}",
"index": true
}
]
}
{
"type": "coin_spent",
"attributes": [
{
"key": "spender",
"value": "{{sdk.AccAddress of the module burning coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being burned}}",
"index": true
}
]
}
addCoins
{
"type": "coin_received",
"attributes": [
{
"key": "receiver",
"value": "{{sdk.AccAddress of the address beneficiary of the coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being received}}",
"index": true
}
]
}
subUnlockedCoins/DelegateCoins
{
"type": "coin_spent",
"attributes": [
{
"key": "spender",
"value": "{{sdk.AccAddress of the address which is spending coins}}",
"index": true
},
{
"key": "amount",
"value": "{{sdk.Coins being spent}}",
"index": true
}
]
}
Parameters
The bank module contains the following parameters
SendEnabled
The SendEnabled parameter is now deprecated and not to be use. It is replaced with state store records.
DefaultSendEnabled
The default send enabled value controls send transfer capability for all coin denominations unless specifically included in the array of SendEnabled parameters.
Client
CLI
A user can query and interact with the bank module using the CLI.
Query
The query commands allow users to query bank state.
simd query bank --help
balances
The balances command allows users to query account balances by address.
The denom-metadata command allows users to query metadata for coin denominations. A user can query metadata for a single denomination using the --denom flag or all denominations without it.
The total command allows users to query the total supply of coins. A user can query the total supply for a single coin using the --denom flag or all coins without it.
simd query bank total [flags]
Example:
simd query bank total --denom stake
Example Output:
amount: "10000000000"
denom: stake
send-enabled
The send-enabled command allows users to query for all or some SendEnabled entries.