> For the complete documentation index, see [llms.txt](https://bsv.brc.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bsv.brc.dev/tokens/0163.md).

# BSV-21 Basket Profile for BRC-46 / BRC-100

Brandon Cryderman / HandCash (<brandongcryderman@gmail.com>)

## Abstract

This BRC defines the **application basket profile** for [BRC-161](/tokens/0161.md) BSV-21 fungible **value** outputs under [BRC-46](/wallet/0046.md) / [BRC-100](/wallet/0100.md). It reserves the basket name `bsv21`, specifies eligibility, documents **tags** for `listOutputs` filters, [BRC-37](/outpoints/0037.md) `customInstructions` for **load-bearing token fields** and **derivation**, and describes normative hold / list / transfer / import flows.

It does **not** redefine BSV-21 token economics (deploy, auth, mint, transfer, burn). Those live in [BRC-161](/tokens/0161.md) (JSON) and [BRC-162](/tokens/0162.md) (binary).

**Trust model:** holders verify *their* tips (local history / remittance / [BRC-176](/tokens/0176.md) BEEF for the outs they hold). Issuers are trusted for mint policy. Global supply-cap / world-conservation proofs are **not** required by this profile.

## Motivation

[BRC-161](/tokens/0161.md) / [BRC-162](/tokens/0162.md) define how BSV-21 appears on outputs. Without a shared basket name and remittance contract, wallets cannot reliably:

1. Hold the same token UTXOs in a common basket (separate from spendable BSV and from `1sat` collectables).
2. List and aggregate balances by token id for apps and UI.
3. Transfer value outputs while preserving remittance (`customInstructions` + filter tags) the receiver needs.
4. Import historical tips via `internalizeAction` with interoperable basket insertion.

This profile records that convention so independent wallets can interoperate without trusting a single vendor. It is the fungible counterpart to [BRC-147](/tokens/0147.md) for collectables.

## Specification

### Basket identifier

* The basket name is the UTF-8 string `bsv21`.
* Per [BRC-46](/wallet/0046.md), implementations normalize basket identifiers by trimming whitespace and lowercasing. After normalization, conforming use of this profile MUST use exactly `bsv21`.
* This identifier does **not** use the [BRC-99](/wallet/0099.md) reserved `p` prefix. It is a standard application basket under BRC-46 permissioning.

### Eligibility

An output SHOULD be placed in basket `bsv21` only when **all** of the following hold:

1. `satoshis === 1` (JSON carrier is a 1-sat inscription output per [BRC-161](/tokens/0161.md) / [BRC-160](/tokens/0160.md)).
2. The output is a BSV-21 **value** output under [BRC-161](/tokens/0161.md): balance-bearing `deploy+mint`, `mint`, or `transfer` (not authority-only `deploy+auth` / `auth`, and not `burn`).
3. The sender or importer reasonably believes the inscription (or prior remittance) names a valid token `id` and `amt` for that output.

Wallets MUST NOT place ordinary payment change, `1sat` collectables, or authority-only outputs into `bsv21` merely to attach token-looking tags. Authority UTXOs MAY be tracked in a future companion basket; they are out of scope for this profile’s list/send surface.

Outputs that fail eligibility MAY still appear if a buggy sender used the basket; receivers SHOULD re-check inscription / remittance fields and MUST NOT present them as spendable token value solely because of basket membership.

### Binary encoding ([BRC-162](/tokens/0162.md))

This profile’s normative remittance examples use the JSON carrier ([BRC-161](/tokens/0161.md)). Wallets that hold [BRC-162](/tokens/0162.md) binary value outputs MAY use the same basket name `bsv21` and the same tag / `customInstructions` field map (token id underscore string, decimal `amt` string). Encoding detection is local; the basket contract is shared.

### Outpoint and token id encoding

* BRC-100 wire `outpoint` fields use **dot** form: `txid.vout`.
* BSV-21 **token ids** use **underscore** form only: `txid_vout` ([BRC-161](/tokens/0161.md)) — in on-chain fields, in CI `id`, and after the `bsv21:` tag prefix.
* Writers MUST use underscore form for those token-id strings. Readers MUST still accept and normalize either outpoint form before comparison.
* Other outpoint-shaped values (e.g. CI / tag `icon`): writers SHOULD use underscore form; readers MUST normalize either form.

### Tags vs customInstructions

Both tags and `customInstructions` travel with the output under [BRC-46](/wallet/0046.md) / [BRC-37](/outpoints/0037.md). They serve different jobs. Writers SHOULD stay within **reference BRC-100 client** limits (`@bsv/sdk` / wallet-toolbox): tags ≤ **300** UTF-8 bytes each; `customInstructions` on `internalizeAction` basket insertion ≤ **1000** UTF-8 bytes.

|                 | **Tags**                                                                   | **`customInstructions`**                                                                                                |
| --------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Role            | **Exact-match filters** for `listOutputs` (`tags` / `tagQueryMode`)        | **Load-bearing remittance** for this tip: token fields apps read by key, plus **derivation** when this wallet locked it |
| Case            | Trimmed and **lowercased** before store/match ([BRC-100](/wallet/0100.md)) | JSON string values **preserve case**                                                                                    |
| Typical content | `bsv21:<tokenId>`, optional kind markers / issuer / BRC-164 `id:`          | `id`, `amt`, `op`, `sym`, `dec`, `icon`, and `protocolID` / `keyID` / `counterparty` when derived                       |

On-chain token fields remain defined by [BRC-161](/tokens/0161.md) / [BRC-162](/tokens/0162.md). CI is a **compact remittance object** for the tip — not a paste of the full inscription JSON (`p` and other wire-only keys need not appear).

**Placement:**

* **`customInstructions`** — token fields apps need without scanning tags or re-parsing script on every UI read: at least `id` and `amt`; `op`, `sym`, `dec`, `icon` when applicable; plus derivation when this wallet created the lock.
* **Tags** — values useful as **exact** `listOutputs` queries (primarily `bsv21:<tokenId>`). Optional kind / issuer / list-key tags as below.

**Reader priority** for token id, amount, and display metadata: `customInstructions` → tags (legacy / compat) → locking script / local history.

### Tags

Tags are optional BRC-46 / BRC-100 output tags used for **exact-match filtering**. Tag equality is **case-insensitive**. Writers SHOULD use a lowercase-safe alphabet (hex outpoints, ASCII markers).

| Tag                                | Requirement                                     | Meaning                                                                                                  |
| ---------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `bsv21:<tokenId>`                  | SHOULD                                          | Token id (`txid_vout` of deploy). Primary filter: “UTXOs of this token.”                                 |
| `bsv21:deploy`                     | SHOULD when applicable                          | Deploy output under this profile.                                                                        |
| `bsv21:auth`                       | SHOULD when applicable                          | Authority UTXO filed in this basket (if tracked here).                                                   |
| `op:<string>`                      | MAY                                             | Op for this output (`transfer`, `mint`, `deploy+mint`, …).                                               |
| `issuer:<pubkeyHex>`               | MAY when issuer is known                        | Issuer pubkey hex for filters. Verify Sigma on-chain for proof.                                          |
| `id:<string>`                      | SHOULD when adopting [BRC-164](/wallet/0164.md) | Per-output list key (wallet-local). Not the BSV-21 token id.                                             |
| `amt:<string>`                     | MAY                                             | Legacy / compat amount filter. Prefer CI `amt` for reads; exact tag match is rarely useful for balances. |
| `sym:<string>`                     | MAY                                             | Legacy lowercased symbol filter. Prefer CI `sym` for display.                                            |
| `dec:<string>` / `icon:<outpoint>` | MAY                                             | Legacy / compat mirrors of CI fields.                                                                    |

Unknown tags MUST be preserved when transporting the output ([BRC-37](/outpoints/0037.md)). Receivers that adopt BRC-164 stamp their own `id:` on import.

### Custom instructions ([BRC-37](/outpoints/0037.md))

When present for basket `bsv21`, `customInstructions` MUST be a **UTF-8 JSON object serialized as a string**.

Conforming writers SHOULD emit a **compact** object — load-bearing token fields plus derivation when this wallet locked the tip:

```json
{
  "id": "<txid_vout>",
  "amt": "<uint64 decimal string>",
  "op": "transfer",
  "sym": "<case-preserving ticker>",
  "dec": "<optional 0-18 string>",
  "icon": "<optional txid_vout>",
  "protocolID": [0, "onesat"],
  "keyID": "<wallet-defined>",
  "counterparty": "self"
}
```

| Field          | Type   | Requirement                | Meaning                                                                |
| -------------- | ------ | -------------------------- | ---------------------------------------------------------------------- |
| `id`           | string | SHOULD                     | Token id, underscore form ([BRC-161](/tokens/0161.md)).                |
| `amt`          | string | SHOULD                     | Amount in this UTXO (integer units as decimal string).                 |
| `op`           | string | SHOULD when known          | Value op for this output (`transfer`, `mint`, `deploy+mint`, …).       |
| `sym`          | string | SHOULD when known          | Display symbol (**case-preserving**).                                  |
| `dec`          | string | SHOULD when known          | Deploy decimals `0`–`18`.                                              |
| `icon`         | string | SHOULD when known          | Deploy icon outpoint (`txid_vout`). Pointer only — see **Icon media**. |
| `protocolID`   | array  | SHOULD when wallet-derived | BRC-43 protocol used to derive the locking key.                        |
| `keyID`        | string | SHOULD when wallet-derived | BRC-43 key id (only when that key locked the output).                  |
| `counterparty` | string | SHOULD when wallet-derived | BRC-43 counterparty (`self`, identity key, …).                         |

Additional JSON keys are permitted and MUST be ignored by readers that do not understand them. Readers MUST still store and forward the entire string unchanged ([BRC-37](/outpoints/0037.md)), subject to the reference client’s size limits.

When the tip is locked with a **literal** script or external address and this wallet has no derivation triple, omit derivation fields and still SHOULD carry `id` / `amt` / display fields the sender knows.

### Icon media (P2P — no content indexer)

BRC-161 `icon` is an **outpoint** of a prior image inscription (B-protocol / `ord` envelope), not a URL. Conforming remittance carries that pointer in CI `icon` (and MAY mirror a tag).

* **Writers SHOULD** include the icon inscription transaction in the **BEEF** that accompanies transfer / import (`inputBEEF`, AtomicBEEF subject parents, or equivalent known-tx set) whenever they know that transaction (minting wallet, prior holder with stored BEEF).
* **Receivers SHOULD** prefer decoding icon bytes from local BEEF / locking script (`ord` envelope field 0 + content-type) and MAY cache them keyed by the icon outpoint.
* Media bytes travel in BEEF (keep CI under the 1000-byte `internalizeAction` cap).
* When no icon outpoint is present, or icon bytes cannot be recovered locally, wallets MAY show a deterministic hash tile (identicon) from token id / `sym` — a UI fallback, not on-chain metadata.
* Content HTTP APIs remain an optional **recovery** aid when local BEEF is missing.

### Issuer attestation

`sym` is not unique. Meaningful tokens bind a **deploy** to an **issuer identity**.

* **Proof (normative when claimed):** the deploy transaction SHOULD carry a [Sigma](https://docs.sigmaidentity.com/) signature over the value tip, signed with the issuer’s identity key (prefer **BRC-77** so the pubkey is recoverable; **BSM** is acceptable when the remittance carries the matching compressed pubkey). This matches `js-1sat-ord` `deployBsv21Token({ signer: { idKey } })`.
* **Filter tag:** writers MAY set tag `issuer:<pubkeyHex>` for `listOutputs` filters.
* **Tags / CI are not proof.** Receivers SHOULD verify Sigma on the tip when present. **Unsigned deploys remain fully valid** value tips under this profile — issuer attestation is optional. When an issuer is shown, wallets SHOULD distinguish a Sigma-matched claim from a remittance-only claim; when no issuer is known, omit issuer UI rather than framing the tip as defective.
* Token id remains CI `id` / tag `bsv21:<tokenId>` / on-chain token id. Per-output list keys use [BRC-164](/wallet/0164.md) `id:`.
* Display SHOULD key by `(issuer, tokenId)` with `sym` as a nickname only.

#### Claims vs admission

* CI token fields and tags are **claims** for remittance / filter convenience.
* Token **admission** for a tip the wallet holds is local: valid script / history the wallet verifies for that tip. Global supply audits and indexers are optional.
* A wallet MUST NOT treat `amt` / token id as proven solely because they appear in CI or tags from an untrusted sender — prefer script parse and local history when trust matters.
* Issuers (especially authority-mint tokens) are trusted for mint policy under this profile.
* Issuer pubkey in tags is a claim until Sigma (or equivalent on-chain attestation) verifies.

### Non-plain locks (out of scope for this profile’s send path)

This profile’s normative hold / list / transfer path is **plain** owner-controlled BSV-21 value tips (inscription + P2PKH-style owner lock), matching common 1Sat / `js-1sat-ord` BSV-21 tooling.

**Cosigner-gated instruments** (e.g. MNEE) use issuer-specific APIs and a different signing flow. They are **not** intermixed with plain BSV-21 in the 1Sat SDK and are **not** standardized by this basket profile. Wallets that encounter a non-plain lock on a tip filed under `bsv21` MUST **fail closed** (named refuse such as `cosigner_required` / `unknown_lock`) and MUST NOT fall through to bare P2PKH `createAction`.

A separate instrument / cosigner profile MAY document those routes later. Remittance MUST NOT invent a generic “any cosigner” send path that pretends MNEE-style flows are ordinary BSV-21 transfers.

### Hold and list

Applications list held tips with BRC-100 `listOutputs`:

```json
{
  "basket": "bsv21",
  "includeTags": true,
  "includeCustomInstructions": true
}
```

UI SHOULD aggregate by token id and sum amounts from **CI** (`id` / `amt`), with tag / script fallbacks per [Reader priority](#tags-vs-custominstructions). Filter held tips of one token with tag `bsv21:<tokenId>`. Wallets MAY require basket permission per BRC-46 / BRC-100 before returning outputs.

### Transfer (send)

A conforming transfer of held `bsv21` value SHOULD use BRC-100 `createAction` with:

1. One or more **inputs** spending value tips (`satoshis === 1`) whose token id matches the token being sent (CI / script / tags).
2. One or more **outputs** with `satoshis: 1`, `basket: "bsv21"`, filter tags (at least `bsv21:<tokenId>`), and `customInstructions` with load-bearing fields (`id`, `amt`, …) plus derivation when this wallet locks the output.
3. Output amounts MUST NOT exceed input amounts for that token id under [BRC-161](/tokens/0161.md) conservation (excess burns). Change SHOULD return to the sender as another `bsv21` output when needed.
4. Prefer supplying a [BRC-176](/tokens/0176.md) validity packet (`inputBEEF` / Atomic BEEF) for spent tips when available so the receiver can prove those outs without an indexer.
5. When an icon outpoint is known for the token, SHOULD merge the **icon inscription transaction** into that BEEF so the receiver can decode ticker media offline (see Icon media).
6. Action `labels` MAY include `bsv21`; labels are non-normative for balances. Held spends MAY use [BRC-164](/wallet/0164.md) list keys in labels when a permission module is present; that is out of scope for this profile’s remittance rules.

Self-kept change SHOULD carry updated CI `amt` (and filter tag `bsv21:<tokenId>`) and derivation CI when the wallet derived the lock. Senders MUST NOT invent a different token id for the same economic tip.

### Import / receive (`internalizeAction`)

To place an existing value tip into basket `bsv21`, use BRC-100 `internalizeAction` with protocol `basket insertion`:

```json
{
  "tx": "<AtomicBEEF or BEEF bytes for the tip transaction>",
  "description": "Import BSV-21 token",
  "labels": ["bsv21"],
  "outputs": [{
    "outputIndex": 0,
    "protocol": "basket insertion",
    "insertionRemittance": {
      "basket": "bsv21",
      "tags": ["bsv21:<txid_vout>"],
      "customInstructions": "{\"id\":\"<txid_vout>\",\"amt\":\"<amt>\",\"op\":\"transfer\",\"sym\":\"DEMO\"}"
    }
  }]
}
```

Import CI MUST stay within the reference client’s **1000-byte** cap. Prefer CI for token fields; use tag `bsv21:<tokenId>` so later `listOutputs` can filter; parse the tip script when verifying admission. Derivation fields apply only if this wallet will unlock the tip with a known triple.

### Payment separation (wallet policy guidance)

Spending or revealing `bsv21` basket outputs is **not** a [BRC-29](/payments/0029.md) / default-basket payment. Conforming wallets SHOULD:

* Require distinct user authorization (or a dedicated token/basket grant) before `createAction` / `relinquishOutput` that spends `bsv21` outputs.
* Not treat a general “pay” or auto-pay grant as authorization to spend `bsv21` tips.
* Not fund ordinary payment outputs from `bsv21` basket UTXOs.
* Not mix `bsv21` value into basket `1sat` ([BRC-147](/tokens/0147.md)) or count it toward BSV balance views.

### Compatibility

* Wallets that do not implement this profile MUST still store and forward unknown baskets’ `customInstructions` and tags unchanged ([BRC-37](/outpoints/0037.md)).
* Basket `1sat` remains collectables only ([BRC-147](/tokens/0147.md)); BSV-21 value MUST NOT be filed there.
* Deprecated tick-based BSV-20 inventory is out of scope.
* Fine-grained [BRC-99](/wallet/0099.md) permission schemes for `bsv21` (e.g. `p bsv21 …` view/spend modules) are **out of scope** for this profile. This document is the plain basket / remittance contract only.

## Security considerations

* **Indexer trust** — Display MUST NOT depend on HTTP content indexers for `icon` when BEEF was supplied. Indexers are optional recovery only (see Icon media).
* **Remittance spoofing** — CI `id` / `amt` / `sym` and filter tags are forgeable. Receivers SHOULD verify tip script / history for outs they accept; do not treat remittance alone as admission.
* **Trusted issuer** — This profile does not require wallets to prove a global supply cap. Over-mint by an authority issuer is an issuer/policy risk accepted by holders of that token.
* **Symbol collision** — `sym` is not unique; always key balances by token id.
* **Basket pollution** — Non-value or multi-sat outs in `bsv21` confuse list UIs; re-check `satoshis` and inscription rules.
* **Over-transfer burn** — Building outputs whose `amt` sum exceeds *spent inputs* burns value ([BRC-161](/tokens/0161.md)). Local send paths SHOULD conserve the sender's inputs.
* **Non-plain lock bypass** — Unlocking a cosigner-gated tip with a plain P2PKH path fails on-chain and MUST be refused in the wallet. Do not treat MNEE-class instruments as ordinary `bsv21` sends.
* **CI / tag limits** — Oversized `customInstructions` on `internalizeAction` is rejected by the reference SDK (1000-byte cap). Case-sensitive data in tags alone is lost to lowercasing.

## Implementations

* **HandCash Desktop** (reference, in progress): basket `bsv21` list / import under Collect (not Pay); plain BSV-21 tips; non-plain locks refuse rather than fall through; ticker icons from local BEEF / mint cache (not HTTP content APIs).\
  Source: `src/wallet/bsv21.ts`, `src/wallet/fungibles.ts`, `src/wallet/tokenIconCache.ts` in [HandCash/HANDCASH-DESKTOP](https://github.com/HandCash/HANDCASH-DESKTOP).
* **HandCash Cloud**: MNEE remains a **separate** Pay / cosigner instrument path (`mneeCosignerService`, `mneeFactory`) — not the plain BRC-163 send surface.

## References

1. [BRC-161](/tokens/0161.md) — BSV-21 Fungible Tokens (JSON / Legacy)
2. [BRC-162](/tokens/0162.md) — BSV-21 Fungible Tokens (Binary)
3. [BRC-176](/tokens/0176.md) — BSV-21 Validity Proofs
4. [BRC-147](/tokens/0147.md) — 1Sat Ordinals Basket Profile
5. [BRC-164](/wallet/0164.md) — Output Identity Tags (`id:`)
6. [BRC-37](/outpoints/0037.md) — Basket and Custom Instructions
7. [BRC-46](/wallet/0046.md) — Output Baskets
8. [BRC-100](/wallet/0100.md) — Wallet-to-Application Interface
9. [BRC-159](/tokens/0159.md) / [BRC-160](/tokens/0160.md) — 1Sat carrier (not FT admission)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://bsv.brc.dev/tokens/0163.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
