> For the complete documentation index, see [llms.txt](https://bsv.brc.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bsv.brc.dev/tokens/0176.md).

# BSV-21 — Validity Proofs

Open Protocol Labs (<info@opl.dev>)

**Authors:** David Case (<dcase@opl.dev>)

**Contributors:** Luke Rohenaz (<luke@opl.dev>), Kurt Wuckert Jr. (<kurt@opl.dev>), Michael Boyd (<root@opl.dev>), Dan Wagner (<dan@opl.dev>)

## Abstract

This BRC defines an offline-verifiable **packet** that proves one or more transaction outputs are valid [BRC-161](/tokens/0161.md) / [BRC-162](/tokens/0162.md) BSV-21 tokens.

The packet is a [BRC-62](/transactions/0062.md) BEEF. Delivery is **transaction-level** ([BRC-95](/transactions/0095.md) Atomic BEEF encoding) when proving outputs of one transaction, or [BRC-158](/transactions/0158.md) Outpoint BEEF when proving a single outpoint. Completeness and validation are **this** profile — not Atomic BEEF’s SPV-minimal ancestor set.

[BRC-161](/tokens/0161.md) / [BRC-162](/tokens/0162.md) say when an output of one transaction is valid given valid inputs. This document says which transactions must be in the bag, and how to check those inputs by walking the bag.

## Motivation

BSV-21 balances live in UTXOs. A receiver who is handed a tip (or several tips in one transaction) needs to check that each claimed output is really a token of the named id and amount — without a global indexer.

1Sat solves the analogous problem with a linear tip→origin walk ([BRC-159](/tokens/0159.md)) and a remittance bag ([BRC-150](/tokens/0150.md)). BSV-21 is different:

* Identity is the **deploy outpoint**, not a sat origin.
* Splits and merges make the ancestry a **DAG**, not a path.
* Validity is **per token id per transaction** (conservation / authority), not sat ordering.
* Funding inputs are irrelevant. Token-input source **bodies** are not.

Atomic BEEF is an SPV package: mined ancestors may be reduced to merkle proofs, and transactions outside the subject’s SPV graph are stripped. Those bodies are not extraneous here. This profile keeps them.

## Relationship to other documents

| Concern                                        | Document                                                                                           |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Token id, ops, per-tx validation (JSON)        | [BRC-161](/tokens/0161.md)                                                                         |
| Same model, binary prefix                      | [BRC-162](/tokens/0162.md)                                                                         |
| Basket `bsv21` remittance / tags               | [BRC-163](/tokens/0163.md)                                                                         |
| BEEF / Atomic BEEF / Outpoint BEEF encodings   | [BRC-62](/transactions/0062.md), [BRC-95](/transactions/0095.md), [BRC-158](/transactions/0158.md) |
| 1Sat tip→origin (not used for BSV-21 validity) | [BRC-159](/tokens/0159.md)                                                                         |

## Specification

### What is proven

Given a packet and a set of **subject outpoints**, a successful verify means: each subject output is a valid BSV-21 output under [BRC-161](/tokens/0161.md) / [BRC-162](/tokens/0162.md), of the token id and amount carried on that output, funded by a valid deploy (and, for mints, a valid authority) **present in the packet**.

The packet proves **local lineage** of the subjects. It does not prove that the issuer minted nothing else, and it does not prove UTXO set membership.

### Packet

There is one packet: a [BRC-62](/transactions/0062.md) BEEF (V1 or V2) plus a **scope** — the set of subject outpoints the packet is verified against. The envelope only determines how the scope travels: embedded, or supplied by the caller.

| Envelope                                   | When                                                 | Scope                                           |
| ------------------------------------------ | ---------------------------------------------------- | ----------------------------------------------- |
| Atomic BEEF (`0x01010101` + txid)          | Proving one or more outputs of **one** transaction   | Embedded: outputs of that transaction id        |
| Outpoint BEEF (`0x16a7beef` + txid + vout) | Proving **one** outpoint                             | Embedded: that outpoint                         |
| Bare BEEF                                  | Otherwise (several transactions’ outputs in one bag) | Out-of-band: caller names the subject outpoints |

The verify result is always bound to the scope, however it arrived. A caller MUST prove exactly the outpoints it intends to accept; a proof obtained for one scope says nothing about any other outpoint.

Atomic BEEF **encoding** is reused — only its byte framing. These [BRC-95](/transactions/0095.md) rules **do not** apply to a BRC-176 packet:

* Fail if a transaction is not an SPV ancestor of the subject.
* Omit a transaction body once a merkle proof exists.

A BRC-176 packet MUST NOT be validated, stripped, or round-tripped as ordinary BRC-95 Atomic BEEF: BRC-95 body omission would remove the token-parent bodies this profile requires, and BRC-95 ancestor rejection would reject packets that are complete under this profile.

[BRC-96](/transactions/0096.md) txid-only entries are not a transaction body. Missing body → that hop cannot be proven.

### Completeness

Let `S` be the subject outpoints. Let `T` be the set of token ids those outputs name (deploy outputs contribute their own outpoint as id).

The BEEF MUST contain a **full transaction body** for:

1. Every transaction that has a subject outpoint.
2. Recursively, the source transaction of every **BSV-21 input of a token id in `T`** spent in those transactions, until each lineage reaches its **deploy**.

It need **not** contain source transactions of non-token inputs (funding, fees, other protocols).

Extra transactions MAY be present. They MUST NOT cause the packet to fail.

To prove even a single transfer output of a transaction, the bag MUST still include **every** same-id token input of that transaction — conservation is per token id, not per vout.

### Decode

Each output is decoded as BSV-21 JSON ([BRC-161](/tokens/0161.md)) or binary ([BRC-162](/tokens/0162.md)). Either encoding is a BSV-21 output. Unrecognized scripts are ignored.

An output that parses under both encodings resolves to binary — [BRC-162](/tokens/0162.md) recognition rules.

Normalized record:

| Encoding                  | Fields     | `id`          | Kind           | `amt`  |
| ------------------------- | ---------- | ------------- | -------------- | ------ |
| JSON `deploy+mint`        | —          | this outpoint | `deploy-value` | `amt`  |
| JSON `deploy+auth`        | —          | this outpoint | `deploy-auth`  | 0      |
| JSON `mint`               | `id`       | field         | `mint`         | `amt`  |
| JSON `transfer`           | `id`       | field         | `transfer`     | `amt`  |
| JSON `burn`               | `id`       | field         | `burn`         | `amt`  |
| JSON `auth`               | `id`       | field         | `auth`         | 0      |
| Binary, empty id, amt > 0 | —          | this outpoint | `deploy-value` | amount |
| Binary, empty id, amt = 0 | —          | this outpoint | `deploy-auth`  | 0      |
| Binary, id, amt = 0       | 36-byte id | that outpoint | `auth`         | 0      |
| Binary, id, amt > 0       | 36-byte id | that outpoint | `value`        | amount |

Token ids compare in underscore form `txid_vout` ([BRC-161](/tokens/0161.md)).

### Validation

Parse the envelope; load the BEEF. Structural failure → unproven.

Before any token rule is evaluated, the BEEF itself MUST be valid under [BRC-62](/transactions/0062.md) / [BRC-95](/transactions/0095.md) / [BRC-158](/transactions/0158.md): every txid MUST match its raw transaction body, every included merkle path MUST verify against its trusted header (for unmined transactions, against the caller's trust anchor), and unmined descendants MUST chain to proven ancestors. BSV-21 admission is evaluated only over that verified transaction set. A self-consistent synthetic DAG is not a proof.

Implementations MAY impose resource limits (transaction count, packet size, recursion depth). A tripped limit MUST be reported as unproven, failing closed for the affected subjects.

`prove(outpoint)` means: this output is a valid BSV-21 token of the `id` and `amt` on its script. That is conservation (or a mint from proven authority) **at this hop**, with every value/auth input itself proven, back to a **deploy**. A deploy in the bag is not enough.

Walk **backward**. A same-id token input counts only if `prove` succeeds on it. Deploy → genesis, done.

Missing source body → that input counts as nothing. A missing body can only reduce `I`, never increase `O`: omitting a same-id parent may produce false negatives (unproven), but can never produce a false positive. Missing non-token/funding parents are irrelevant to BSV-21 lineage. Bitcoin transactions cannot cycle; if `prove` re-enters an outpoint still on the stack, the bag is malformed → unproven.

Conservation is per token id in the transaction: proving one transfer uses the other same-id output **amounts** on that tx. It does not prove those siblings unless they are subjects too.

```
function prove(outpoint, beef):
    if outpoint is already on the prove stack: fail          // malformed BEEF

    tx = load outpoint's transaction from beef
    if missing: fail

    token = decodeBsv21(tx.outputs[outpoint.vout])
    if token is none: fail
    if token is a deploy: return ok                          // genesis

    I = 0
    hasAuth = false
    for each input of tx:
        sourceTx = load input's source transaction from beef
        if missing: continue
        spent = decodeBsv21(sourceTx.outputs[input.vout])
        if spent is none: continue
        if spent is a deploy:
            spent.id = input's source outpoint
        if spent.id != token.id: continue                    // other token / funding
        if prove(input's source outpoint, beef) fails: continue

        if spent is authority: hasAuth = true
        else if spent is burn: continue
        else: I += spent.amt

    // O = same-id amounts this tx moves (not mints). Valid only if I >= O.
    O = 0
    for each output of tx:
        created = decodeBsv21(output)
        if created is none: continue
        if created is a deploy:
            created.id = this output's outpoint
        if created.id != token.id: continue
        if created is transfer or burn:
            O += created.amt
        else if created is binary value and not hasAuth:
            O += created.amt

    if token is transfer or burn or (binary value and not hasAuth):
        if I < O: fail
        return ok
    if token is mint or auth or (binary value and hasAuth):
        if not hasAuth: fail
        return ok
    fail
```

On failure, the named subjects are **unproven**. On success, what is proven for each subject is the record `(outpoint, id, amt)` decoded from its own script in the verified packet — not any externally supplied claim about that outpoint.

### What this document does not cover

* Unspent status / UTXO set membership
* Global circulating supply
* Basket remittance, tags, display fields — [BRC-163](/tokens/0163.md)
* 1Sat sat ordering — [BRC-159](/tokens/0159.md)

## Examples

**Fixed-supply transfer (one parent):**

```
deploy+mint 10_000 at D
  → transfer 4_900 + transfer 5_100
```

Packet for the 4\_900 tip: subject tx + deploy tx. `I = 10_000 >= 10_000`, so that transfer is valid (the 5\_100 sibling is used only for the conservation check).

**Merge — both branches required:**

```
D 10_000
  → A 6_000 + B 4_000
      → M 10_000   (spends A and B)
```

Packet for `M` MUST include `M`, `A`’s tx, `B`’s tx, and `D`. Omitting `B`: `I = 6_000`, `O = 10_000` → unproven. Spend-as-input of `A` alone is not enough.

**Mint:**

```
deploy+auth at D
  → mint 1_000_000 + auth     (spends D)
```

Packet for the mint: subject tx + `D`. `hasAuth` from a proven `deploy-auth`. Value inputs are not required.

**Over-transfer (fail):**

```
In:  500
Out: 300 + 400 transfer
```

`I < O` → neither transfer is valid.

## Security considerations

* **Claims** — A sender `id` / `amt` / `sym` string is not this packet.
* **Issuer mint policy** — Authority mints are valid if the auth lineage reaches `deploy+auth`. This does not cap global supply.
* **Unspent status** — Not in scope. Same UTXO checks as any BSV payment.

## Implementations

* [b-open-io/1sat-stack](https://github.com/b-open-io/1sat-stack)
* [b-open-io/1sat-sdk](https://github.com/b-open-io/1sat-sdk)

## References

1. [BRC-161](/tokens/0161.md) — BSV-21 Fungible Tokens (JSON / Legacy)
2. [BRC-162](/tokens/0162.md) — BSV-21 Fungible Tokens (Binary)
3. [BRC-163](/tokens/0163.md) — BSV-21 Basket Profile
4. [BRC-62](/transactions/0062.md) — BEEF
5. [BRC-95](/transactions/0095.md) — Atomic BEEF
6. [BRC-96](/transactions/0096.md) — BEEF V2 Txid Only
7. [BRC-158](/transactions/0158.md) — Outpoint BEEF
8. [BRC-159](/tokens/0159.md) — 1Sat origin tracking
9. [BRC-150](/tokens/0150.md) — 1Sat provenance remittance


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://bsv.brc.dev/tokens/0176.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
