Source: HANDCASH-DESKTOP/docs/bnt-tokens.md · sha256 16a21f682037

BNT tokens

Spec: BRC-255 draft, Bitcoin Native Tokens: Compact Format, in BRCs-255/tokens/0255.md until it is reviewed. The marker, box, tag and protocol IDs are bnt; the countersigned mode (IPT) gets its own ipt marker when it is built, so no marker spans both classes.

Decision

All tokens and issuer items use one HandCash format. BRC-162 (BSV-21) is not supported:

An asset's mode and kind are fixed at deploy, so it never changes filing. Rows are tagged with the filing name and <filing>:<40-hex asset id>.

New HandCash items are BNT items too. 1Sat ordinals (1sat, BRC-147/150/165) stay fully supported as an outside format: receive, hold, send, market (ordinal-v2) and BRC-150 provenance, so items keep moving to and from 1Sat wallets and markets. HandCash minting stops creating them.

An item is never both formats at once. Keeping a 1Sat view in step would tie every BNT item transfer to ordinal satoshi positions, which brings back market pads and lost-satoshi risk.

BRC-162 tokens are not read, listed, claimed or swept. A BRC-162 or application/bsv-20 output paying a wallet address stays there: the address scan recognises it only to keep it out of the coin and 1Sat paths.

