Abstract
This document is a compact output format for Bitcoin Native Tokens, both fungible tokens and one-of-a-kind items. A recipient checks every hop back to the deploy, so every output's bytes are paid again in every later proof. This format keeps them small: a transfer with one input, a payment and change is 452 bytes, against 492 for the same transfer as a BRC-162 token with P2PKH (§12).
Every asset it defines passes the six BNT tests of draft BRC-251 (#320). Its amount is in the locking script, the holder's signature alone moves it, and a recipient checks it from transaction bodies, so no issuer, indexer or overlay takes part in validity. A sender ships only the hops the recipient has not already checked. Draft BRC-252 (#321) replaces long histories with step proofs, trustless checkpoints that the recipient checks itself.
Issuer-Permissioned Tokens (IPT), BRC-251's other class, use this layout, payload, proofs and filing tags under their own marker, ipt, with a clause their issuer countersigns. Draft BRC-253 (#333) specifies them.
Motivation
Because a BNT recipient sees every transaction back to the deploy, the size of each output and each proof is the cost of being native. BRC-162 outputs repeat a 36-byte token id, and every P2PKH input carries the holder's public key. A proof therefore grows faster than it needs to with each hop, and a consolidation pulls in the history of every input it spends.
This format keeps the BNT rules and changes the bytes:
- Its own prefix. A 3-byte marker and a 20-byte asset id replace the BRC-162 prefix.
- The holder key in the lock. The unlock carries only the signature.
- Issuance in the format. Fixed, open and capped supply, collections and limited editions are rules of the format, checked on the walk like any transfer.
Relationship to other documents
| Concern | Document |
|---|---|
| What makes a token a Bitcoin Native Token, shared terms, wallet duties | Draft BRC-251 (#320) §1, Terms, §4 |
IPT: this layout under the marker ipt with a countersign clause |
Draft BRC-253 (#333) |
| Wallet interface: keys, signatures, building and recording transactions | BRC-100 |
| Transaction encoding in proofs | BRC-30 Extended Format |
| Recording a receipt | BRC-95 Atomic BEEF, passed to BRC-100 internalizeAction |
| Finality | BRC-67 SPV |
| Key derivation | BRC-42, BRC-43 |
| Store-and-forward delivery | BRC-33, sealed per BRC-78; live channel draft BRC-246 (#248) |
| Remittance fields | BRC-37 customInstructions |
| Baskets, scoped access, row keys, spend labels | BRC-46, BRC-99, BRC-123, BRC-164, BRC-111 |
| Payload encoding | DAG-CBOR (IPLD) |
MUST, MUST NOT, SHOULD and MAY express requirements for conforming implementations.
Terms
BRC-251's terms apply, with the marker bnt. This document fixes three of them:
- Asset id. The deploy txid in internal byte order, then the deploy output's index as one byte, hashed with
HASH160. - Issuer key. An asset has one issuer key, or a set of
mofnkeys (§3). An issuer key never takes part in a holder's transfer. - Holder key. Derived per §8.
Specification
1. Locking scripts
The marker is the three bytes 62 6e 74 ("bnt"). Amounts are minimally encoded script numbers. A deploy, a held output and an authority output are:
deploy: <marker> <supply> <payload> OP_2DROP OP_DROP <clause>
held: <marker ‖ asset id> <amount> OP_2DROP <clause>
authority: <marker ‖ asset id> OP_0 OP_2DROP <clause>
capped: <marker ‖ asset id> OP_0 <units issued> <members issued> OP_2DROP OP_2DROP <clause>
A capped asset's authority (§4) uses the second authority form. Every other authority uses the first. units issued is a positive script number and members issued a script number of zero or more.
The first push of a held or authority output is exactly 23 bytes, and a deploy's is exactly 3. BRC-162 starts with a 36-byte push or OP_0, so neither format parses as the other. A deploy's supply is a positive script number. Every output of an asset carries at least 1 satoshi, and the satoshi has no meaning.
The clause of every output of a BNT asset is:
<holder key> OP_CHECKSIG
It is unlocked by <holder signature>, under any sighash type. A BRC-100 wallet makes such a signature with createSignature, hashToDirectlySign set to the double SHA-256 of the input's sighash preimage (BIP-143 with FORKID) under that sighash type, for the key's protocol, key ID and counterparty, and passes the unlocking script to signAction. No key leaves the wallet. A mint authority is held by its issuer, so spending it needs the issuer (§4, §5). No output has a branch for anyone but its holder.
A wallet MUST parse a clause exactly and re-encode it. Only the canonical encoding counts. An output under this marker with any other clause is not a BNT output, and a reader treats it as no holding.
2. Deploy
A deploy transaction writes one or more deploy outputs. The asset id of each one is its own outpoint, hashed as in Terms, so one transaction can deploy up to 256 assets, such as the items of a collection.
The payload is a DAG-CBOR map:
| Key | Type | Meaning |
|---|---|---|
v |
uint | 1 |
mode |
text | "open" |
kind |
text | "token" or "item"; absent means "token" |
iss |
bytes or array of bytes | The issuer identity key, or one per cosigner, 33 bytes each (§3) |
m |
uint | The threshold of a shared set; absent means 1 |
auth |
bool | true makes the deploy output a mint authority (§4) |
max |
uint | With auth: the most units that may ever exist, supply included; at most 2^64−1 (§4) |
size |
uint | With auth: the most member items the collection may ever deploy (§5) |
sym, name |
text | Display symbol and name |
dec |
uint | Decimal places, at most 18; tokens only |
icon |
array | [media type, bytes] |
ctype, c |
text, bytes | An item's media type and content |
col |
bytes | An item's collection asset id, 20 bytes (§5) |
hdl |
text | The issuer's BRC-169 handle |
crit |
array of text | Keys a reader MUST understand; a reader that does not know one MUST refuse the asset |
- An item has
kind"item",supply1, nodec, noauth, nomaxand nosize. maxandsizeneedauth. A deploy'ssupplyMUST NOT exceedmax.- The payload MUST NOT contain a URL. Issuers are reached by identity key.
modeMUST be"open". A reader MUST refuse an asset whosemodeis anything else.- The deploy output's clause is the clause of §1.
- The deploy is an asset only if the issuer signed it (§3).
A reader holds the deploy transaction once per asset and keeps it only when its txid and index hash to the asset id.
3. Issuer keys
The issuer key is not the identity key itself. It is the BRC-42 child of the identity key named in iss, for protocol [2, "bnt issuer"], key ID 1 and counterparty anyone. Anyone can derive it from iss, so the asset names its issuer without an extra signature. The issuer reaches it through BRC-100 getPublicKey and createSignature with those arguments. A shared set derives one key per entry of iss, in order.
A deploy transaction MUST have an input signed ALL | FORKID by issuer keys: an input that spends an output locked <issuer key> OP_CHECKSIG, or m of the set's keys in OP_CHECKMULTISIG form. The issuer usually funds the deploy from such a coin. Without that signature a deploy is not an asset. Otherwise anyone could write someone else's identity key into a payload and circulate an asset in its name. The issuer's signature is part of the deploy transaction, so checking it is checking a transaction body (BNT test 2).
4. Transfers and minting
These rules apply to each asset id that a transaction writes or spends. They are read from the claimed spent scripts in Extended Format.
- Closure. Every output carrying the asset id uses the clause of §1, with any holder key. A wallet MUST treat an output with the asset id and another clause as not a holding of the asset.
- Conservation. Amounts on the outputs sum exactly to amounts on the inputs. An input that spends a deploy output counts that deploy's
supply. - Minting. A transaction that spends an authority, which is a deploy output with
author an authority output, MAY write more than it spends. It spends at most one authority and writes at most one authority output, and writing none ends minting. - Items. An item's outputs total exactly 1.
- No conjuring. Every asset id on an output is on an input, except in the asset's own deploy.
One transaction MAY carry several assets.
Burning. Conservation keeps every unit in an output, so a holder burns units or an item by transferring them to the holder key 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0. It is the BIP-341 point whose x-coordinate is the SHA-256 of the generator, so no one knows its private key. Burned units still count in supply, and a wallet SHOULD show them as burned rather than in circulation.
Supply policy. The deploy fixes how many units can ever exist:
| Policy | Payload | Units ever |
|---|---|---|
| Fixed | no auth |
supply |
| Open | auth, no max or size |
unbounded |
| Capped | auth with max, size or both |
at most max; at most size members |
A capped asset's authority states what has been issued so far. Spending its deploy output counts as supply units and 0 members issued. A transaction that spends a capped authority issues units = before + (written − spent) and members = before + m, where m counts the deploy outputs in the transaction whose payload names the asset in col. Both totals MUST stay within max and size, even when the transaction writes no authority output. An authority output it does write MUST state exactly those totals, and an authority of an asset that is not capped MUST NOT state any. A reader checks each step from the spent authority alone. The walk in §7 reaches every earlier authority, so no step can understate what came before.
5. Collections
A collection is an asset whose deploy has auth. A member deploy names it with col and spends the collection's authority, the deploy output or a later authority output, writing the next authority output. Spending an authority needs the issuer, so copying a payload does not make a member. A reader checks membership from the member's deploy transaction. A batch deploy (§2) adds many members in one transaction.
A collection with size is a limited edition: its authority counts members (§4), and once size members exist no transaction that spends the authority may deploy another. Dropping the authority closes a collection at whatever count it has.
6. Proof packages
A recipient receives a proof package:
{ "v": 1, "subject": "<txid>", "txs": ["<Extended Format, hex>", "…"], "deploys": ["<Extended Format, hex>"] }
txs holds the subject first, then the ancestors §7 needs, each as Extended Format. deploys carries the deploy transactions the recipient does not hold, also as Extended Format, because checking the issuer's signature (§3) needs the script and satoshis of the output the deploy spends. It MAY be empty. A sender SHOULD omit transactions it has already sent the same recipient, and the recipient MAY answer { "t": "need", "txids": [...] }, or fetch a missing transaction from the chain and link its sources itself.
Extended Format only claims what each input spends. A recipient that holds the transaction behind an input MUST check the claimed script and satoshis against that transaction's output. A signature covers the claim, so an unmined transaction could otherwise claim more than its parent wrote.
7. Validity
A recipient validates the outputs of transaction T for asset A:
- It checks
Tagainst §4, and checks every input ofTthat spends an output ofAby script validation against its claimed script and satoshis. - It stops if
TisA's deploy, signed by the issuer as §3 requires. - Otherwise it validates, in the same way, the transaction behind each input of
Tthat spends an output ofA. Those transactions come from the package or from what it has already validated.
A recipient records every transaction it validated, and later walks also stop there. A walk MAY also stop at a transaction whose step proof the recipient accepts under draft BRC-252 §4. It MUST NOT stop at any other point: not at a mined transaction, an indexer's listing, or a signature by the issuer or anyone else. It credits an output only when that output's transaction validated. It MUST NOT credit an output of a deploy that pays it directly.
Every walk therefore reaches the deploy, or a step proof the recipient checks itself, and trusts no one for supply, not even the issuer. A sender ships only the hops the recipient has not validated (§6), so a recipient that has seen an asset before receives only the new hops.
Finality. A transfer, like any Bitcoin payment, is final once mined, proven by SPV (BRC-67) against block headers (BNT test 5). A recipient MAY credit it on miner acceptance, showing it as unconfirmed until mined.
8. Holder keys
A holder key is the BRC-42 child for protocol [2, "bnt holder"], key ID <derivation prefix> <derivation suffix>, with the holder's identity key as counterparty, or the sender's own identity key for an output it keeps (BRC-100 self derives the same key). The holder reaches it through BRC-100 getPublicKey and createSignature. The prefix is fresh per transfer and the suffix per output. No two outputs share a holder key, so the chain does not link a holder's outputs to each other or to its identity.
A delivery carries the remittance of the recipient's outputs:
{ "v": 1, "t": "transfer", "asset": "<asset id>", "senderIdentityKey": "<66 hex>", "outputs": [{ "vout": 0, "derivationPrefix": "…", "derivationSuffix": "…" }], "proof": <§6 package>, "beef": "<optional BRC-95 Atomic BEEF of the subject, base64>" }
beef is what the recipient passes to BRC-100 internalizeAction (§9). A sender SHOULD include it when it fits the transport. A recipient without it builds one from the package and the chain. It never affects validity, which is §7 alone. The delivery is sealed per BRC-78 to the recipient's box bnt or sent on a BRC-246 channel. The transport never affects validity (BNT test 6). A recipient acknowledges it only after it filed the outputs or found the transfer invalid.
9. Filing
A recipient records the outputs it validated with BRC-100 internalizeAction, protocol basket insertion, into its filing, with the tags and customInstructions this section defines. An asset files by kind, which never changes: a token in bntft, an item in bntnft. Both names follow the BRC-100 basket rules. After BRC-46 normalization, conforming use MUST be exactly these.
- Tag
bntmarks a row of this format, and tag<filing>:<assetId>with the 40-hex asset id MUST be present on every held output and authority. A reader tells formats apart by thebnttag and the asset id length, never by the filing name. A deploy output has no asset id until its transaction is signed, so it carries<filing>:deployand a reader derives the id from its outpoint (§3). Tagamt:<units>gives a held output's amount. Tagbnt-authoritymarks an authority, which never counts in a balance. Tagcollection:<collectionId>marks a member whose membership checked out,creator:<issuerKey>names the first issuer, andsym:/name:carry display fields when present. Each output carries a BRC-164id:<key>tag. - Code written for another format sharing a filing MUST NOT spend or heal a row of this format. An output of this format spent outside §4 is burned.
customInstructions, when present, is a UTF-8 JSON object holdingasset(absent on a deploy output),kind,mode("open"),form(held,authorityordeploy),amt, andsym,dec,nameandcolwhen known. It also holdsderivationPrefix,derivationSuffixandsenderIdentityKey, which are the only way back to the holder key. A wallet MUST keep them and include them in its history backup.
10. Application access
The filings are viewed through their BRC-99 permission schemes (BRC-123, Category 1, Asset):
| Capability | Wire |
|---|---|
| View all | listOutputs basket p <filing> all |
| View by asset (tokens) | p bntft token with bntft:<assetId> tags |
| View by collection | p bntnft collection with collection:<collectionId> tags |
| View by creator | p bntnft creator with creator:<issuerKey> tags |
| Look up a row | p <filing> id with id:<key> tags |
- A wallet MUST refuse any other scope, a filtered scope without a matching tag, and any request that names a filing directly.
- A wallet MAY cover
bntnftwith the same grant as its other item formats, so one item permission shows every item. A view of one format MUST NOT return another format's rows. - An output of this format is spent only through the wallet, which builds the transfer, the package and the remittance (§6, §8). An application never receives a holder signature, and MUST NOT write a script of this format or a filing above through its own action.
- Payment, auto-pay and other schemes' grants MUST NOT cover these views or sends.
11. Reissuing assets from other formats (informative)
An issuer moving an asset off another token format deploys it under this format. Each holder then sends its old outputs to the issuer with a { "t": "convert" } request in box bnt. The issuer pays the same amount in the new asset and burns or keeps what it received. A wallet MAY show old outputs read-only until they are converted. Nothing in this document reads another format.
12. Efficiency (informative)
The table measures one transfer: one asset input, a recipient output and change, and one funding input. Signatures are counted at 72 bytes. "Output life" is the bytes an output adds when created plus when spent.
| Lock | Unlock | Transfer | Output life | |
|---|---|---|---|---|
| BRC-162 with P2PKH | 67 | 107 | 492 | 224 |
| This format | 64 | 73 | 452 | 187 |
On mainnet, a transfer of this shape (the split of the test token in draft BRC-252's experiment, bnt-proof) is 449 bytes: a 72-byte signature and 2-byte amounts where the table allows 73 and 3.
The saving comes from a 23-byte prefix instead of 39, and a holder key in the lock instead of a 20-byte hash with the public key in every unlock. A proof holds every hop the recipient has not already validated, about 0.5 KB per hop in Extended Format, so a first receipt of an old, busy asset is the largest case.
Security considerations
- No issuer after the deploy. An asset never needs its issuer after the deploy, except to mint through an authority it holds. A stolen issuer key cannot move a holder's output or add supply beyond the deploy's policy. It can only sign deploys and spend the authority, and a capped authority states its totals on chain.
- Extended Format claims. Each holder signature commits to its own input's script and satoshis. False claims fail script validation, or fail on chain, where an invalid ancestor makes every descendant unmineable. A recipient credits only per §7 and the finality rule.
- Deploy forgery. A deploy not signed per §3 is no asset. Payload data never names an issuer by itself.
- IPT. IPTs are not BNTs and use their own marker, so none is written under
bnt. Closure (§4) still makes an output with another clause no holding, and §2 refuses a deploy whosemodeis not"open", so an IPT can never enter a BNT balance. - Hash-based id. The asset id is a 160-bit hash. Taking over an existing asset's id needs a second preimage. A collision only lets someone deploy two of their own assets that share an id, and a reader holding both deploys MUST refuse both.
- Proof size. A package of transactions grows with history, as for every BNT. Draft BRC-252 (#321) step proofs keep it to the new transfer, its parents and one proof per parent, without trusting anyone.
- Privacy. Holder keys are fresh children, so the chain does not link a holder's outputs to each other or to its identity.
Rationale
No trusted shortcut. A short proof that trusted the issuer would make the issuer's signature part of validity, and the asset would no longer be native. An asset that wants that trade is an IPT under its own marker (BRC-253), and every asset here is checked to its deploy.
Public keys in the lock. A hash with the key in every unlock costs more over an output's life. Holder keys are fresh children either way, so naming them reveals nothing.
A derived issuer key. Deriving the issuer key from iss with counterparty anyone binds the asset to an identity with no signature in the payload. The identity key itself never signs automatically.
A hashed asset id. It saves 16 bytes on every output. A recipient needs the deploy once anyway, for its rules and metadata.
Open questions
- Zero-satoshi outputs. If miners relay asset outputs of 0 satoshis, the carrier satoshi can go.
Test script
media/0255/bnt.mjs runs §1's clause through @bsv/sdk's interpreter. It checks that an output spends with its holder's signature and that a stranger cannot spend it, checks that anyone can derive §3's issuer key from the identity key, and prints the lock sizes used in §12. Run npm install && npm test in that directory.
References
- Draft BRC-251 (#320) — Bitcoin Native Tokens (BNT): Definition and Wallet Duties
- Draft BRC-252 (#321) — Short Proofs for Bitcoin Native Tokens
- Draft BRC-253 (#333) — Issuer-Permissioned Tokens (IPT): Countersigned Tokens and Items
- BRC-162 — Mandala Tokens
- BRC-30 — Transaction Extended Format (EF)
- BRC-42 — BSV Key Derivation Scheme (BKDS)
- BRC-43 — Security Levels, Protocol IDs, Key IDs and Counterparties
- BRC-33 — PeerServ Message Relay Interface
- BRC-78 — Serialization Format for Portable Encrypted Messages
- Draft BRC-246 (#248) — Session Upgrade
- BRC-37 — Basket and Custom Instructions Extension for Bitcoin Outpoints
- BRC-99 — P Baskets: Allowing Future Wallet Basket and Digital Asset Permission Schemes
- BRC-123 — Basket Permission Scheme Registry and Governance
- BRC-100 — Wallet-to-Application Interface
- BRC-95 — Atomic BEEF Transactions
- BRC-67 — Simplified Payment Verification