摘要

本文档定义了 Cosmos SDK 的 bank 模块。 bank 模块负责处理账户之间的多资产代币转账,并跟踪必须针对特定类型账户以不同方式处理的特殊伪转账场景(尤其是 vesting 账户的委托/取消委托)。它暴露了若干具有不同能力的接口,供其他必须修改用户余额的模块进行安全交互。 此外,bank 模块还会跟踪应用中使用的所有资产的总供应量,并提供相应的查询支持。 该模块已用于 Cosmos Hub。

目录

供应量

supply 功能:
  • 被动跟踪链内代币的总供应量,
  • 为模块持有/交互 Coins 提供一种模式,并且
  • 引入不变量检查,用于验证链的总供应量。

总供应量

网络的总 Supply 等于账户中所有代币数量之和。每当 Coin 被铸造(例如作为通胀机制的一部分)或销毁(例如由于罚没,或治理提案被否决)时,总供应量都会更新。

模块账户

供应量功能引入了一种新的 auth.Account 类型,模块可以使用它来分配代币,并在特殊情况下铸造或销毁代币。在基础层面,这些模块账户能够向 auth.Account 以及其他模块账户发送和接收代币。该设计替代了先前的另一种设计:模块若要持有代币,需要先将发送方账户转入的代币销毁,然后在模块内部追踪这些代币。之后,为了发送代币,模块又需要实质上在目标账户中重新铸造代币。新设计消除了各模块为完成这类记账而重复实现的逻辑。 ModuleAccount 接口定义如下:
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
}
警告! 任何允许资金被直接或间接发送的模块或消息处理器,都必须显式保证这些资金不能被发送到模块账户(除非明确允许)。
supply 的 Keeper 还为 auth Keeper 和 bank Keeper 引入了与 ModuleAccount 相关的新包装函数,以便能够:
  • 通过提供 Name 获取和设置 ModuleAccount。
  • 仅通过传入 Name,即可在其他 ModuleAccount 或标准 Account(BaseAccount 或 VestingAccount)之间发送代币。
  • 为某个 ModuleAccount 执行 Mint 或 Burn 代币(受其权限限制)。

权限

每个 ModuleAccount 都有一组不同的权限,这些权限提供了执行特定操作所需的不同对象能力。权限需要在创建 supply Keeper 时注册,这样每次 ModuleAccount 调用允许的函数时,Keeper 都可以查找该特定账户的权限,并决定是否执行该操作。 可用权限包括:
  • Minter:允许模块铸造指定数量的代币。
  • Burner:允许模块销毁指定数量的代币。
  • Staking:允许模块委托和取消委托指定数量的代币。

状态

x/bank 模块维护以下主要对象的状态:
  1. 账户余额
  2. 面额元数据
  3. 所有余额的总供应量
  4. 哪些面额允许发送的信息
此外,x/bank 模块还维护以下索引来管理上述状态:
  • 供应量索引:0x0 | byte(denom) -> byte(amount)
  • 面额元数据索引:0x1 | byte(denom) -> ProtocolBuffer(Metadata)
  • 余额索引:0x2 | byte(address length) | []byte(address) | []byte(balance.Denom) -> ProtocolBuffer(balance)
  • 面额到账户地址的反向索引:0x03 | byte(denom) | 0x00 | []byte(address) -> 0

参数

bank 模块将其参数以 0x05 为前缀存储在状态中,参数可以通过治理或具有权限的地址进行更新。
  • 参数:0x05 | ProtocolBuffer(Params)
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/bank.proto#L12-L23

Keeper

bank 模块提供了以下导出的 keeper 接口,可传递给其他读取或更新账户余额的模块。模块应使用能够满足其所需功能的最小权限接口。 最佳实践要求仔细审查 bank 模块代码,以确保权限限制符合你的预期。

拒绝地址

x/bank 模块接收一个地址映射,这些地址会被视为禁止通过 MsgSend、MsgMultiSend 以及 SendCoinsFromModuleToAccount 这类直接 API 调用来直接、显式接收资金的黑名单地址。 通常,这些地址是模块账户。如果这些地址在状态机预期规则之外接收到资金,不变量很可能被破坏,并可能导致网络停止。 通过向 x/bank 模块提供一组黑名单地址,如果用户或客户端尝试直接或间接向黑名单账户发送资金,例如通过 IBC,该操作将返回错误。