BRC-251 and BRC-252 (#320, #321) stay as drafts. BRC-253 is rewritten as BRC-255's acceptance protocol, and BRC-254's token-v2 layout moves to BNT outputs.

Why it is cheaper

Measured with @bsv/sdk (BRC-255 §14 and media/0255/bnt.mjs):

Lock bytes 1-in, 2-out transfer Output life (created + spent)
BRC-253 (retired) 246 891 478
IPT (countersigned) 125 580 322
BRC-162 (retired) 67 492 224
BNT open 64 452 187

Where the bytes go:

Proof trust is fixed per mode, with nothing in between:

Supply

Every token deploy fixes its supply policy (BRC-255 §4), and listBntAssets reports it as supply:

Policy issueBntAsset arguments Minting
Fixed no auth, max or size mintBntAsset refuses fixed-supply
Open auth: true any amount
Capped max (units, supply included) and/or size (collection members); either implies auth up to the cap; past it is over-cap

A capped authority states the units and members issued so far. Every reader checks each mint against the cap during its walk, so a mint past the cap is never credited, whoever signed it. Dropping the authority ends minting for good.

Modules (open mode, shipped)

Concern Module
Script encode/parse: deploy, held, authority, both clauses, beacon src/wallet/bnt/script.ts
Deploy payload (DAG-CBOR, crit) src/wallet/bnt/payload.ts
Issuer and holder keys ([2, "bnt issuer"] counterparty anyone; [2, "bnt holder"]) src/wallet/bnt/keys.ts
Deploy read, per-transaction conservation check, walk to the deploy src/wallet/bnt/validity.ts
Proof package (§6) and validated-transaction store src/wallet/bnt/proof.ts, store.ts
Issue, mint, transfer, receive (box bnt), durable outbox, per-peer frontier src/wallet/bnt/wallet.ts, state.ts, chart bntMachine
Collectables shelf and send form src/components/BntShelf.tsx, chart bntHoldingsMachine
BRC-100 methods issueBntAsset, mintBntAsset, transferBntAsset, listBntAssets (wallet-internal burnBntUnits) src/wallet/brc100Handler.ts
App views (p bntft token, p bntnft … under the item grant) src/wallet/itemAccess.ts

Rules the code holds to:

Coins of other chains are native, not wrapped

Spec: BRC-213 (docs/bsva/brcs/coins/0213.md, held back, not submitted), abstract for UTXO chains with Litecoin, Bitcoin Cash (D = 6), Bitcoin (D = 3) and Dogecoin (D = 20) bindings. Apps call getCoinAddress / listCoins / sendCoins with chain: "ltc", "bch", "btc" or "doge"; the Litecoin names below are aliases kept for apps written against earlier builds. Bitcoin Cash works the same way with its own key ([2, "bch coin"], reserved from apps), CashAddr P2PKH, SIGHASH_ALL|FORKID, a header chain checked by SHA-256d and ASERT (bch/headers.ts), and Fulcrum servers over WSS that fail over (bch/chain.ts). Bitcoin ([2, "btc coin"]) and Dogecoin ([2, "doge coin"]) follow the same pattern over the shared Electrum source (coins/electrumChain.ts): Bitcoin pays from P2WPKH with BIP143 and checks SHA-256d with the 2016-block retarget; Dogecoin pays from P2PKH with the legacy signature hash and checks each header's AuxPoW against its scrypt parent, with DigiShield every block. This is depositing: the wallet holds the coins themselves. Wrapping a deposited coin so it moves at BSV fees is a separate, optional step that the wallet does not do; BRC-213 §13 lays out what it would cost and whom each kind of wrap trusts.

The wallet holds litecoin on Litecoin, with its own key; nothing is wrapped, minted or burned. A backed BNT token held by anyone but its issuer is a claim only the issuer's reserve key can pay, which is custody, so the wallet never creates one: a backed token only ever moves back to one of its issuers (transferBntAsset refuses anyone else with custodial, BRC-100 code BNT_CUSTODIAL). Earlier builds' wLTC tokens are retired records: hidden from Collections and Activity, never counted or priced.

Key The P2WPKH key [2, "wltc reserve"], key ID 1, counterparty self (the derivation earlier builds used, so their coins carry over). Apps can never derive or sign with that protocol (reservedKeyRefusal → asset-protocol).
Receive getLtcDepositAddress returns the address and tells the watcher to read it for 2 hours; outside that window it reads every 5 minutes. A coin is recorded once its raw transaction hashes to its txid and pays this wallet's script.
Spendable A received coin is spendable once this wallet's own Litecoin header chain (ltc/headers.ts: shipped checkpoint, exact retarget, scrypt proof of work, most work wins a reorg) counts 6 confirmations over its block. Explorers say which block, never how deep. Change from the wallet's own broadcast payment is spendable at once.
Pay withdrawLtc({ address, litoshi, description? }) (Send → LTC in the app) is an app payment in the same scope as a BSV createAction: contract group wallet, refused offline and on an install that does not hold the account (re-checked after a gap), the same "Approve payment" prompt with the amount in LTC and dollars, and the same per-app Auto-pay / spending grant, counted at its BSV-equivalent dollar value (no price, no Auto-pay; an approved payment with Auto-pay on waits for one). The Activity row names the app. It signs a P2WPKH payment from spendable coins, refuses the wallet's own address (own-address), records it, then broadcasts. A broadcast Litecoin did not take is retried each pass; a sent payment is checked until it is in a block and re-broadcast whenever no explorer knows it.
Show Each received coin has a phase (ltc/depositPhase.ts): unconfirmed, confirming n of 6, movable, unchecked, missing, abandoned, each saying who established it and when. Activity rows ltc-receive / ltc-send read "Received / Sent X LTC", "On Litecoin", in dollars, and open on a Litecoin explorer.
Report listLtcDeposits reads Litecoin and returns { address, balance, locked, deposits, withdrawals }: balance is received and unspent, locked the part still confirming.

The explorers (litecoinspace.org, then BRC-CLOUD /v1/ltc, Esplora API) are untrusted: amounts come from raw transactions the wallet checked, and depth only from its own header chain. The first run carries an earlier build's wLTC record over (handcash.wltc.v1 → handcash.ltc.v1) and rewrites its rows to name the Litecoin transactions. Code: src/wallet/coins/ (engine shared by every chain), src/wallet/ltc/ and src/wallet/bch/ (bindings), chart coinMachine. The watcher of a chain reads the network only for an account that has shown its address or held a coin on that chain.

Not built: Runes-style open minting. Open minting needs a mint output that anyone can spend, but only if they re-create it with a lower counter. Plain BNT locks cannot hold that rule without a covenant or a change to the spec.

Phases

  1. Spec review. BRC-255 draft open; name, marker and protocol IDs still movable.
  2. Core. Done: script, payload, keys, validity, proof, with adversarial tests.
  3. Open mode end to end. Done: issue, send, receive, bntft / bntnft filing, app access, Collectables shelf.
  4. Countersigned mode. Acceptor, requester, beacon, release, policy, and items with collections. Until it lands, a countersigned transfer stays unacknowledged in box bnt so nothing is lost. HandCash handles are the first product IPT: a claimed $handle is supply-1 iptnft issued by the BRC-169 certifier (handleIpt.ts); the certificate path stays live and the on-chain tip is this phase.
  5. Market. BRC-254 on BNT outputs; BRC-CLOUD index and storefront read BNT rows in bntft / bntnft.
  6. Items. Mint-studio and issuers mint BNT items. The market sells them on the token layout, with no pads. 1sat stays as the outside-format path.
  7. BRC-162 removed. Done: no read-only support and no conversion path.

Open decisions