diff --git a/tokens/0163.md b/tokens/0163.md index d96b2ff7..f4dfe321 100644 --- a/tokens/0163.md +++ b/tokens/0163.md @@ -4,7 +4,7 @@ Brandon Cryderman / HandCash (brandongcryderman@gmail.com) ## Abstract -This BRC defines the **application basket profile** for [BRC-161](./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 the tag vocabulary and [BRC-37](../outpoints/0037.md) `customInstructions` schema used for display and remittance, and describes normative hold / list / transfer / import flows. +This BRC defines the **application basket profile** for [BRC-161](./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](./0161.md) (JSON) and [BRC-162](./0162.md) (binary). @@ -16,8 +16,8 @@ It does **not** redefine BSV-21 token economics (deploy, auth, mint, transfer, b 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 `id` / `amt` / display claims for the receiver. -4. Import historical tips via `internalizeAction` with interoperable remittance. +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](./0147.md) for collectables. @@ -48,111 +48,110 @@ This profile’s normative remittance examples use the JSON carrier ([BRC-161](. ### 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](./0161.md)). -* Inside this profile’s `customInstructions`, the BSV-21 token field `id` MUST use underscore form. -* The output tag for token id is `bsv21:` (see Tags). After the `bsv21:` prefix, tags MAY use either outpoint form; readers MUST normalize before comparison. -* Tip outpoints in other tags MAY use either form; readers MUST normalize. +* BSV-21 **token ids** use **underscore** form only: `txid_vout` ([BRC-161](./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. Profiles MUST NOT recommend shapes that fail in the **reference BRC-100 client** (`@bsv/sdk` wallet validation / wallet-toolbox storage): +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 | Query / filter keys (`listOutputs` + `tags` / `tagQueryMode`) | Remittance object: token fields, display, optional spend metadata | -| Case | Reference validation **trims and lowercases** each tag before store/match ([BRC-100](../wallet/0100.md)) | JSON string values **preserve case** | -| Size | Each tag ≤ **300 UTF-8 bytes** (`OutputTagStringUnder300Bytes`) | `internalizeAction` basket insertion caps the string at **1000 UTF-8 bytes**. `createAction` outputs are not capped the same way in current SDK validation, but writers SHOULD still keep CI lean so import/re-file paths succeed | -| Authority | Non-authoritative claims / local metadata | Same for display; inscription / local history admit value | +| 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:`, optional kind markers / issuer / BRC-164 `id:` | `id`, `amt`, `op`, `sym`, `dec`, `icon`, and `protocolID` / `keyID` / `counterparty` when derived | -**Writers SHOULD:** +On-chain token fields remain defined by [BRC-161](./0161.md) / [BRC-162](./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). -* Put **filterable** facts in tags: bare `bsv21`, `bsv21:`, `amt:<…>`, and when useful `op:<…>`. -* Put **case-preserving display** (`sym`) and load-bearing token fields (`id`, `amt`, `op`, `dec`, `icon`) in `customInstructions`. -* Prefer a short CI object on `internalizeAction` (well under 1000 bytes). Do not embed large proofs, BEEF, or media **bytes** in CI for this profile — carry icon media in accompanying BEEF (see Icon media). +**Placement:** -**Writers SHOULD NOT:** +* **`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:`). Optional kind / issuer / list-key tags as below. -* Rely on a `sym:` tag as the primary display symbol (lowercasing destroys intended case; use CI `sym`). -* Use tag prefix `id:` for the BSV-21 token id (see Tags). -* Stuff oversized blobs into CI and expect `internalizeAction` to accept them. - -Readers resolving a display symbol SHOULD prefer `customInstructions.sym` when present, and MAY fall back to a `sym:` tag. +**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 filtering and display hints. They are **non-authoritative for balances**. Because the reference client lowercases tags, writers MUST treat tag equality as **case-insensitive** and SHOULD write tags in a lowercase-safe alphabet (hex outpoints, decimal `amt`, ASCII ops). +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` | SHOULD on conforming transfers and imports | Marks the output as a BSV-21 value tip under this profile (no suffix). | -| `bsv21:` | SHOULD when known | Token id (`txid_vout` of deploy). Distinct from the bare `bsv21` marker. | -| `amt:` | SHOULD when known | Token amount in this output (decimal string, integer units). | -| `op:` | MAY | Last known op (`transfer`, `mint`, `deploy+mint`, …). | -| `sym:` | MAY (legacy / filter only) | Lowercased symbol hint. New writers SHOULD put display symbol in `customInstructions.sym` instead. | -| `issuer:` | SHOULD when issuer is known | Compressed issuer identity pubkey (hex). Filter mirror only — verify Sigma on-chain. | -| `id:` | MAY | Wallet-local per-output list key (see below). **Not** the BSV-21 token id. | - -Conforming writers MUST NOT use `id:<…>` for the BSV-21 token id. The tag prefix `id:` is reserved for a **per-output** identity / list key across the wallet stack (so a specific output can be found without scanning every basket row). Generation is wallet-defined; it is not global asset identity. Token id in inscription / `customInstructions` JSON remains the BRC-161 field name `id`. - -Unknown tags MUST be preserved when transporting the output ([BRC-37](../outpoints/0037.md)). +| `bsv21:` | 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:` | MAY | Op for this output (`transfer`, `mint`, `deploy+mint`, …). | +| `issuer:` | MAY when issuer is known | Issuer pubkey hex for filters. Verify Sigma on-chain for proof. | +| `id:` | SHOULD when adopting [BRC-164](../wallet/0164.md) | Per-output list key (wallet-local). Not the BSV-21 token id. | +| `amt:` | MAY | Legacy / compat amount filter. Prefer CI `amt` for reads; exact tag match is rarely useful for balances. | +| `sym:` | MAY | Legacy lowercased symbol filter. Prefer CI `sym` for display. | +| `dec:` / `icon:` | 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 (plain BSV-21 value tips): +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 { - "p": "bsv-20", - "op": "transfer", "id": "", "amt": "", - "sym": "", + "op": "transfer", + "sym": "", + "dec": "", "icon": "", - "dec": "" + "protocolID": [0, "onesat"], + "keyID": "", + "counterparty": "self" } ``` | Field | Type | Requirement | Meaning | |-------|------|-------------|---------| -| `p` | string | SHOULD | `bsv-20` (legacy protocol id per [BRC-161](./0161.md)). | -| `op` | string | SHOULD | Value op for this output (`transfer`, `mint`, or `deploy+mint`). | -| `id` | string | SHOULD | Token id, underscore form. | +| `id` | string | SHOULD | Token id, underscore form ([BRC-161](./0161.md)). | | `amt` | string | SHOULD | Amount in this UTXO (integer units as decimal string). | -| `sym` | string | SHOULD when known | Display symbol (case-preserving; not unique). Preferred over a `sym:` tag. | -| `icon` | string | MAY | Deploy icon outpoint (`txid_vout`). Pointer only — see **Icon media**. | -| `dec` | string | MAY | Deploy decimals `0`–`18` as a decimal string. | -| `issuer` | string | SHOULD when known | Compressed issuer identity pubkey hex (33-byte). Remittance **mirror** of on-chain attestation — not the proof. | +| `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. -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 above. +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. +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 MUST NOT** require receivers to fetch icon bytes from an HTTP content API (Gorilla, 1Sat content host, or similar) in order to display the ticker. * **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 MUST** prefer decoding icon bytes from local BEEF / locking script (`ord` envelope field 0 + content-type) and MAY cache them keyed by the icon outpoint. -* **`customInstructions` MUST NOT** embed raw image bytes or large base64 (CI stays under the 1000-byte `internalizeAction` cap). The outpoint in CI remains the stable pointer; media travels in BEEF. -* When no icon outpoint is present, or icon bytes cannot be recovered locally, wallets MAY show a deterministic hash tile (identicon) from `id` / `sym` — that is a UI fallback, not on-chain metadata. -* Content HTTP APIs remain an optional **recovery** aid for wallets that lost local BEEF; they are not part of the normative remittance contract. +* **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 } })`. -* **Remittance mirror:** writers SHOULD set `customInstructions.issuer` to that compressed pubkey hex and MAY set tag `issuer:` (lowercase-safe) for `listOutputs` filters. -* **CI/tags 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. -* Do **not** use tag `id:` for issuer or for the BSV-21 token id. Token id remains `bsv21:` / CI field `id`. +* **Filter tag:** writers MAY set tag `issuer:` 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:` / 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 -* Tags and `customInstructions` fields are **claims** for display and remittance convenience. -* Token **admission** for a tip the wallet holds is local: valid inscription / remittance fields plus any history the wallet verifies for that tip. Global supply audits and indexers are optional. -* A wallet MUST NOT treat `amt` / `id` as proven solely because they appear in remittance from an untrusted sender. +* 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 remittance is a claim until Sigma (or equivalent on-chain attestation) verifies. +* 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) @@ -174,20 +173,20 @@ Applications list held tips with BRC-100 `listOutputs`: } ``` -UI SHOULD aggregate by `id` (sum `amt`) for balances. Wallets MAY require basket permission per BRC-46 / BRC-100 before returning outputs. +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:`. 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 claimed `id` matches the token being sent. -2. One or more **outputs** with `satoshis: 1`, `basket: "bsv21"`, tags per this profile, and `customInstructions` as above with `op: "transfer"`, the same `id`, and the output `amt`. -3. Output amounts MUST NOT exceed input amounts for that `id` under [BRC-161](./0161.md) conservation (excess burns). Change SHOULD return to the sender as another `bsv21` output when needed. +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:`), 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](./0161.md) conservation (excess burns). Change SHOULD return to the sender as another `bsv21` output when needed. 4. Prefer supplying `inputBEEF` / BEEF for spent tips when available so the receiver can validate the spend graph ([BRC-62](../transactions/0062.md), [BRC-95](../transactions/0095.md)). -5. When `customInstructions.icon` (or deploy `icon`) is set, 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. +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. -Senders that rebuild `customInstructions` MUST preserve `id` / `amt` / display fields they still know (including `icon` when known), and MUST NOT invent a different `id` for the same physical tip. +Self-kept change SHOULD carry updated CI `amt` (and filter tag `bsv21:`) 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`) @@ -203,14 +202,14 @@ To place an existing value tip into basket `bsv21`, use BRC-100 `internalizeActi "protocol": "basket insertion", "insertionRemittance": { "basket": "bsv21", - "tags": ["bsv21", "bsv21:", "amt:"], - "customInstructions": "{\"p\":\"bsv-20\",\"op\":\"transfer\",\"id\":\"\",\"amt\":\"\",\"sym\":\"DEMO\"}" + "tags": ["bsv21:"], + "customInstructions": "{\"id\":\"\",\"amt\":\"\",\"op\":\"transfer\",\"sym\":\"DEMO\"}" } }] } ``` -Import CI MUST stay within the reference client’s **1000-byte** `customInstructions` cap. +Import CI MUST stay within the reference client’s **1000-byte** cap. Prefer CI for token fields; use tag `bsv21:` 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) @@ -226,11 +225,12 @@ Spending or revealing `bsv21` basket outputs is **not** a [BRC-29](../payments/0 * 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](./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** — `id` / `amt` / `sym` in CI are forgeable. Receivers SHOULD verify tip history for outs they accept; do not treat remittance alone as admission. +* **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. @@ -249,7 +249,8 @@ Spending or revealing `bsv21` basket outputs is **not** a [BRC-29](../payments/0 1. [BRC-161](./0161.md) — BSV-21 Fungible Tokens (JSON / Legacy) 2. [BRC-162](./0162.md) — BSV-21 Fungible Tokens (Binary) 3. [BRC-147](./0147.md) — 1Sat Ordinals Basket Profile -4. [BRC-37](../outpoints/0037.md) — Basket and Custom Instructions -5. [BRC-46](../wallet/0046.md) — Output Baskets -6. [BRC-100](../wallet/0100.md) — Wallet-to-Application Interface -7. [BRC-159](./0159.md) / [BRC-160](./0160.md) — 1Sat carrier (not FT admission) +4. [BRC-164](../wallet/0164.md) — Output Identity Tags (`id:`) +5. [BRC-37](../outpoints/0037.md) — Basket and Custom Instructions +6. [BRC-46](../wallet/0046.md) — Output Baskets +7. [BRC-100](../wallet/0100.md) — Wallet-to-Application Interface +8. [BRC-159](./0159.md) / [BRC-160](./0160.md) — 1Sat carrier (not FT admission)