Abstract
A BRC-100 wallet holds more than one kind of value: BSV, coins of other chains, items, Bitcoin Native Tokens (BNT) and Issuer-Permissioned Tokens (IPT). Each kind has its own storage, its own proof of authenticity, and its own way for applications to see and move it.
This document calls those kinds asset profiles. A profile is the unit of everything an application can be granted. It fixes six things:
- where the value lives;
- what proves it;
- how an application views it;
- how an application moves it;
- which actions exist;
- which spending scope covers it.
Three rules make profiles safe to combine:
- Every held asset belongs to exactly one profile, for life.
- A grant in one profile never grants anything in another.
- Only coin profiles share the payment budget.
The document registers seven profiles (bsv, coin:<chain>, 1sat, bntft, bntnft, iptft, iptnft), marks which token profiles are BNT, and defines one read method, listAssetProfiles, so applications can learn what a wallet holds and accepts before asking.
Motivation
Each asset format so far has specified its own basket, permission scheme and actions in isolation. Each wallet then decides for itself how they interact:
- Does Auto-pay cover a token transfer?
- Does permission to view items show tokens?
- Is a Litecoin balance a collectable?
Different answers in different wallets are a security problem, not a style choice. An application granted a payment budget must not be able to spend an item out of it.
Applications face the opposite gap. They cannot tell whether a wallet holds Litecoin or Issuer-Permissioned Tokens without calling methods and handling failures.
Relationship to other documents
| Profile | Specified by |
|---|---|
bsv |
BRC-100, with budgets from BRC-73 |
coin:<chain> |
Draft BRC-213 (native UTXO-chain coins, #337) |
1sat |
BRC-147 storage, BRC-150 authenticity, BRC-165 permissions |
bntft |
Draft BRC-255 (#335), with the BNT tests of draft BRC-251 (#320) |
bntnft |
Draft BRC-255 (#335) |
iptft, iptnft |
Draft BRC-253 (#333) |
A peer paying an identity, not an application, learns which coin profiles that identity receives from its signed receiving record (BRC-213 §7.2), whose coin:<chain> entries are the profile ids here.
Market listings and direct offers for the asset profiles are draft BRC-254 (#334). Draft BRC-255 (#335) is the compact BNT format: its tokens file in bntft and its items in bntnft. A compact countersigned asset is BRC-253 §15, under its own marker ipt, and files in iptft or iptnft. Readers tell formats apart by their tags, never by profile.
A Bitcoin Native Token (BNT) passes the six tests of draft BRC-251: in the script, valid from transactions alone, moved by its holder alone, rules fixed at deploy, settled by Bitcoin, and peer to peer. bntft and bntnft hold only BNT. iptft and iptnft hold Issuer-Permissioned Tokens (IPT, BRC-253), never BNT.
Terms
- Profile: a named kind of held value with the fields of §1.
- Coin profile: a profile whose value is a plain payment unit (
bsv,coin:<chain>). - Asset profile: any other profile. Its value has identity, is issued by someone, and is proven back to an origin or deploy.
- Grant: anything a user gives an application that outlives one call: a view permission, a spend label scope, an automatic-payment budget, a BRC-73
spendingAuthorization.
Specification
1. Profile fields
| Field | Meaning |
|---|---|
id |
Lowercase identifier. Coin profiles of other chains are coin:<chain id> |
| Holds | What one unit is |
| Custody | Where the value lives and whose key moves it |
| Authenticity | What proves a held unit is what it claims |
| View | How an application lists it, and which grant that needs |
| Move | How an application spends it, and which consent that needs |
| Actions | The application methods that act on it, each its own consent |
| Spending scope | payment (shares the per-application payment budget) or per-action (always its own prompt, never in a budget) |
| BNT | Token profiles only: whether every unit is a Bitcoin Native Token (BRC-251) |
2. Registered profiles
id |
Holds | Custody | Authenticity | View | Move | Spending scope |
|---|---|---|---|---|---|---|
bsv |
Satoshis | Wallet keys on BSV | BRC-67 SPV | getBalance, listOutputs on the application's own baskets |
createAction |
payment |
coin:ltc |
Litoshi | The wallet's coin key on Litecoin (BRC-213 §2) | Burial 6 deep on the wallet's own Litecoin header chain (BRC-213 §4) | listCoins({ chain: "ltc" }) |
sendCoins({ chain: "ltc", … }) |
payment |
coin:bch |
Satoshi | The wallet's coin key on Bitcoin Cash | Burial 6 deep on its own Bitcoin Cash header chain | listCoins({ chain: "bch" }) |
sendCoins({ chain: "bch", … }) |
payment |
coin:btc |
Satoshi | The wallet's coin key on Bitcoin | Burial 3 deep on its own Bitcoin header chain | listCoins({ chain: "btc" }) |
sendCoins({ chain: "btc", … }) |
payment |
coin:doge |
Koinu | The wallet's coin key on Dogecoin | Burial 20 deep on its own Dogecoin header chain | listCoins({ chain: "doge" }) |
sendCoins({ chain: "doge", … }) |
payment |
1sat |
One-sat ordinal items | Owner key on BSV | BRC-150 proof to the origin | Basket p 1sat <scope> (BRC-165) |
createAction labeled p 1sat input id <key> |
per-action |
bntft |
BNT fungible tokens | Owner key on BSV | BRC-255 §7 to the deploy | Basket p bntft <scope> |
Wallet methods only (BRC-255) | per-action |
bntnft |
BNT items (supply 1) | Owner key on BSV | BRC-255 §7 to the deploy | Basket p bntnft <scope>, with BRC-165 scopes |
Wallet methods only (BRC-255) | per-action |
iptft |
Countersigned fungible tokens (IPT, not BNT) | Owner key, plus the issuer's key set for each transfer | The issuer's countersignature in each transfer | Basket p iptft <scope> |
Wallet methods only (BRC-253) | per-action |
iptnft |
Countersigned items (supply 1, IPT, not BNT) | As iptft |
As iptft |
Basket p iptnft <scope> |
Wallet methods only (BRC-253) | per-action |
coin:<chain> registers one profile per BRC-213 binding. A wallet lists only the chains it supports.
3. Rules
- One profile, for life. A held asset belongs to exactly one profile from the moment the wallet records it. Nothing moves an asset between profiles. Even a countersigned asset whose issuer released it stays
iptftoriptnft. - Grants never cross. A grant in one profile gives nothing in another. Permission to view
1satitems shows no tokens, abntftview never shows an item, and a payment budget never covers an asset profile. - One payment budget. All coin profiles share one budget per application: any automatic-payment budget the wallet keeps, and any BRC-73
spendingAuthorizationgrant. A payment incoin:<chain>counts at its BSV-equivalent fiat value (BRC-213 §10). An application cannot route around its budget by paying on another chain. - Asset actions stay per-action. A transfer, mint, issue, listing, purchase or offer that moves an asset profile is never approved automatically, even when it also pays satoshis. The satoshis it pays do not open a payment-budget path for the asset.
- Separate views. A wallet shows each profile in its own place, and never counts an IPT in a BNT balance. Coins are balances, never collectables or tokens. Items are never mixed into token lists. A retired or migrated record of an earlier format is not shown as value in any profile.
- Refuse by profile. A request that names one profile but would move value of another is refused, not partly executed.
- No profile, no value. A wallet MUST NOT count, price or offer value it cannot place in a profile it supports.
4. listAssetProfiles()
This method requires a connected application and shows no further prompt. It tells the application which profiles the wallet supports and how to reach each. It reveals no balances.
{
"profiles": [
{ "id": "bsv", "scope": "payment", "spec": ["BRC-100"] },
{ "id": "coin:ltc", "scope": "payment", "spec": ["BRC-213"],
"chain": "ltc", "decimals": 8, "minConfirmations": 6,
"methods": ["getCoinAddress", "listCoins", "sendCoins"] },
{ "id": "1sat", "scope": "per-action", "spec": ["BRC-147", "BRC-150", "BRC-165"],
"basket": "p 1sat", "spendLabel": "p 1sat input id" },
{ "id": "bntft", "scope": "per-action", "spec": ["BRC-251", "BRC-255"], "basket": "p bntft", "bnt": true },
{ "id": "bntnft", "scope": "per-action", "spec": ["BRC-255"], "basket": "p bntnft", "bnt": true },
{ "id": "iptft", "scope": "per-action", "spec": ["BRC-253"], "basket": "p iptft", "bnt": false },
{ "id": "iptnft", "scope": "per-action", "spec": ["BRC-253"], "basket": "p iptnft", "bnt": false }
]
}
speclists the documents the wallet implements for that profile, so an application can tell a draft-level implementation apart.basketis the prefix of the view basket, andspendLabelthe prefix of the spend label, where they exist.- Coin profiles carry the BRC-213 binding facts an application needs to show amounts and waiting times.
bntis present on token profiles only. An application MUST NOT call an asset a Bitcoin Native Token unless its profile hasbnt: true.
A wallet MUST list only profiles whose full rule set it enforces. An application MUST treat a missing profile as unsupported, and MUST NOT probe for it with spending calls.
5. Registering a profile
A new profile is registered by a document that fixes every field of §1 and states how it satisfies §3. A profile whose units can pay for anything SHOULD be a coin profile and join the payment budget. A profile whose units have identity MUST be per-action.
Security considerations
- Budget escape. Without rule 3, an application limited to $10 a day in BSV could pay unlimited Litecoin. Without rule 4, a budget sized for small payments could move a valuable item that happens to cost one satoshi to send.
- Confused deputy. Rule 6 stops a request built for one profile from reaching inputs of another, such as a
createActionspend label that selects an output filed elsewhere. - Fingerprinting.
listAssetProfilesreveals which profiles a wallet supports, not what it holds. A wallet MAY answer it only after connection, as specified, to avoid exposing even that to unconnected sites.
Rationale
- The profile is the unit of consent. Users reason about "my Litecoin" or "my items", not baskets or script templates. Grants that follow that line are the ones users can predict.
- Format is not profile. A profile names a class, not an encoding. What a holder can do with an asset depends on whether it is native or countersigned, not on how its lock is encoded.
- Native is objective. BNT is decided by six tests a reader checks from the token's rules, not by who issued it, so the
bntflag is a fact about the profile and never a rating. - Coins share one budget, assets never do. Coins are interchangeable at a price, so one fiat budget is meaningful. Assets are not interchangeable, so no budget can describe them.
Open questions
- Per-profile budgets. Some users may want a smaller budget for one chain. That would narrow rule 3 without breaking it, because the shared budget stays an upper bound.
Implementations
HandCash Desktop and Mobile (@handcash/wallet-ui) implement every profile in §2, with coin bindings coin:ltc, coin:bch, coin:btc and coin:doge. The code is in src/wallet/assetProfiles.ts (listAssetProfiles), and the profile table is in docs/asset-actions-and-permissions.md §0.