常见类型

Input

多方转账中的输入。
// Input models transaction input.
message Input {
  string   address                        = 1;
  repeated cosmos.base.v1beta1.Coin coins = 2;
}

Output

多方转账中的输出。
// Output models transaction outputs.
message Output {
  string   address                        = 1;
  repeated cosmos.base.v1beta1.Coin coins = 2;
}

BaseKeeper

基础 keeper 提供完全权限访问:可以任意修改任何账户余额,以及铸造或销毁代币。 可以通过将 baseKeeper 与 WithMintCoinsRestriction 一起使用,为每个模块实现受限的铸造权限,从而对铸造施加特定限制(例如只允许铸造某些 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

send keeper 提供对账户余额的访问能力,以及在账户之间转移代币的能力。send keeper 不会修改总供应量(不会铸造或销毁代币)。
// 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
}

发送限制

SendKeeper 会在每次资金转账前应用一个 SendRestrictionFn。
// 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)
在创建 SendKeeper(或 BaseKeeper)之后,可以通过 AppendSendRestriction 或 PrependSendRestriction 函数向其添加发送限制。 这两个函数都会将提供的限制与此前提供的所有限制组合起来。 AppendSendRestriction 会把提供的限制添加到此前所有发送限制之后执行。 PrependSendRestriction 会把提供的限制添加到此前所有发送限制之前执行。 当遇到错误时,这种组合会短路。也就是说,如果第一个返回错误,第二个就不会执行。 在 SendCoins 期间,发送限制会在从发送方地址扣除代币并将其添加到接收方地址之前应用。 在 InputOutputCoins 期间,发送限制会在输入代币被扣除之后应用,并且会在资金添加到每个输出之前,对每个输出分别应用一次。 发送限制函数应利用上下文中的自定义值,以便允许绕过该特定限制。 发送限制不会应用于 ModuleToAccount 或 ModuleToModule 转账。这是因为模块需要将资金转移到用户账户和其他模块账户。这是一项设计决策,目的是为状态机提供更高的灵活性。状态机应当能够在模块账户与用户账户之间不受限制地转移资金。 其次,这一限制甚至会限制状态机自身的使用。用户将无法接收奖励,也无法在模块账户之间转移资金。在用户将资金从用户账户发送到 community pool,然后通过治理提案将这些代币转入用户账户的场景下,如何处理应由应用链开发者自行决定。我们无法在这里做出强假设。 第三,如果某个代币被禁用,而该代币又在 begin/endblock 中被转移,这个问题可能会导致链停机。这是我们维持当前变更的最后一个原因,因为对用户而言,这种限制带来的伤害大于收益。 例如,在你的模块 keeper 包中,你可以这样定义发送限制函数:
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")
}
应将 bank keeper 提供给你的 keeper 构造函数,以便将发送限制添加到其中:
func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, bankKeeper mymodule.BankKeeper)

Keeper {
    rv := Keeper{/*...*/
}

bankKeeper.AppendSendRestriction(rv.SendRestrictionFn)

return rv
}
然后,在 mymodule 包中,定义这些上下文辅助函数:
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
}
现在,在任何你想使用 SendCoins 或 InputOutputCoins,但又不希望应用你的发送限制的地方:
func (k Keeper)

DoThing(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins)

error {
    return k.bankKeeper.SendCoins(mymodule.WithBypass(ctx), fromAddr, toAddr, amt)
}

ViewKeeper

