diff --git a/SUMMARY.md b/SUMMARY.md index e7191d44..4de9a83e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -120,6 +120,7 @@ * [1Sat Provenance Remittance for Basket `1sat`](./tokens/0150.md) * [1Sat Ordinals — Single-Satoshi Tokens and Origin Tracking](./tokens/0159.md) * [1Sat Ordinals — Inscription Envelopes](./tokens/0160.md) +* [1Sat Fungible Basket Profile for BRC-46 / BRC-100](./tokens/0175.md) * [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) ## Overlays diff --git a/tokens/0147.md b/tokens/0147.md index 5d3405bd..d97b7508 100644 --- a/tokens/0147.md +++ b/tokens/0147.md @@ -213,11 +213,13 @@ Normative fine-grained asset permission schemes remain [BRC-99](../wallet/0099.m * Wallets that do not implement this profile MUST still store and forward unknown baskets’ `customInstructions` and tags unchanged ([BRC-37](../outpoints/0037.md)). * This profile does not conflict with BRC-99 `p ` baskets; future permission-wrapped variants MAY wrap `1sat` semantics under a `p …` name without invalidating this profile for the plain `1sat` basket. * Marketplaces (e.g. OrdLock), BSV-20/21, and mint APIs are out of scope. +* **[BRC-175](./0175.md) fungibles** share the bare P2PKH tip-move shape with this profile. Chain-scan / instant-ingest MUST NOT place an output in basket `1sat` solely because its creating transaction spends a 1-sat input. Importers SHOULD require an inscribed non–`1sat-ft` ancestor (or equivalent remittance / provenance) before painting a collectable. An ancestor with content-type `application/1sat-ft+json` MUST NOT yield eligibility for this profile. ## Security considerations * **Tag / metadata spoofing** — Without provenance verification, a sender can attach another inscription’s `origin` and `name`. See [BRC-150](./0150.md). A forged `content` only mis-points display media; it does not prove tip→origin. * **Basket pollution** — Placing non-1-sat or non-ordinal outputs in `1sat` confuses list UIs; receivers should re-check `satoshis` and inscription rules. +* **Fungible misfile** — Auto-importing every bare 1-sat that spends a 1-sat parent will file [BRC-175](./0175.md) transfer tips into `1sat`. Those tips then look burnable as junk NFTs. See Compatibility above. * **Burn** — Per 1Sat Ordinals, packing a sat into a multi-sat output ends that origin trail. Do not continue `origin:` claims across a burn. * **Indexer trust** — Display media URLs and collection metadata often come from indexers; this profile does not make indexers authoritative for tip→origin binding. Forwarding `content` reduces indexer dependence for derivative media. diff --git a/tokens/0175.md b/tokens/0175.md new file mode 100644 index 00000000..567c9c45 --- /dev/null +++ b/tokens/0175.md @@ -0,0 +1,266 @@ +# BRC-175: 1Sat Fungible Basket Profile for BRC-46 / BRC-100 + +Brandon Cryderman / HandCash (brandongcryderman@gmail.com) + +## Abstract + +This BRC defines the **fungible** application basket profile for 1Sat under [BRC-46](../wallet/0046.md) / [BRC-100](../wallet/0100.md). A fungible 1Sat token is a set of **1-satoshi tips that share one origin**. Token identity is that origin outpoint. Each tip carries a face value **`amt`** (token units). **Balance** is the sum of `amt` on bound tips held. Transfers **spend tip UTXOs and create new 1-sat tips** (recipients and change), funded with ordinary BSV dust — the same custody class as collectables ([BRC-147](./0147.md)), not a split of one physical satoshi across payees. + +**Locked supply is optional:** the origin MAY set `supply: "locked"` with `max` (**total units**). When omitted, tips still bind via tip→origin rules with face-value `amt`; wallets MUST NOT invent a lock. Offline tip→origin proof reuses [BRC-150](./0150.md). Mint-batch attestation MAY bind same-tx genesis siblings until a transfer creates a 150 path. A tip with omitted `amt` MUST be treated as `amt = 1` (tip-count legacy). + +This BRC does not redefine 1Sat origin theory ([BRC-159](./0159.md)) or inscription envelopes ([BRC-160](./0160.md)). + +## Motivation + +Collectables already interoperate as tip→origin assets with offline proof ([BRC-147](./0147.md), [BRC-150](./0150.md)). Fungible balances that re-inscribe protocol JSON each hop do not share that transfer path and typically depend on indexers for discovery and settlement. Counting tips as units (without face-value `amt`) does not scale: moving *N* units costs *O(N)* tips. + +Wallets and future **token markets** need a shared contract so that: + +1. Markets and wallets identify a token by **origin** (one id forever). +2. Inventory is a set of **tip UTXOs** (listable, lockable, settleable like collectables). +3. Large balances MAY sit in **one tip** (`amt` large); sends **split/merge** with change. +4. Fungible tips are not filed into NFT basket `1sat`. +5. Supply policy is readable from the **origin tip** without an indexer census. +6. Custody never depends on a market overlay or indexer being online. + +## Specification + +### Basket identifier + +* The storage basket name is the UTF-8 string `1sat-ft`. +* After [BRC-46](../wallet/0046.md) normalization (trim + lowercase), conforming use MUST use exactly `1sat-ft`. +* This identifier does **not** use the [BRC-99](../wallet/0099.md) `p ` prefix. +* Outputs for this profile MUST NOT be placed in basket `1sat` ([BRC-147](./0147.md)). +* Writers and readers MUST use `1sat-ft` (no legacy basket alias). + +### Eligibility + +An output SHOULD be placed in basket `1sat-ft` only when **all** hold: + +1. `satoshis === 1`. +2. The tip is a carrier for fungible units of a token whose identity is a shared origin outpoint. +3. The tip **binds** to that origin under §Binding (below). +4. The tip carries a valid first `ord` envelope ([BRC-160](./0160.md)). Genesis and mint-batch tips MUST. **New transfer colour tips MUST** (see §Transfer inscription). Legacy bare leftovers MAY exist; they bind only via lineage walk and MUST NOT invent `amt`. + +Wallets MUST NOT place ordinary payment change or multi-sat outputs into `1sat-ft`. +**Autodetect is not “any 1-sat output”:** `satoshis === 1` is necessary; basket + binding are required before counting units. + +### Token identity and units + +* **Token id** = origin outpoint (`txid_vout` after normalize; same rules as [BRC-147](./0147.md)). +* **Carrier** = 1-sat tip UTXO. Face value is **not** the satoshi amount. +* **`amt`** = positive integer token units on that tip (decimal string in JSON). +* **Balance** = sum of `amt` over bound tips held for that origin. +* Missing `amt` ⇒ treat as `"1"` (legacy tip-count rows). +* **`max`** (locked) = total units at genesis, **not** required tip count. +* **Decimals** — Origin MAY reserve `dec`; readers that do not implement decimals MUST treat `amt` as integer units and MUST NOT invent fractional balances. + +### Origin inscription (normative policy surface) + +The **origin tip** (outpoint = token id) MUST carry an `ord` envelope whose body is UTF-8 JSON: + +```json +{ + "p": "1sat-ft", + "v": 1, + "amt": "1000", + "sym": "GOLD", + "name": "optional", + "supply": "locked", + "max": "1000" +} +``` + +| Field | Requirement | Meaning | +|-------|-------------|---------| +| `p` | MUST | Profile id `1sat-ft`. | +| `v` | MUST | Origin schema version. This BRC defines `1`. | +| `amt` | MUST | Face value of **this** tip (genesis tip often holds the full issued amount). | +| `supply` | MAY | `locked` freezes total units. Omit when no cap. `open` means uncapped (no local max). | +| `max` | MUST when `supply` is `locked` | Total units. Decimal string of a positive integer. | +| `sym` | SHOULD | Short symbol (≤ 32). | +| `name` | MAY | Display name (≤ 80). | +| `dec` | RESERVED | Future decimal places; ignore if unimplemented. | +| `issuer` / `auth` | RESERVED | Future open-mint authority; ignore until defined. | + +Unknown fields MUST be preserved by wallets that round-trip remittance ([BRC-37](../outpoints/0037.md) spirit) and MUST be ignored for validation unless this BRC defines them. + +**Authority:** When locked, the origin inscription is the sole durable supply policy. Remittance MUST NOT contradict it (`supply` / `max` on tips SHOULD echo the origin when present; on conflict, **origin wins**). Wallets MUST NOT write `supply: locked` unless the origin defined it. Child tips MUST NOT claim `amt` that violates §Conservation. + +### Supply (optional lock) + +| State | Meaning | +|-------|---------| +| *(omitted)* / `open` | No declared unit cap. Hold/send still use `amt` + binding + conservation. | +| `locked` | Genesis defines total units `max`. No further mint against this origin. | + +Conforming **locked mints** MUST: + +1. Create one or more 1-sat tips in the genesis transaction whose **`amt` values sum to `max`**, and +2. Set origin `supply` to `locked` with that `max`. + +A single genesis tip with `amt = max` is the preferred compact locked mint. Unlocked mints MAY omit `supply`/`max` and issue any positive genesis `amt`. + +Conforming **receivers** MUST accept a tip only if it binds under §Binding. When the origin is locked, mint-batch / local holdings MUST respect `max`. Holders are **not** required to enumerate global circulating supply via an indexer. Markets MAY show indexer stats as convenience only (grade C). + +### Mint batch (genesis siblings) + +Optional. Genesis MAY create the origin tip and additional siblings in the **same transaction** (`randomizeOutputs: false` recommended so origin is `txid_0`). + +Sibling at `txid_vout` binds when: + +* same txid as origin, +* remittance includes `"mint": { "batch": true, "vout": }`, +* `origin` matches the genesis origin outpoint, +* each sibling has a positive `amt`, +* sum of all genesis tip `amt` values equals origin `max`, and +* each output is 1 sat with a valid first `ord` envelope on genesis outputs. + +After the first transfer of a tip, senders SHOULD attach [BRC-150](./0150.md) provenance; receivers MUST prefer a verifying 150 package over mint-batch attestation alone. + +### Conservation (split / merge) + +Token units are conserved across spends: + +1. Select one or more **input tips** bound to the same origin. +2. Let `S` = sum of input `amt`. +3. Create one or more **output tips** (1 sat each), funded with ordinary BSV for dust + fees. +4. Let `T` = sum of output `amt` for that origin in the same action. +5. Receivers / wallets MUST require `T === S`. Change is an output tip back to the sender (or another allowed destination) with the residual `amt`. + +There is **no** requirement that one physical satoshi be “split” among recipients. The input tip UTXO(s) are spent; new 1-sat carriers are created. Multi-recipient pays = multiple new tips in one (or more) transactions. + +Merging (many inputs → fewer outputs) and splitting (few inputs → more outputs) use the same conservation rule. + +### Binding (when a tip counts) + +A tip **binds** to origin `O` if any of the following hold (checked in order): + +1. **Genesis** — tip outpoint equals `O`, and origin inscription parses as this profile. +2. **Transfer inscription** — this tip’s first `ord` envelope is `application/1sat-ft+json` with a positive integer `amt`, and the spend-chain / BRC-150 walk binds to `O`. `amt` is taken from this envelope. +3. **BRC-150** — `customInstructions.provenance` verifies per [BRC-150](./0150.md) v2 to origin `O`. +4. **Mint batch** — same-tx sibling attestation as above. +5. **Parent hop** — remittance `parent` names a tip that itself binds to `O` (inductive), and this tip’s `amt` is consistent with a conserving spend of that parent (wallets SHOULD verify when parent spend data is available). Wallets MUST depth-limit inductive walks and MUST fail closed on cycles, missing parents, or parents that do not themselves bind. A `parent` claim alone MUST NOT bind. + +`mint.extend` without a future auth rule does **not** bind. + +### Tags + +| Tag | Requirement | Meaning | +|-----|-------------|---------| +| `1sat-ft` | SHOULD | Profile marker. | +| `ordinal` | SHOULD | 1-sat ordinal tip. | +| `origin:` | SHOULD when known | Claimed origin. | +| `name:` / `sym:` | MAY | Display claims. | +| `id:` / `outid:` | SHOULD for held rows | Local list key ([BRC-164](../wallet/0164.md) practice). | + +Unknown tags MUST be preserved. + +### Custom instructions (per tip) + +```json +{ + "p": "1sat-ft", + "origin": "", + "amt": "400", + "sym": "", + "supply": "locked", + "max": "", + "mint": { "batch": true, "vout": 3 }, + "parent": "", + "provenance": { } +} +``` + +| Field | Requirement | Meaning | +|-------|-------------|---------| +| `p` / `origin` | SHOULD / MUST when known | Profile + token id. | +| `amt` | MUST when known | Face value of this tip (decimal string). Omit only for legacy `amt = 1`. | +| `supply` / `max` | SHOULD when origin locked | Echo origin policy only — do not invent. | +| `mint` | MAY | Genesis batch attestation. | +| `parent` | MAY | Prior tip for inductive bind. | +| `provenance` | SHOULD when buildable | BRC-150 v2 object. | + +### Transfer inscription + +Conforming senders MUST inscribe each new colour tip (payee and change) as `application/1sat-ft+json` over P2PKH. Body is UTF-8 JSON: + +```json +{ + "amt": "9000" +} +``` + +| Field | Requirement | Meaning | +|-------|-------------|---------| +| `amt` | MUST | Face value of **this** tip (decimal string). | + +Origin is **not** written on the leftover. Identity is the spend-chain / [BRC-150](./0150.md) walk to the mint, same as [BRC-147](./0147.md). `amt` is on this tip so a split can be verified before the wallet shows it as true. + +Other 1-sat outputs in the same transaction are **not** carriers unless they carry this envelope. + +Remittance (`customInstructions`) MAY repeat `origin` + `amt` to speed import, same as 147. When remittance `amt` and the inscription disagree, **the inscription wins**. A tip with no envelope is legacy: scanners MUST walk spend-chain to an inscribed ancestor and MUST NOT invent `amt`. + +### Transfer + +1. Select input tip(s) bound to the same origin with sufficient Σ `amt`. +2. Build output tip(s): payee amounts + change; enforce §Conservation. +3. Spend inputs; create new 1-sat outputs (BSV funds dust + fees). +4. **Inscribe** each colour tip per §Transfer inscription. +5. File under basket `1sat-ft` with tags + CI including `amt` (fast path). +6. Attach BRC-150 when a path exists; else set `parent` to a spent tip and echo origin policy. + +### Chain scan and basket separation + +Address / UTXO scan MAY surface 1-sat outs. Conforming wallets: + +* An output whose own first `ord` envelope is `application/1sat-ft+json` MUST be this profile. Read `amt` from the body. Origin comes from the 150 / spend-chain walk. Remittance is a fast path only. +* MUST NOT place a 1-sat output in basket `1sat` ([BRC-147](./0147.md)) solely because the creating transaction spends a 1-sat input. +* MUST NOT place an output in basket `1sat-ft` until it **binds** under §Binding. Unbound 1-sat outs SHOULD be held (not swept, not painted as collectables). +* When walking legacy bare-tip lineage, an ancestor whose first `ord` envelope content-type is `application/1sat-ft+json` MUST be treated as this profile — not as a collectable mint. Do not invent `amt` for that leftover. + +### Peer capability (optional) + +Senders MAY advertise support for basket `1sat-ft` / this profile via identity or handle-resolve metadata, and MAY warn or refuse bare-address sends to peers that do not advertise it. Remittance is a fast path for identified peers; the inscription is custody. Capability bits are **not** a custody requirement. + +### Provenance scope + +The BRC-150 v2 **object schema and verification rules** apply unchanged when present. Conforming wallets MUST accept 150 packages for `1sat-ft` tips. A future BRC-150 errata MAY list basket `1sat-ft` explicitly; until then this BRC imports the format by reference. + +### Open mint (reserved) + +Post-genesis mint via `mint.extend` is **not binding** without a future **auth** rule on the origin. Uncapped (`supply` omitted / `open`) is not the same as free extend — new tips still need genesis, mint-batch, BRC-150, or a proven parent hop. + +### Market / overlay notes (informative) + +| Concern | Approach | +|---------|----------| +| Token identity in a catalog | Origin outpoint | +| Seller inventory | Bound tips in `1sat-ft` (UTXOs) with `amt` | +| Listing lock | Existing OrdLock / marketplace patterns on tip UTXOs | +| Settlement | Conserving tip spends to buyer (same as wallet send) | +| Discovery / volume charts | Optional overlays (grade C) — never custody | +| Scaling | Transfer cost ≈ tips spent/created, not unit count; prefer compact `amt` | + +## Security considerations + +* **Tag / CI spoofing** — Without binding (§Binding), identity is unproven. Markets MUST NOT treat unproven tips as inventory of origin `O`. +* **`amt` inflation** — Receivers MUST enforce §Conservation when parent spend data is available; unbound or non-conserving tips MUST NOT increase spendable balance. +* **Mint-batch weaker than 150** — Prefer 150 after first hop. Genesis envelopes + `max` + Σ `amt` bound the batch. +* **Locked vs lying minter** — Origin `max` does not stop extra 1-sat outputs on-chain; conforming wallets **reject** tips that do not bind / conserve for that token id. +* **Open mint** — Deferred precisely because unbounded extend claims are forgeable. +* **Basket mix-up** — Do not file fungibles into `1sat` or collectables into `1sat-ft`. +* **Legacy bare leftovers** — Older sends may be bare P2PKH. A bare 1-sat that spends a 1-sat parent is **not** a collectable. Walk lineage; FT mime ancestor ⇒ this profile, and do not invent `amt`. New conforming sends inscribe origin + amt so a scan can read the tip without remittance. Pre–BRC-175 clients that treat unknown `ord` mimes as collectables may still misfile inscribed tips; that is an old-client bug, not a reason to omit the envelope. + +## Relationship to other BRCs + +| BRC | Relationship | +|-----|----------------| +| [BRC-147](./0147.md) | Collectables / NFT basket `1sat`. Remittance is a fast path on both profiles; see §Chain scan. | +| [BRC-150](./0150.md) | Provenance format for lineage hops. | +| [BRC-159](./0159.md) / [BRC-160](./0160.md) | Origin theory / envelopes. | +| [BRC-165](./0165.md) | P1Sat permissions for collectables; fungible scopes MAY follow. | + +## Copyright + +Copyright (c) Brandon Cryderman / HandCash. Licensed under the Open BSV License. diff --git a/tokens/README.md b/tokens/README.md index 58d0b7dc..e15039c9 100644 --- a/tokens/README.md +++ b/tokens/README.md @@ -17,4 +17,5 @@ BRC | Standard 150 | [1Sat Provenance Remittance for Basket `1sat`](./0150.md) 159 | [1Sat Ordinals — Single-Satoshi Tokens and Origin Tracking](./0159.md) 160 | [1Sat Ordinals — Inscription Envelopes](./0160.md) +175 | [1Sat Fungible Basket Profile for BRC-46 / BRC-100](./0175.md) 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./0226.md) \ No newline at end of file