BRC-159 Tokens

1Sat Ordinals — Single-Satoshi Tokens and Origin Tracking

1Sat Ordinals treats a 1-satoshi output as a non-fungible token and tracks it through the chain. Each token has a stable id — its origin — the outpoint where the 1-satoshi chain started. Spending that sat into a new 1-satoshi output under sat ordering moves the token; the origin does not change.

Open Protocol Labs (info@opl.dev) Authors: David Case (dcase@opl.dev), Luke Rohenaz (luke@opl.dev) Contributors: Kurt Wuckert Jr. (kurt@opl.dev), Michael Boyd (root@opl.dev), Dan Wagner (dan@opl.dev)

Implementer view

Where this fits

Defines a 1-satoshi token’s stable origin and transfer rule.

How it connects

Relationships

Found in the source

Implementation links

These links are extracted from the upstream implementation section. Inclusion is not an OpenStandards endorsement.

Authoritative content mirror

Full upstream text

View source
Rendered from tokens/0159.md at the indexed upstream commit

Abstract

1Sat Ordinals treats a 1-satoshi output as a non-fungible token and tracks it through the chain.

Each token has a stable id — its origin — the outpoint where the 1-satoshi chain started. Spending that sat into a new 1-satoshi output under sat ordering moves the token; the origin does not change.

Motivation

Bitcoin SV allows outputs of exactly one satoshi. A single satoshi is already discrete and transferable; 1Sat Ordinals just names it and tracks it.

This document standardizes four ideas:

  1. A token is a chain of 1-satoshi outputs.
  2. Origin is the permanent name of that chain (an outpoint).
  3. The token's current outpoint is whichever 1-satoshi output in the chain is still unspent.
  4. A transfer is a spend that puts that satoshi into a new 1-satoshi output under sat ordering.

Everything else — inscriptions, metadata, names, wallet baskets, marketplaces — can build on this.

Ordinal Theory

1Sat uses the sat-assignment rule from Ordinal Theory: satoshis move from inputs to outputs in order (inputs in order, sats within each input; outputs in order, sats within each output). The nth satoshi spent is the nth satoshi created.

1Sat does not use global sat serial numbers from coinbase. Identity is the origin of a 1-satoshi chain. Background: https://docs.ordinals.com/.

Specification

Outpoints

An outpoint is transaction id + output index.

String forms in common use:

  • underscoretxid_vout (common in 1Sat indexers and OrdFS-style paths)
  • dottxid.vout (common in BRC-100 / overlay APIs; see BRC-36 practice)

Both denote the same outpoint when txid is 64 hex characters and vout is a non-negative decimal integer. Implementations that accept external references MUST treat the two forms as equal after normalization.

Token

A 1Sat token is a chain of 1-satoshi outputs linked by transfers.

  • Only outputs with exactly 1 satoshi are part of a chain.
  • Any locking script is fine. This BRC does not care how the coin is locked.
  • Script data (inscriptions, metadata) is optional; it does not create or name the token.

While held, exactly one output in the chain is unspent — the token's current location.

Origin

The origin is the outpoint of the first 1-satoshi output in the chain. It is the stable id for APIs, indexes, and UIs. Transfers never change it.

Every 1-satoshi output is either the next link after a transfer, or a new origin.

Sat ordering and transfer

Satoshis flow input→output in order. Counts matter, not input/output indexes: a 1-sat input at any index can land at any 1-sat output if the running totals align.

Forward (spend): For a 1-satoshi input, let S_in = (sum of earlier input values) + 1. Find the output where the running output total first reaches S_in.

  • That output value is 1transfer (new location; origin unchanged).
  • That output value is > 1no transfer (chain ends; a fresh 1-sat output that did not receive a transferred sat is a new origin).
  • No output reaches S_inburn (paid as fee).

Finding origin from an outpoint

Start at any 1-satoshi outpoint in a chain and walk backward. When the walk cannot continue as a 1→1 hop, the current outpoint is the origin.

function proveOrigin(outpoint, txs):
    current = outpoint

    loop:
        tx = load current's transaction from txs
        if missing: fail

        if tx.outputs[current.vout].satoshis != 1: fail

        outAcc = 0
        for j in 0 .. current.vout-1:
            outAcc += tx.outputs[j].satoshis

        inAcc = 0
        for each input of tx:
            sourceTx = load input's source transaction from txs
            if missing: fail
            inSats = sourceTx.outputs[input.vout].satoshis

            // Input entirely before our sat — skip.
            if inAcc + inSats <= outAcc:
                inAcc += inSats
                continue

            // This input covers our sat.
            if inSats == 1 and inAcc == outAcc:
                current = input's source outpoint
                continue outer loop

            break   // not a 1→1 hop

        // Stopped walking — this outpoint is the origin.
        return origin = current

Examples

Simple transfer (token input first):

inputs:
  i0  1-sat token      1 sat     ← global sat #1
  i1  funding     10_000 sats     ← sats #2 … #10001

outputs:
  o0  1-sat token      1 sat     ← sat #1          origin unchanged
  o1  payment      4_000 sats     ← sats #2 … #4001
  o2  change       5_998 sats     ← sats #4002 … #10001

Counts, not indexes — funding can come before the token on both sides:

inputs:
  i0  funding A    3_000 sats     ← sats #1 … #3000
  i1  1-sat token      1 sat     ← sat  #3001        (input index 1, not 0)
  i2  funding B    5_000 sats     ← sats #3002 … #8001

outputs:
  o0  payment      2_000 sats     ← sats #1 … #2000
  o1  payment      1_000 sats     ← sats #2001 … #3000
  o2  1-sat token      1 sat     ← sat  #3001        origin unchanged
  o3  change       4_999 sats     ← sats #3002 … #8001

Sats before the token input = 3000 → global sat #3001. Sats before o2 = 3000, o2 value 1 → transfer. If #3001 landed inside a larger output, the chain would not continue.

Two tokens in one transaction:

inputs:
  i0  token A          1 sat     ← sat #1
  i1  token B          1 sat     ← sat #2
  i2  funding     10_000 sats     ← sats #3 …

outputs:
  o0  token A          1 sat     ← sat #1   origin A unchanged
  o1  token B          1 sat     ← sat #2   origin B unchanged
  o2  change       9_998 sats     ← from funding

What this document does not cover

  • Inscription envelopes — BRC-160
  • Metadata, wallets, baskets, tags
  • Offline or wire proof packages
  • Content delivery and application rules (collectables, names, markets, locks, …)

Security considerations

  • Coin selection — Do not spend 1Sat token outputs in ordinary payments unless a transfer is intended.
  • Output order — Builders must place each token satoshi in the intended 1-satoshi output.
  • Claimed origins — A string origin: … is not proof.
  • Proof packages — Offline tip→origin proofs, bundle size, and send hot paths are specified in BRC-150 (and optionally BRC-158). Sat ordering itself is O(inputs+outputs) per hop and is not the scalability bottleneck.

Implementations

References

  1. 1Sat Ordinals — https://docs.1satordinals.com
  2. Ordinal Theory — https://docs.ordinals.com/
  3. BRC-36 — Format for Bitcoin Outpoints
  4. BRC-45 — Definition of UTXOs as Bitcoin Tokens
  5. BRC-160 — 1Sat Ordinals — Inscription Envelopes
  6. BRC-150 — 1Sat Provenance Remittance for Basket 1sat
  7. BRC-158 — Outpoint BEEF