view keeper 提供对账户余额的只读访问。view keeper 不具备修改余额的功能。所有余额查询均为 O(1)。
// ViewKeeper defines a module interface that facilitates read only access to
// account balances.
type ViewKeeper interface {
    ValidateBalance(ctx context.Context, addr sdk.AccAddress)

error
    HasBalance(ctx context.Context, addr sdk.AccAddress, amt sdk.Coin)

bool

    GetAllBalances(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    GetAccountsBalances(ctx context.Context) []types.Balance
    GetBalance(ctx context.Context, addr sdk.AccAddress, denom string)

sdk.Coin
    LockedCoins(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    SpendableCoins(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    SpendableCoin(ctx context.Context, addr sdk.AccAddress, denom string)

sdk.Coin

    IterateAccountBalances(ctx context.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool))

IterateAllBalances(ctx context.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool))
}

消息

MsgSend

将代币从一个地址发送到另一个地址。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L38-L53
该消息会在以下情况下失败:
  • 这些代币未启用发送
  • to 地址受限

MsgMultiSend

将代币从一个发送方发送到一组不同的地址。如果任一接收地址不对应现有账户,则会创建一个新账户。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L58-L69
该消息会在以下情况下失败:
  • 任一代币未启用发送
  • 任一 to 地址受限
  • 任一代币被锁定
  • 输入和输出之间没有正确对应

MsgUpdateParams

bank 模块参数可以通过 MsgUpdateParams 更新,这可以通过治理提案完成。签名者始终是 gov 模块账户地址。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L74-L88
该消息处理可能在以下情况下失败:
  • signer 不是 gov 模块账户地址。

MsgSetSendEnabled

与 x/gov 模块一起使用,用于创建或编辑 SendEnabled 条目。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L96-L117
该消息会在以下情况下失败:
  • authority 不是 bech32 地址。
  • authority 不是 x/gov 模块的地址。
  • 存在多个具有相同 Denom 的 SendEnabled 条目。
  • 一个或多个 SendEnabled 条目包含无效的 Denom。

事件

bank 模块会发出以下事件:

消息事件

MsgSend

类型属性键属性值
transferrecipient{recipientAddress}
transferamount{amount}
messagemodulebank
messageactionsend
messagesender{senderAddress}

MsgMultiSend

类型属性键属性值
transferrecipient{recipientAddress}
transferamount{amount}
messagemodulebank
messageactionmultisend
messagesender{senderAddress}

Keeper 事件

除了消息事件之外,在调用以下方法时,bank keeper 也会产生事件(或调用过程中最终会触发这些方法的任何方法)。

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
    }
  ]
}

参数

bank 模块包含以下参数

SendEnabled

SendEnabled 参数现已弃用,不应继续使用。它已被状态存储记录所替代。

DefaultSendEnabled

默认的发送启用值控制所有币种的发送转账能力,除非这些币种被明确包含在 SendEnabled 参数数组中。

客户端

CLI

用户可以使用 CLI 查询并与 bank 模块交互。

查询

query 命令允许用户查询 bank 状态。
simd query bank --help
balances
balances 命令允许用户按地址查询账户余额。
simd query bank balances [address] [flags]
示例:
simd query bank balances cosmos1..
示例输出:
balances:
- amount: "1000000000"
  denom: stake
pagination:
  next_key: null
  total: "0"
denom-metadata
denom-metadata 命令允许用户查询币种的元数据。用户可以使用 --denom 标志查询单个币种的元数据,或在不使用该标志的情况下查询所有币种。
simd query bank denom-metadata [flags]
示例:
simd query bank denom-metadata --denom stake
示例输出:
metadata:
  base: stake
  denom_units:
  - aliases:
    - STAKE
    denom: stake
  description: native staking token of simulation app
  display: stake
  name: SimApp Token
  symbol: STK
total
total 命令允许用户查询代币的总供应量。用户可以使用 --denom 标志查询单个代币的总供应量,也可以在不使用该标志时查询所有代币。
simd query bank total [flags]
示例:
simd query bank total --denom stake
示例输出:
amount: "10000000000"
denom: stake
send-enabled
send-enabled 命令允许用户查询全部或部分 SendEnabled 条目。
simd query bank send-enabled [denom1 ...] [flags]
示例:
simd query bank send-enabled
示例输出:
send_enabled:
- denom: foocoin
  enabled: true
- denom: barcoin
pagination:
  next-key: null
  total: 2 

Transactions

tx 命令允许用户与 bank 模块交互。
simd tx bank --help
send
send 命令允许用户将资金从一个账户发送到另一个账户。
simd tx bank send [from_key_or_address] [to_address] [amount] [flags]
示例:
simd tx bank send cosmos1.. cosmos1.. 100stake

gRPC

用户可以使用 gRPC 端点查询 bank 模块。

Balance

