Beersy
BRC-163

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

Fungible tokens on BSV (like a stablecoin or a points system) live as tiny special outputs mixed in with a wallet's ordinary money and its collectible items. Without agreement between wallets on where to keep these outputs and what information to attach to them, one wallet's tokens look like spendable cash or junk collectibles to another wallet, balances can't be reliably totaled, and sending a token to someone else risks losing the details they need to use it. This lets independent wallets hold, list, send and receive the same of token and understand each other correctly.

Brandon Cryderman / HandCashchanged 28 Aug 202612 min read

Reference for an AI

Everything an assistant needs to answer questions about BRC-163 accurately, including what it depends on.

Summary

Why
Independent wallets need a shared, agreed-upon place and format for fungible token outputs so they don't confuse them with spendable money, collectibles, or each other's incompatible conventions.
What
BRC-163 defines a standard wallet "" named bsv21 and a set of tags and custom instructions for holding, listing, transferring and importing BSV-21 fungible token outputs under the BRC-46 and BRC-100 .
How
A wallet files eligible 1-satoshi BSV-21 value outputs under the basket bsv21, tags them like bsv21:<tokenId> for exact-match filters, and stores compact JSON in (id, amt, op, sym, dec, icon, and derivation fields) so balances can be listed by token id and transfers via or…

What this lets you do

  • Hold BSV-21 token outputs in a dedicated bsv21 basket separate from cash and collectibles
  • List and sum token balances by token id using listOutputs filters and customInstructions
  • Transfer token value with createAction while preserving remittance data and derivation for the receiver
  • Import an existing token via internalizeAction with proper basket insertion
  • Refuse to spend cosigner-gated tokens instead of silently failing on-chain

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

walletbsv21 baskettoken output

The specification

Abstract

This BRC defines the application basket profile for BRC-161 BSV-21 fungible value outputs under BRC-46 / BRC-100. It reserves the name bsv21, specifies eligibility, documents tags for listOutputs filters, BRC-37 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 (JSON) and BRC-162 (binary).

Trust model: holders verify their (local history / remittance / 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 / BRC-162 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 for collectables.

Specification

Basket identifier

  • The basket name is the UTF-8 string bsv21.
  • Per BRC-46, 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 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 / BRC-160).
  2. The output is a BSV-21 value output under BRC-161: 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)

This profile’s normative remittance examples use the JSON carrier (BRC-161). Wallets that hold BRC-162 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) — 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 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 / BRC-37. 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.

TagscustomInstructions
RoleExact-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
CaseTrimmed and lowercased before store/match (BRC-100)JSON string values preserve case
Typical contentbsv21:<tokenId>, optional 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 / BRC-162. 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) → / 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).

TagRequirementMeaning
bsv21:<tokenId>SHOULDToken id (txid_vout of deploy). Primary filter: “UTXOs of this token.”
bsv21:deploySHOULD when applicableDeploy output under this profile.
bsv21:authSHOULD when applicableAuthority filed in this basket (if tracked here).
op:<string>MAYOp for this output (transfer, mint, deploy+mint, …).
issuer:<pubkeyHex>MAY when issuer is knownIssuer pubkey hex for filters. Verify Sigma on-chain for proof.
id:<string>SHOULD when adopting BRC-164Per-output list key (wallet-local). Not the BSV-21 token id.
amt:<string>MAYLegacy / compat amount filter. Prefer CI amt for reads; exact tag match is rarely useful for balances.
sym:<string>MAYLegacy lowercased symbol filter. Prefer CI sym for display.
dec:<string> / icon:<outpoint>MAYLegacy / compat mirrors of CI fields.

Unknown tags MUST be preserved when transporting the output (BRC-37). Receivers that adopt BRC-164 stamp their own id: on import.

Custom instructions (BRC-37)

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:

{
  "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"
}
FieldTypeRequirementMeaning
idstringSHOULDToken id, underscore form (BRC-161).
amtstringSHOULDAmount in this UTXO (integer units as decimal string).
opstringSHOULD when knownValue op for this output (transfer, mint, deploy+mint, …).
symstringSHOULD when knownDisplay symbol (case-preserving).
decstringSHOULD when knownDeploy decimals 018.
iconstringSHOULD when knownDeploy icon outpoint (txid_vout). Pointer only — see Icon media.
protocolIDarraySHOULD when wallet-derivedBRC-43 protocol used to derive the locking key.
keyIDstringSHOULD when wallet-derivedBRC-43 (only when that key locked the output).
counterpartystringSHOULD when wallet-derivedBRC-43 (self, , …).

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), 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, 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 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 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 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:

{
  "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. 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 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, BRC-95).
  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; are non-normative for balances. Held spends MAY use BRC-164 list keys in labels when a 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:

{
  "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 / 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) 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).
  • Basket 1sat remains collectables only (BRC-147); BSV-21 value MUST NOT be filed there.
  • Deprecated tick-based BSV-20 inventory is out of scope.
  • Fine-grained BRC-99 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 collisionsym 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). 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.
  • HandCash Cloud: MNEE remains a separate Pay / cosigner instrument path (mneeCosignerService, mneeFactory) — not the plain BRC-163 send surface.

References

  1. BRC-161 — BSV-21 Fungible Tokens (JSON / Legacy)
  2. BRC-162 — BSV-21 Fungible Tokens (Binary)
  3. BRC-147 — 1Sat Ordinals Basket Profile
  4. BRC-164 — Output Identity Tags (id:)
  5. BRC-37 — Basket and Custom Instructions
  6. BRC-46 — Output Baskets
  7. BRC-100
  8. BRC-159 / BRC-160 — 1Sat carrier (not FT admission)
Was this helpful?

Search Beersy

Search standards by number, title, author or topic