# address-list

<!-- generated by site/scripts/gen-skills.ts; do not hand-edit -->

# Address List

Instruction text for agents that use the Address List skill, loaded by `bb dev skills address-list` 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 address-list skill and build me one: on-chain address list where membership = owning x1 of token ID 1. Manager can add/remove addresses. 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 address-list` prints the same instructions.

## Summary

Required standards: ["Address List"]

- validTokenIds: must be exactly [{ "start": "1", "end": "1" }]
- Two collection approvals required with exact approvalIds (frontend depends on these):
  1. "manager-add": fromListId "Mint", toListId "All", initiatedByListId = creator. Mints token to add address.
  2. "manager-remove": fromListId "!Mint", toListId burn address (bb1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs7gvmv), initiatedByListId = creator. Burns token to remove address.
- Both approvals must have overridesFromOutgoingApprovals: true
- No peer-to-peer transfer approval, only manager can modify the list
- Standard is "Address List" (not "Non-Transferable")
- Use per-field tools (set_standards, add_approval, set_permissions, set_invariants)

## Instructions

## Address List Configuration

An address list collection represents membership as token ownership: owning x1 of token ID 1 = being on the list. The manager controls membership by minting (adding) and burning (removing) tokens.

### Preferred path: presets (two short tool calls)

Both approvals are fully canonical, the only param that varies is the manager/creator address. Use the presets:

```ts
add_preset_approval({ presetId: "address-list.manager-add",    params: { creatorAddress: "bb1..." } })
add_preset_approval({ presetId: "address-list.manager-remove", params: { creatorAddress: "bb1..." } })
```

`list_presets({skill: "address-list"})` enumerates them. For non-canonical variants (e.g. a committee instead of a single manager) fall back to raw `add_approval`.

### Critical: Exact Approval IDs Required

The frontend identifies address list approvals by their exact approvalIds. Using different IDs will break the UI.

- Approval 1: approvalId must be **"manager-add"**
- Approval 2: approvalId must be **"manager-remove"**

### Required Structure

1. **Standards**: ["Address List"] (not "Non-Transferable")
2. **validTokenIds**: [{ "start": "1", "end": "1" }]
3. **Burn address**: bb1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs7gvmv (ETH null address in bb1 format)

### Manager-Add Approval (mint to add)

```json
{
  "fromListId": "Mint",
  "toListId": "All",
  "initiatedByListId": "bb1creator...",
  "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "tokenIds": [{ "start": "1", "end": "1" }],
  "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "uri": "ipfs://METADATA_APPROVAL_manager-add",
  "customData": "",
  "approvalId": "manager-add",
  "approvalCriteria": {
    "overridesFromOutgoingApprovals": true
  },
  "version": "0"
}
```

### Manager-Remove Approval (forceful burn to remove)

`fromListId` must be **"!Mint"** (All except Mint). Using "All" is rejected by the chain with "Mint address cannot be included in address list with other addresses", the Mint slot can only appear in a list by itself.

```json
{
  "fromListId": "!Mint",
  "toListId": "bb1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs7gvmv",
  "initiatedByListId": "bb1creator...",
  "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "tokenIds": [{ "start": "1", "end": "1" }],
  "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "uri": "ipfs://METADATA_APPROVAL_manager-remove",
  "customData": "",
  "approvalId": "manager-remove",
  "approvalCriteria": {
    "overridesFromOutgoingApprovals": true
  },
  "version": "0"
}
```

### Invariants

Do not set `noForcefulPostMintTransfers: true` on address-list collections. That invariant would block manager-remove from burning tokens (since its `overridesFromOutgoingApprovals: true` is only chain-allowed when `fromListId` is exactly "Mint"). The manager must be able to forcibly burn a list member's token, so leave that invariant off. Other default invariants (e.g. `noCustomOwnershipTimes`) are fine.

### Default Balances

```json
{
  "balances": [],
  "outgoingApprovals": [],
  "incomingApprovals": [],
  "autoApproveAllIncomingTransfers": true,
  "autoApproveSelfInitiatedOutgoingTransfers": true,
  "autoApproveSelfInitiatedIncomingTransfers": true,
  "userPermissions": {}
}
```

### Permissions

Lock approvals and token IDs:
- canUpdateCollectionApprovals: frozen
- canUpdateValidTokenIds: frozen
- canUpdateManager: frozen (typically)

## Common Mistakes

- Don't use approvalId other than "manager-add" and "manager-remove", the frontend depends on these exact strings.
- Don't use standard "Non-Transferable", address lists use "Address List".
- Don't add a peer-to-peer transfer approval, only the manager should modify the list.
- Don't forget overridesFromOutgoingApprovals: true on both approvals.
