Skip to content

Two-factor authentication for transfers using a secondary approval address

Custom 2FA

Instruction text for agents that use the Custom 2FA skill, loaded by bb dev skills custom-2fa and the MCP get_skill_instructions tool.

Try it

Paste this into Claude Code, Codex, or Cursor with the BitBadges MCP server wired. The agent loads the skill, builds, verifies, and hands back a link to review and sign.

text
Load the custom-2fa skill and build me one: two-factor authentication for transfers using a secondary approval address. Use the session tools, run validate, review, and simulate in parallel, fix anything critical, then call get_review_url and give me the link.

From a shell, bb dev skills custom-2fa prints the same instructions.

Summary

Required standards: ["Custom-2FA"]

  • autoDeletionOptions.allowPurgeIfExpired: must be true
  • Approval name must contain "Custom 2FA"
  • Use time-dependent ownershipTimes in MsgTransferTokens (not forever)
  • Calculate timestamps: current time + expiration duration (milliseconds since epoch)
  • Tokens automatically expire and can be purged after expiration

Instructions

Custom-2FA Configuration

When creating a Custom-2FA collection, follow these requirements:

Preferred path: preset (one short tool call)

The mint approval is canonical, use custom-2fa.mint:

ts
add_preset_approval({ presetId: "custom-2fa.mint", params: { managerAddress: "bb1..." } })

The actual token expiration is set per-mint via the MsgTransferTokens ownershipTimes window (e.g. now → now + 5601000 ms). The approval itself just enables the Mint path with auto-purge.

Required Structure

  1. Standards: must include "Custom-2FA"

    • "standards": ["Custom-2FA"]
  2. Approval Requirements:

    • autoDeletionOptions.allowPurgeIfExpired: must be true
    • This allows expired tokens to be automatically purged
    • Approval name must contain "Custom 2FA"
  3. Time-Dependent Ownership: Use time-dependent ownershipTimes in MsgTransferTokens

    • Calculate timestamps: current time + expiration duration
    • Example: 5 minutes = Date.now() + (5 * 60 * 1000)

Complete Example

json
{
  "messages": [
    {
      "typeUrl": "/tokenization.MsgUniversalUpdateCollection",
      "value": {
        "standards": ["Custom-2FA"],
        "collectionApprovals": [{
          "fromListId": "Mint",
          "toListId": "All",
          "initiatedByListId": "bb1manager...",
          "approvalId": "2fa-mint",
          "tokenIds": [{ "start": "1", "end": "18446744073709551615" }],
          "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
          "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
          "approvalCriteria": {
            "overridesFromOutgoingApprovals": true,
            "autoDeletionOptions": { "allowPurgeIfExpired": true }
          }
        }]
      }
    },
    {
      "typeUrl": "/tokenization.MsgTransferTokens",
      "value": {
        "collectionId": "0",
        "transfers": [{
          "from": "Mint",
          "toAddresses": ["bb1recipient..."],
          "balances": [{
            "amount": "1",
            "tokenIds": [{ "start": "1", "end": "1" }],
            "ownershipTimes": [{ "start": "1706000000000", "end": "1706000300000" }]
          }]
        }]
      }
    }
  ]
}

2FA-Specific Gotchas

  • Must set allowPurgeIfExpired: true
  • Use time-dependent ownershipTimes in transfers (not forever)
  • Calculate expiration timestamps correctly (milliseconds since epoch)
  • Tokens automatically expire and can be purged after expiration

Edit this page on GitHub

For agents

How agents read these docs