Concept

Transfers & Compliance

A send is decided in one place. tokenfactory.CanSendTokens is consulted for bank sends, NFT sends, and swap settlement alike. It evaluates in a fixed order: blacklist first, then the restricted-denom rule. There is no second, weaker path.

The decision engine: CanSendTokens

Every token movement on CenterChain -- bank sends, NFT transfers, swap settlements -- passes through a single function:tokenfactory.CanSendTokens. This is the single most important thing to understand about behavior: there is no second, weaker path.

Step 1: Blacklist

If sender or recipient is on the denom's blacklist, the send is rejected immediately. No exceptions.

Step 2: Restriction

If the denom has a restriction rule, the recipient must satisfy it (or be the admin/delegate). Otherwise, rejected.

Step 3: Standard checks

Balance sufficiency, denom existence, send-enabled flag. Only after blacklist and restriction pass.

What goes through CanSendTokens

Bank sends

Standard MsgSend / MsgMultiSend. Every denom, every amount.

NFT transfers

The NFT module calls CanSendTokens for the underlying denom before transferring.

Swap settlement

When a swap settles, the movement of tokens also passes through the same function.

IBC sends

Tokens leaving via IBC also pass through the same compliance layer.

Blacklist mechanics

  • Managed by denom admin (or delegate) via MsgAddToBlacklist / MsgRemoveFromBlacklist.
  • Applies to both sender and recipient -- a blacklisted address cannot send or receive.
  • Cap: 100,000 blacklisted addresses chain-wide.
  • Blacklist check runs before any other logic -- immediate rejection, no state changes.

Restricted-denom rule

  • Set by denom admin via MsgSetDenomRestriction. Defines who may hold/receive the denom.
  • Can be an allowlist (only these addresses) or a denylist logic via module config.
  • Admin and delegates are always exempt from restriction.
  • Checked after blacklist, before standard balance checks.

Ante handler integration

The ante handler wraps CanSendTokens so that every transaction carrying token movements is validated before execution. This means:

  • Failed compliance = transaction rejected before state changes
  • Gas is consumed for the check, but no state is mutated on rejection
  • Errors are descriptive: ErrBlacklisted, ErrRestricted, ErrInsufficientBalance

Read the full specification

Full message flow diagrams, error code reference, and integration patterns.