Transferability / Approvals

Transferability in BitBadges is controlled through a hierarchical approval system with three levels: collection, outgoing, and incoming.

Three Transferability Levels

BitBadges supports three levels of transferability control:

Level
Controlled By
Approval Level
Approver Address
Stored On
Msg
Use Case

Collection

Manager/Issuer

collection

""

TokenCollection.collectionApprovals

MsgCreateCollection / MsgUpdateCollection

Global rules, freezability, compliance

Outgoing

Sender

outgoing

bb1...

UserBalanceStore.outgoingApprovals

MsgUpdateUserApprovals

Listings, delegation

Incoming

Recipient

incoming

bb1...

UserBalanceStore.incomingApprovals

MsgUpdateUserApprovals

Bids, access control

Each transfer must satisfy collection-level AND (unless overridden) user-level approvals, while also having sufficient balances to transfer.

Approval vs Permission vs Transfers

  • Approvals define the rules for transfers on multiple levels.

  • Transfers execute if the approval rules defined allow it and sufficient balances.

  • Permissions can control the updatability of approvals - canUpdateCollectionApprovals

Transfer Validation Process

Each transfer must 1) have sufficient balances, 2) satisfy the collection approvals, and 3) satisfy the corresponding user-level approvals (unless overridden).

Validation flow:

Transfer validation flow

Collection Approvals

Collection approvals define transferability rules for the entire collection on a global level. These rules apply to both minting and post-minting transfers. These let the issuer / manager control global compliance and transferability, such as freezability, revocation, and more.

All transfers must satisfy the collection approvals.

Important: Approval IDs are unique identifiers and must not collide with other collection approvals.

The Six Core Fields

Every approval defines Who? When? What? through these six core fields:

Field
Type
Purpose
Example

toListId

Address List ID

Who can receive tokens

"All", "Mint", "bb1..."

fromListId

Address List ID

Who can send tokens

"Mint", "!Mint"

initiatedByListId

Address List ID

Who can initiate transfer

"All", "bb1..."

transferTimes

UintRange[] (UNIX Milliseconds)

When transfer can occur

[{start: 1691931600000n, end: 1723554000000n}]

tokenIds

UintRange[] (Token IDs)

Which token IDs

[{start: 1n, end: 100n}]

ownershipTimes

UintRange[] (UNIX Milliseconds)

Which ownership times to be transferred

[{start: 1n, end: 18446744073709551615n}]

Example Approval

Translation: Allow anyone to claim tokens 1-100 from the Mint address between Aug 13, 2023 and Aug 13, 2024.

Approval Criteria

Approval criteria adds additional restrictions beyond basic approval matching. They are used to control who can transfer, when, how much, how often, and more. If criteria is not satisfied, the approval is not satisfied.

See Approval Criteria for all available criteria.

User-Level Approvals

Senders and recipients can configure user-level approvals that gate transfers. These follow the same structure as collection approvals (minus hardcoded sender/recipient logic respectively and no override logic). Sender approvals control who can send tokens on behalf of the user. Recipient approvals control who can send tokens to the user.

Stored on UserBalanceStore.outgoingApprovals[] (OutgoingApproval[]) and UserBalanceStore.incomingApprovals[] (IncomingApproval[]).

Outgoing Approvals

Control who can send tokens on behalf of the user:

Incoming Approvals

Control who can send tokens to the user:

User-Level Auto-Approval Flags

We provide auto-approval flags to automatically approve transfers without requiring explicit approval matching. These flags are used for convenience and ease of use for user-level approval handling. Typically, we recommend leaving all the auto-approval flags set to true.

Auto-Approve Self-Initiated Outgoing Transfers

When autoApproveSelfInitiatedOutgoingTransfers: true, outgoing transfers initiated by the user are automatically approved without checking outgoing approvals.

Use case: Convenience for users who want to freely send tokens they own without managing outgoing approval configurations.

Auto-Approve Self-Initiated Incoming Transfers

When autoApproveSelfInitiatedIncomingTransfers: true, incoming transfers initiated by the user are automatically approved without checking incoming approvals.

Use case: Convenience for users who want to receive tokens they request (e.g., claiming, requesting airdrops) without managing incoming approval configurations.

Auto-Approve All Incoming Transfers

When autoApproveAllIncomingTransfers: true, all incoming transfers are automatically approved regardless of who initiates them.

Use case: Users who want to accept all incoming transfers without restrictions. Useful for open wallets or accounts that should receive tokens from anyone.

Override Behavior

Collection approvals can override user-level approvals for administrative controls like freezing, revocation, or forced transfers. This is done via the approvalCriteria field and only available on the collection approval criteria interface.

If set to true, we do NOT check the corresponding user-level approvals for the sender and/or recipient.

Mint Address Overrides

Because the Mint address cannot control its own user-level approvals, it must always override the sender's outgoing approvals to properly work.

When Overrides Apply

Outgoing overrides: When overridesFromOutgoingApprovals: true, the collection approval bypasses the sender's outgoing approvals. This enables:

  • Freezing tokens (prevent transfers regardless of user settings)

  • Forced revocation (remove tokens from users)

  • Administrative transfers (manager-controlled actions)

Incoming overrides: When overridesToIncomingApprovals: true, the collection approval bypasses the recipient's incoming approvals. This enables:

  • Forced transfers (send tokens even if recipient blocks them)

  • Administrative actions (manager-controlled distributions)

Important: Overrides are powerful and should be used carefully. They allow executing transfers that would otherwise be blocked by user settings.

If you want to setup your collection without any overrides, you can simply set the invariants.noForcefulPostMintTransfers to true. This will prevent any collection approvals from ever using override flags.

Break-Down Logic

The system can break down transfers and approvals into partial matches to make transfers succeed. It deducts as much as possible from each approval as it iterates.

For proper design, you should try to design your approvals such that they never have to match to more than one. However, if needed, we break down the transfer and approvals as fine-grained as we can to make it succeed.

See Auto-Scan and Prioritized Approvals for details on how the system selects approvals and how you can selectively prioritize approvals.

Last updated