Conformance
Test vectors
Two sets. Payload vectors are decided from the JSON alone, so a parser can be tested against them with no chain state. Ledger vectors are sequences of operations with the exact resulting balances, which is where implementations actually diverge. Every expected outcome cites the rule that produces it.
Paste any payload vector into the validator to see the same
verdict rendered check by check. The ledger vectors are stated as postings so they can be
compared against an indexer's own event stream. Addresses are written as
A, B, and C to keep the arithmetic readable; a real
implementation keys balances by output script (R22).
1. Payload vectors
Each vector is decided by payload rules only. "Valid" here means the payload is well-formed; whether it takes effect still depends on chain state, which the ledger vectors cover.
1.1 Valid payloads
{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"}
The original ordi deploy. Four-byte ticker, positive
max, lim at or below max, dec omitted so
divisibility is 18 (R7, R9).
{"p":"brc-20","op":"deploy","tick":"tst4","max":"1000","lim":"10","dec":"2"}
dec of 2 means amounts for this ticker may carry at most
two decimal places. lim of 10 caps each mint; 100 mints are needed to exhaust
the supply (R9).
{"p":"brc-20","op":"mint","tick":"ordi","amt":"1000"}
All required fields present as strings. Effect depends on the deploy's
lim and remaining supply (R13,
R14).
{"p":"brc-20","op":"transfer","tick":"ordi","amt":"100"}
Well-formed. Validity at confirmation also requires 100 available at the holding address (R16).
{"p":"brc-20","op":"deploy","tick":"gamma","max":"1000","self_mint":"true"}
gamma is 5 UTF-8 bytes, permitted only because
self_mint is the string "true". Omitting lim means the
per-mint limit equals max (extensions).
{
"p": "brc-20",
"op": "mint",
"tick": "ORDI",
"amt": "1.500",
"memo": "ignored"
}
Whitespace is irrelevant. "ORDI" is the same ticker as
ordi (R8). "1.500" trims to one
significant decimal place, so it fits any dec of 1 or more
(R11). Unknown keys are ignored
(R6).
1.2 Invalid payloads
{"p":"brc-20","op":"deploy","tick":"ordi","max":21000000,"lim":"1000"}
The single most common mistake. max is an unquoted JSON
number; every BRC-20 value must be a JSON string. Write "max":"21000000"
(R3).
{"p":"brc20","op":"mint","tick":"ordi","amt":"10"}
p must be exactly "brc-20", with the hyphen.
No BRC-20 indexer reads this inscription at all (R4).
{"p":"brc-20","op":"Mint","tick":"ordi","amt":"10"}
op values are case-sensitive and lowercase. Ticker
comparison is case-insensitive, but nothing else is
(R5, R8).
{"p":"brc-20","op":"mint","tick":"ord","amt":"10"}
Three UTF-8 bytes. A ticker is exactly 4 bytes, or 5 under the self-mint extension (R7).
{"p":"brc-20","op":"deploy","tick":"gamma","max":"1000"}
Same ticker as V5 but no "self_mint":"true", so the
5-byte length is not permitted (R7).
{"p":"brc-20","op":"deploy","tick":"dec9","max":"1000","dec":"20"}
Divisibility is capped at 18 decimal places (R9).
{"p":"brc-20","op":"deploy","tick":"lim9","max":"1000","lim":"5000"}
lim may not exceed max
(R9).
{"p":"brc-20","op":"mint","tick":"ordi","amt":"0"}
Amounts must be positive. Zero-value operations are not a way to signal anything (R9).
{"p":"brc-20","op":"mint","tick":"ordi","amt":"1.0000000000000000001"}
Nineteen fractional digits, above the protocol maximum of 18. Against a
ticker with a smaller dec, correspondingly fewer digits are allowed
(R11).
{"p":"brc-20","op":"deploy","tick":"big4","max":"18446744073709551616"}
One above the uint64 maximum, 18446744073709551615. Indexers apply uint-safe arithmetic (R9).
{"p":"brc-20","op":"transfer","tick":"ordi","amt":"0100"}
The amount grammar forbids leading zeros. Write "100"
(R11).
{"p":"brc-20","op":"mint","tick":"ordi"}
A mint requires amt. There is no default
(R13).
{"p":"brc-20","op":"mint","tick":"ordi","amt":"10",}
Trailing comma. The content does not parse as JSON, so it is not an operation at all (R1).
[{"p":"brc-20","op":"mint","tick":"ordi","amt":"10"}]
An operation is one JSON object. Batching several operations into one inscription is not part of the protocol (R1).
2. Ledger vectors
These fix the behavior that payload checking cannot: ordering, supply accounting, the two-phase transfer, and reversal. Each vector lists operations in block order and the exact resulting state.
L1 · First is valid
| Block | Operation | Outcome |
|---|---|---|
| 900001 | {"p":"brc-20","op":"deploy","tick":"tst4","max":"1000","lim":"10"} | Applied. tst4 is now deployed with max 1000, lim 10. |
| 900002 | {"p":"brc-20","op":"deploy","tick":"TST4","max":"9999999","lim":"9999999"} | Ignored. Same ticker after lowercasing. |
| 900003 | {"p":"brc-20","op":"mint","tick":"tst4","amt":"10"} by A | Applied against the first deploy: 10 is within lim 10. |
| 900003 | {"p":"brc-20","op":"mint","tick":"tst4","amt":"5000"} by A | Invalid. 5000 exceeds lim 10 from the owning deploy. |
Expected state: tst4 has max 1000, lim 10, dec 18, minted
supply 10. A holds 10 available. The second deploy never existed as far as the ledger is
concerned (R10, R8).
L2 · The mint remainder rule
Ticker rem4 deployed with max 1000 and lim 400.
| # | Operation | Credited | Minted supply |
|---|---|---|---|
| 1 | A mints 400 | 400 | 400 |
| 2 | B mints 400 | 400 | 800 |
| 3 | C mints 400 | 200 | 1000 |
| 4 | A mints 1 | 0 | 1000 |
Expected state: A holds 400, B holds 400, C holds 200. Operation 3 is credited only the 200 remaining, not the 400 requested, and not rejected. Operation 4 is invalid because the supply is exhausted (R14).
Trap: an implementation that rejects operation 3 outright, or credits it the full 400, diverges from every other indexer from that block onward.
L3 · Two-phase transfer, all three outcomes
A holds 1000 ordi, all available. Each row starts from that state.
| Sequence | A available | A transferable | B available |
|---|---|---|---|
| Start | 1000 | 0 | 0 |
| A inscribes transfer of 100 | 900 | 100 | 0 |
| …then spends it to B | 900 | 0 | 100 |
| …or spends it back to A | 1000 | 0 | 0 |
| …or spends it as fee | 1000 | 0 | 0 |
| …then moves the spent inscription again | No further BRC-20 effect, whatever it does. | ||
Expected behavior: the inscribe step never changes A's overall balance; it only reclassifies 100 from available to transferable. Self and fee spends both restore availability and credit nobody. The second and later spends of the same inscription are inert (R17 to R21).
Trap: collapsing inscribe and spend into one event produces the right final balances for the recipient case and the wrong ones for self and fee, and it breaks state hashes against implementations that emit both events.
L4 · Over-reservation and permanent voiding
| Block | Operation | Outcome |
|---|---|---|
| 900010 | A holds 100 available; A inscribes transfer of 60 | Valid. A: 40 available, 60 transferable. |
| 900011 | A inscribes transfer of 60 again | Void. Available balance is 40, not 100. |
| 900012 | A receives 500 more ordi | A: 540 available, 60 transferable. |
| 900013 | A spends the block 900011 inscription to B | No effect. It was void at confirmation and stays void. |
| 900014 | A spends the block 900010 inscription to B | Settles: A 540 overall, B credited 60. |
Expected state: A holds 540 available and 0 transferable; B holds 60 available. The later balance increase never revives the void inscription (R16).
L5 · Reorg reversal
| Step | Event | Expected state |
|---|---|---|
| 1 | Block 900001: A mints 1000 tst4 | A: 1000 available. |
| 2 | Block 900002: A inscribes transfer of 250, then spends it to B | A: 750 overall; B: 250 available. |
| 3 | Blocks 900001 and 900002 are orphaned | Both blocks' events are reversed: A and B hold nothing, minted supply returns to 0. |
| 4 | Replacement block 900001' contains only the mint | A: 1000 available; B: 0. The transfer never happened. |
Expected behavior: the state after step 4 must be identical to a clean replay of the new chain. Reversal must restore both sides of the transfer and return the reservation, and reverting a deploy must revert every mint and transfer that depended on it.
Trap: reversing only the settlement and leaving the reservation behind leaves A with a phantom transferable balance that no clean replay would produce. Reversal below a declared finality depth should be refused as an error rather than applied.
3. Where these come from
Payload vectors follow the rules in the specification as published by domo and maintained by the Layer1 Foundation, with OPI as the reference implementation. Ledger vectors are stated to match the behavior the Bitcoin Universe BRC-20 read model enforces: per-mint limit and maximum supply invariants, immutable deploy economics, case-insensitive ticker identity with preserved display casing, separate transfer-inscribe and first-spend events with recipient, self, and fee dispositions, integer-only arithmetic on scaled amounts, and transactional reorg reversal that refuses to cross the finalized height. Behavior that is not implemented in the org's own code is not asserted here.