Balance 端点允许用户按地址查询指定面额的账户余额。
cosmos.bank.v1beta1.Query/Balance
示例:
grpcurl -plaintext \
    -d '{"address":"cosmos1..","denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/Balance
示例输出:
{
  "balance": {
    "denom": "stake",
    "amount": "1000000000"
  }
}

AllBalances

AllBalances 端点允许用户按地址查询所有面额的账户余额。
cosmos.bank.v1beta1.Query/AllBalances
示例:
grpcurl -plaintext \
    -d '{"address":"cosmos1.."}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/AllBalances
示例输出:
{
  "balances": [
    {
      "denom": "stake",
      "amount": "1000000000"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DenomMetadata

DenomMetadata 端点允许用户查询单个代币面额的元数据。
cosmos.bank.v1beta1.Query/DenomMetadata
示例:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomMetadata
示例输出:
{
  "metadata": {
    "description": "native staking token of simulation app",
    "denomUnits": [
      {
        "denom": "stake",
        "aliases": [
          "STAKE"
        ]
      }
    ],
    "base": "stake",
    "display": "stake",
    "name": "SimApp Token",
    "symbol": "STK"
  }
}

DenomsMetadata

DenomsMetadata 端点允许用户查询所有代币面额的元数据。
cosmos.bank.v1beta1.Query/DenomsMetadata
示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomsMetadata
示例输出:
{
  "metadatas": [
    {
      "description": "native staking token of simulation app",
      "denomUnits": [
        {
          "denom": "stake",
          "aliases": [
            "STAKE"
          ]
        }
      ],
      "base": "stake",
      "display": "stake",
      "name": "SimApp Token",
      "symbol": "STK"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DenomOwners

DenomOwners 端点允许用户查询单个代币面额的元数据。
cosmos.bank.v1beta1.Query/DenomOwners
示例:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomOwners
示例输出:
{
  "denomOwners": [
    {
  "address": "cosmos1..",
  "balance": {
  "denom": "stake",
  "amount": "5000000000"
      }
    
},
    {
  "address": "cosmos1..",
  "balance": {
  "denom": "stake",
  "amount": "5000000000"
      }
    
},
  ],
  "pagination": {
  "total": "2"
  }
}

TotalSupply

TotalSupply 端点允许用户查询所有代币的总供应量。
cosmos.bank.v1beta1.Query/TotalSupply
示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/TotalSupply
示例输出:
{
  "supply": [
    {
      "denom": "stake",
      "amount": "10000000000"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

SupplyOf

SupplyOf 端点允许用户查询单个代币的总供应量。
cosmos.bank.v1beta1.Query/SupplyOf
示例:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/SupplyOf
示例输出:
{
  "amount": {
    "denom": "stake",
    "amount": "10000000000"
  }
}

Params

Params 端点允许用户查询 bank 模块的参数。
cosmos.bank.v1beta1.Query/Params
示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/Params
示例输出:
{
  "params": {
    "defaultSendEnabled": true
  }
}

SendEnabled

SendEnabled 端点允许用户查询 bank 模块的 SendEnabled 条目。 对于未返回的任何面额,请使用 Params.DefaultSendEnabled 的值。
cosmos.bank.v1beta1.Query/SendEnabled
示例:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/SendEnabled
示例输出:
{
  "send_enabled": [
    {
      "denom": "foocoin",
      "enabled": true
    },
    {
      "denom": "barcoin"
    }
  ],
  "pagination": {
    "next-key": null,
    "total": 2
  }
}

Abstract

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. This module is used in the Cosmos Hub.

Contents

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:
  1. Account balances
  2. Denomination metadata
  3. The total supply of all balances
  4. Information on which denominations are allowed to be sent.
In addition, the x/bank module keeps the following indexes to manage the aforementioned state:
  • Supply Index: 0x0 | byte(denom) -> byte(amount)
  • Denom Metadata Index: 0x1 | byte(denom) -> ProtocolBuffer(Metadata)
  • Balances Index: 0x2 | byte(address length) | []byte(address) | []byte(balance.Denom) -> ProtocolBuffer(balance)
  • Reverse Denomination to Address Index: 0x03 | byte(denom) | 0x00 | []byte(address) -> 0

Params

The bank module stores its params in state with the prefix of 0x05, it can be updated with governance or the address with authority.
  • Params: 0x05 | ProtocolBuffer(Params)
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/bank.proto#L12-L23

Keepers

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.

Common Types

Input

An input of a multiparty transfer
// Input models transaction input.
message Input {
  string   address                        = 1;
  repeated cosmos.base.v1beta1.Coin coins = 2;
}

Output

An output of a multiparty transfer.
// Output models transaction outputs.
message Output {
  string   address                        = 1;
  repeated cosmos.base.v1beta1.Coin coins = 2;
}

BaseKeeper

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 before coins are removed from the from address and 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. Send Restrictions are not placed on ModuleToAccount or ModuleToModule transfers. This is done due to modules needing to move funds to user accounts and other module accounts. This is a design decision to allow for more flexibility in the state machine. The state machine should be able to move funds between module accounts and user accounts without restrictions. Secondly this limitation would limit the usage of the state machine even for itself. users would not be able to receive rewards, not be able to move funds between module accounts. In the case that a user sends funds from a user account to the community pool and then a governance proposal is used to get those tokens into the users account this would fall under the discretion of the app chain developer to what they would like to do here. We can not make strong assumptions here. Thirdly, this issue could lead into a chain halt if a token is disabled and the token is moved in the begin/endblock. This is the last reason we see the current change and more damaging then beneficial for users. 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:
func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, bankKeeper mymodule.BankKeeper)

Keeper {
    rv := Keeper{/*...*/
}

bankKeeper.AppendSendRestriction(rv.SendRestrictionFn)

return rv
}
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:
func (k Keeper)

DoThing(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins)

error {
    return k.bankKeeper.SendCoins(mymodule.WithBypass(ctx), fromAddr, toAddr, amt)
}

ViewKeeper

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).
// ViewKeeper defines a module interface that facilitates read only access to
// account balances.
type ViewKeeper interface {
    ValidateBalance(ctx context.Context, addr sdk.AccAddress)

error
    HasBalance(ctx context.Context, addr sdk.AccAddress, amt sdk.Coin)

bool

    GetAllBalances(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    GetAccountsBalances(ctx context.Context) []types.Balance
    GetBalance(ctx context.Context, addr sdk.AccAddress, denom string)

sdk.Coin
    LockedCoins(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    SpendableCoins(ctx context.Context, addr sdk.AccAddress)

sdk.Coins
    SpendableCoin(ctx context.Context, addr sdk.AccAddress, denom string)

sdk.Coin

    IterateAccountBalances(ctx context.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool))

IterateAllBalances(ctx context.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool))
}

Messages

MsgSend

Send coins from one address to another.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L38-L53
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.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L58-L69
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.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L74-L88
The message handling can fail if:
  • signer is not the gov module account address.

MsgSetSendEnabled

Used with the x/gov module to set create/edit SendEnabled entries.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L96-L117
The message will fail under the following conditions:
  • The authority is not a bech32 address.
  • The authority is not x/gov module’s address.
  • There are multiple SendEnabled entries with the same Denom.
  • One or more SendEnabled entries has an invalid Denom.

Events

The bank module emits the following events:

Message Events

MsgSend

TypeAttribute KeyAttribute Value
transferrecipient{recipientAddress}
transferamount{amount}
messagemodulebank
messageactionsend
messagesender{senderAddress}

MsgMultiSend

TypeAttribute KeyAttribute Value
transferrecipient{recipientAddress}
transferamount{amount}
messagemodulebank
messageactionmultisend
messagesender{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.
simd query bank balances [address] [flags]
Example:
simd query bank balances cosmos1..
Example Output:
balances:
- amount: "1000000000"
  denom: stake
pagination:
  next_key: null
  total: "0"
denom-metadata
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.
simd query bank denom-metadata [flags]
Example:
simd query bank denom-metadata --denom stake
Example Output:
metadata:
  base: stake
  denom_units:
  - aliases:
    - STAKE
    denom: stake
  description: native staking token of simulation app
  display: stake
  name: SimApp Token
  symbol: STK
total
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.
simd query bank send-enabled [denom1 ...] [flags]
Example:
simd query bank send-enabled
Example output:
send_enabled:
- denom: foocoin
  enabled: true
- denom: barcoin
pagination:
  next-key: null
  total: 2 

Transactions

The tx commands allow users to interact with the bank module.
simd tx bank --help
send
The send command allows users to send funds from one account to another.
simd tx bank send [from_key_or_address] [to_address] [amount] [flags]
Example:
simd tx bank send cosmos1.. cosmos1.. 100stake

gRPC

A user can query the bank module using gRPC endpoints.

Balance

The Balance endpoint allows users to query account balance by address for a given denomination.
cosmos.bank.v1beta1.Query/Balance
Example:
grpcurl -plaintext \
    -d '{"address":"cosmos1..","denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/Balance
Example Output:
{
  "balance": {
    "denom": "stake",
    "amount": "1000000000"
  }
}

AllBalances

The AllBalances endpoint allows users to query account balance by address for all denominations.
cosmos.bank.v1beta1.Query/AllBalances
Example:
grpcurl -plaintext \
    -d '{"address":"cosmos1.."}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/AllBalances
Example Output:
{
  "balances": [
    {
      "denom": "stake",
      "amount": "1000000000"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DenomMetadata

The DenomMetadata endpoint allows users to query metadata for a single coin denomination.
cosmos.bank.v1beta1.Query/DenomMetadata
Example:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomMetadata
Example Output:
{
  "metadata": {
    "description": "native staking token of simulation app",
    "denomUnits": [
      {
        "denom": "stake",
        "aliases": [
          "STAKE"
        ]
      }
    ],
    "base": "stake",
    "display": "stake",
    "name": "SimApp Token",
    "symbol": "STK"
  }
}

DenomsMetadata

The DenomsMetadata endpoint allows users to query metadata for all coin denominations.
cosmos.bank.v1beta1.Query/DenomsMetadata
Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomsMetadata
Example Output:
{
  "metadatas": [
    {
      "description": "native staking token of simulation app",
      "denomUnits": [
        {
          "denom": "stake",
          "aliases": [
            "STAKE"
          ]
        }
      ],
      "base": "stake",
      "display": "stake",
      "name": "SimApp Token",
      "symbol": "STK"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

DenomOwners

The DenomOwners endpoint allows users to query metadata for a single coin denomination.
cosmos.bank.v1beta1.Query/DenomOwners
Example:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/DenomOwners
Example Output:
{
  "denomOwners": [
    {
  "address": "cosmos1..",
  "balance": {
  "denom": "stake",
  "amount": "5000000000"
      }
    
},
    {
  "address": "cosmos1..",
  "balance": {
  "denom": "stake",
  "amount": "5000000000"
      }
    
},
  ],
  "pagination": {
  "total": "2"
  }
}

TotalSupply

The TotalSupply endpoint allows users to query the total supply of all coins.
cosmos.bank.v1beta1.Query/TotalSupply
Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/TotalSupply
Example Output:
{
  "supply": [
    {
      "denom": "stake",
      "amount": "10000000000"
    }
  ],
  "pagination": {
    "total": "1"
  }
}

SupplyOf

The SupplyOf endpoint allows users to query the total supply of a single coin.
cosmos.bank.v1beta1.Query/SupplyOf
Example:
grpcurl -plaintext \
    -d '{"denom":"stake"}' \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/SupplyOf
Example Output:
{
  "amount": {
    "denom": "stake",
    "amount": "10000000000"
  }
}

Params

The Params endpoint allows users to query the parameters of the bank module.
cosmos.bank.v1beta1.Query/Params
Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/Params
Example Output:
{
  "params": {
    "defaultSendEnabled": true
  }
}

SendEnabled

The SendEnabled enpoints allows users to query the SendEnabled entries of the bank module. Any denominations NOT returned, use the Params.DefaultSendEnabled value.
cosmos.bank.v1beta1.Query/SendEnabled
Example:
grpcurl -plaintext \
    localhost:9090 \
    cosmos.bank.v1beta1.Query/SendEnabled
Example Output:
{
  "send_enabled": [
    {
      "denom": "foocoin",
      "enabled": true
    },
    {
      "denom": "barcoin"
    }
  ],
  "pagination": {
    "next-key": null,
    "total": 2
  }
}