BRC-20

A fungible token ledger written in Bitcoin inscriptions

Normative specification · version 1.0.0

BRC-20 specification

This document states the BRC-20 payload format and validity rules as one numbered rulebook. The protocol originated with domo's March 2023 experiment; the maintained indexing rules are published by the Layer1 Foundation with OPI as the open reference implementation. This page restates those rules for implementers and marks every Bitcoin Universe-specific indexing decision explicitly in section 8.

Reading this page

Rules are numbered R1 to R24 and are individually linkable (for example #r10). The payload validator and the test vectors cite these rule numbers. An operation that breaks any applicable rule has no effect on the ledger; there are no partial applications except the last-mint remainder rule R14.

1. Carrier and encoding

A BRC-20 operation is the content of one Ordinals inscription:

{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"}

That single line, inscription number 348020, opened the first BRC-20 ticker. The protocol has no other wire format: no OP_RETURN variant, no binary encoding.

2. Identifiers

Ticker (tick)
The asset identifier. Exactly 4 UTF-8 bytes (R7). The limit counts bytes, not characters: "ordi" is 4 bytes, and a single 4-byte emoji is also a valid ticker. Comparison is case-insensitive (R8): ordi, ORDI, and OrDi are the same asset. Under the self-issuance extension (section 7), a ticker may instead be exactly 5 bytes.
Inscription ID
<reveal-txid>i<index>, for example b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735i0. Identifies one inscription, and therefore one operation.
Address
Balance identity is the output script (scriptPubKey) holding the inscribed satoshi (R22). In practice this is an address, usually taproot.

3. Operations

3.1 deploy

Opens the ledger for a new ticker and fixes its economics forever.

deploy fields
FieldRequiredValueConstraints
pyes"brc-20"Exact lowercase string (R4)
opyes"deploy"Exact lowercase string (R5)
tickyesticker4 UTF-8 bytes; 5 with self_mint (R7)
maxyesmaximum supplyPositive decimal string, amount grammar (R9, R11)
limnoper-mint limitPositive, at most max; defaults to max (R9)
decnodecimal placesInteger string 0 to 18; defaults to 18 (R9)
self_mintno"true"Extension; required for 5-byte tickers (section 7)
{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"}

3.2 mint

Credits new units to the first owner of the mint inscription.

mint fields
FieldRequiredValueConstraints
pyes"brc-20"Exact lowercase string (R4)
opyes"mint"Exact lowercase string (R5)
tickyestickerMust match a confirmed deploy (R12)
amtyesamount to mintPositive, at most lim, decimals within dec (R13)
{"p":"brc-20","op":"mint","tick":"ordi","amt":"1000"}

3.3 transfer

Moves units in two phases: first inscribe the intent, then spend the inscription (section 4).

transfer fields
FieldRequiredValueConstraints
pyes"brc-20"Exact lowercase string (R4)
opyes"transfer"Exact lowercase string (R5)
tickyestickerMust match a confirmed deploy (R12)
amtyesamount to movePositive, at most the available balance at confirmation (R16)
{"p":"brc-20","op":"transfer","tick":"ordi","amt":"100"}

4. Validity rules

Envelope and parsing

  1. The inscription content MUST parse as exactly one JSON object. Content that is not valid JSON, or that parses to an array, string, number, boolean, or null, is not a BRC-20 operation and is ignored entirely.
  2. The inscription content type MUST be text/plain (with or without parameters such as ;charset=utf-8) or application/json. Other content types are not BRC-20 operations.
  3. Every protocol field value MUST be a JSON string. A JSON number, boolean, or null in p, op, tick, max, lim, amt, dec, or self_mint makes the operation invalid. "max": 21000000 (unquoted) is the classic mistake.
  4. The p field MUST equal "brc-20" exactly: lowercase, with the hyphen. Any other value means the inscription is not a BRC-20 operation.
  5. The op field MUST equal "deploy", "mint", or "transfer" exactly, in lowercase.
  6. Keys are case-sensitive. Unknown keys are ignored and do not invalidate an otherwise valid operation. A missing required key makes the operation invalid.

Tickers

  1. tick MUST be exactly 4 bytes when encoded as UTF-8. Under the self-issuance extension a deploy with "self_mint": "true" MAY instead use a ticker of exactly 5 UTF-8 bytes, and mints and transfers of such a ticker carry the same 5-byte value. The limit counts bytes, never characters.
  2. Ticker identity is case-insensitive. Indexers MUST compare tickers after lowercasing and SHOULD preserve the deploy's original casing for display.

deploy

  1. max is required and MUST be positive. lim, when present, MUST be positive and MUST NOT exceed max; when absent it defaults to max. dec, when present, MUST be a string of digits representing an integer from 0 through 18; when absent it defaults to 18. Arithmetic is uint-safe: no value may exceed the uint64 maximum, 18446744073709551615.
  2. First is valid. The first valid confirmed deploy of a ticker owns it. Every later deploy of the same ticker (case-insensitive) is ignored, whatever its fields say. Deployment economics (max, lim, dec) are immutable once indexed.
  3. Numeric values use the amount grammar (0|[1-9][0-9]*)(.[0-9]+)?: base-10, no sign, no exponent, no leading zeros, no lone leading or trailing decimal point, no separators. After trimming trailing zeros, the fractional digits MUST NOT exceed the ticker's dec.

mint

  1. A mint is valid only for a ticker whose valid deploy was confirmed in an earlier block, or earlier in the same block by transaction order. Minting an undeployed ticker has no effect.
  2. amt is required, MUST be positive, MUST respect the amount grammar and the ticker's dec, and MUST NOT exceed the ticker's per-mint lim.
  3. A mint that would push minted supply past max is credited only the remaining amount. Once minted supply equals max, every further mint is invalid. This remainder rule is the protocol's only partial application.
  4. The minted amount credits the available balance of the first owner of the mint inscription: the address that receives the inscribed satoshi in the reveal transaction.

transfer

  1. Inscribing a transfer is valid only if amt is at most the inscribing address's available balance at the moment the transfer inscription is confirmed. A transfer inscription that fails this test is permanently void; a later balance increase never revives it.
  2. A valid transfer inscription immediately moves amt from the owner's available balance to their transferable balance. Overall balance is unchanged; the amount is reserved, exactly once, against that specific inscription.
  3. A transfer inscription is one-use. Only its first spend after the inscription settles it. Any later movement of the same inscription is an ordinary inscription transfer with no BRC-20 effect.
  4. If the first spend delivers the inscription to a different address, the sender's overall balance is debited amt and the recipient's available balance is credited amt.
  5. If the first spend returns the inscription to the sender, the reservation is released: amt moves back from transferable to available and no ownership changes.
  6. If the first spend consumes the inscription as transaction fee (the inscribed satoshi ends up in the miner's fee claim), the inscription is spent and the amount returns to the sender's available balance. No third party is credited.

Ordering and identity

  1. Balances attach to output scripts (scriptPubKey). Operations apply in strict block order: by block height, then by transaction index within the block. Replaying the same blocks under these rules MUST be deterministic.
  2. Mempool state is never protocol state. Every validity decision is made at confirmation, against the confirmed ledger at that point (reference).
  3. Cursed inscriptions (inscriptions assigned negative numbers under the Ordinals rules) are not valid BRC-20 operations.

5. State transitions

Each address holds, per ticker, an available balance and a transferable balance. Overall balance is their sum. The two-phase transfer is the heart of the protocol: it is why a BRC-20 balance can move by spending an inscription in an otherwise ordinary Bitcoin transaction.

Two-phase transfer as double-entry postings A ledger with three columns: sender available, sender transferable, and recipient available. Posting one, inscribe transfer of 100: sender available is debited 100 and sender transferable is credited 100. Posting two has three alternative outcomes for the first spend. To recipient: sender transferable debited 100, recipient available credited 100. To self: sender transferable debited 100, sender available credited 100. As fee: the same restoration as self, with the inscription consumed. Transfer of 100 units, posted double-entry sender recipient available transferable available 1 · inscribe -100 +100 · 2a · to recipient · -100 +100 2b · to self +100 -100 · 2c · as fee +100 -100 · Every posting balances. Overall supply never changes on transfer.
Postings for one transfer inscription of 100 units. Exactly one of 2a, 2b, or 2c happens, decided by the inscription's first spend (R18 to R21).
State transition summary
EventAvailableTransferableOverall
deploy confirmedunchangedunchangedunchanged (opens the ticker)
mint confirmed+amtunchanged+amt
transfer inscribed-amt+amtunchanged
first spend, to recipientrecipient +amt-amtsender -amt, recipient +amt
first spend, to self+amt-amtunchanged
first spend, as fee+amt-amtunchanged

6. Invalid conditions

Two different failure classes exist. Content that fails R1, R2, or R4 is not a BRC-20 inscription at all. Content that passes those but breaks any other rule is an invalid BRC-20 operation: recognized, recorded by some indexers for diagnostics, but with zero ledger effect. Both classes leave balances untouched.

Common invalid conditions
ConditionClassRule
Content is not one JSON objectNot BRC-20R1
p missing or not "brc-20"Not BRC-20R4
Numeric field written as a JSON numberInvalid operationR3
Unknown op valueInvalid operationR5
Ticker not 4 bytes (or 5 with self_mint)Invalid operationR7
Second deploy of an existing tickerIgnored deployR10
dec above 18, or not an integer stringInvalid deployR9
lim greater than maxInvalid deployR9
Value over the uint64 maximumInvalid operationR9
More fractional digits than decInvalid operationR11
Mint of an undeployed tickerInvalid mintR12
amt above limInvalid mintR13
Mint after supply is fully mintedInvalid mintR14
Transfer amt above available balanceVoid transfer inscriptionR16
Second spend of a transfer inscriptionNo effectR18
Cursed inscriptionNot BRC-20R24

7. Extensions

Self-issuance and 5-byte tickers

A governance-approved extension to the original rules allows deploys carrying "self_mint": "true" to claim tickers of exactly 5 UTF-8 bytes. For such tickers only the deployer may mint. Classic 4-byte tickers are unaffected. This site documents the extension because 5-byte tickers exist in the live ledger and Universe indexing accepts 4 and 5 byte tickers for marketplace evidence.

Programmable extensions

Later upstream proposals (often grouped as BRC2.0) add balance-affecting programmable behavior on top of the classic three operations. They are out of scope for this specification. The Bitcoin Universe read model represents classic deploy / mint / transfer economics only and does not index programmable extensions.

8. Universe indexing decisions

Everything below is a Bitcoin Universe implementation decision layered on the upstream rules, stated here so readers can tell the two apart.

9. References