Normative
BLOCK-20 specification
Rules a reader applies to decide what a BLOCK-20 inscription means. Every rule below is drawn from the Bitcoin Universe writer implementation and the bitcoin-universe-block20-v1 reader profile. Rule identifiers are stable.
1. Scope and authority
This document specifies how a BLOCK-20 reader interprets Bitcoin inscriptions, and what a BLOCK-20 writer emits. It is not a Bitcoin consensus rule and not a guarantee that every third-party reader will agree.
Keywords: MUST and MUST NOT describe behavior required for conformance with this profile. Reject means the inscription has no effect on token state. Ignore means the inscription is not an error but still changes nothing.
Two implementations sit behind these rules. The writer is Bitcoin Universe Inscribe, which serializes the payload and broadcasts the inscription. The reader is the bitcoin-universe-block20-v1 profile, which computes ticker state, balances, and supply. The writer performs no token validation. All validity is decided by the reader.
2. Carrier and envelope
- R2.1
A BLOCK-20 message MUST be carried as the content of an Ordinals inscription. The Bitcoin Universe writer reveals it through a taproot script-path spend of a dedicated commit output. The carrier MUST NOT be an
OP_RETURNoutput. - R2.2
The inscription content type MUST match
text/plainorapplication/json, compared case-insensitively, optionally followed by parameters after a semicolon. The writer emitstext/plain;charset=utf-8. Any other content type is ignored. - R2.3
Content length MUST be at least 1 byte and at most 2048 bytes. Empty content and oversized content are ignored without being parsed.
- R2.4
The operating address for an inscription is the address that holds it at the block being processed: the reveal owner for a deploy, mint, or transfer inscription, and the destination for a later move. It MUST be a Bitcoin address supported by the reader for the configured network. Bech32 addresses MUST be entirely lowercase or entirely uppercase, and MUST carry the
bc1prefix on mainnet ortb1on testnet. Base58 addresses MUST start with1or3on mainnet, orm,n, or2on testnet. - R2.5
A deploy, mint, or transfer inscription whose operating address is absent or unsupported MUST be rejected. For a transfer settlement, an absent or unsupported destination is not a rejection: it settles as a burn (see R7.9).
- R2.6
A reader MUST associate a BLOCK-20 reveal with the output it was inscribed into. If the inscription has already moved away from its reveal transaction before the reader observes it, the reader MUST fail closed rather than guess an owner.
3. Payload grammar
BLOCK-20 does not accept general JSON. It accepts a flat object whose keys and values are all JSON strings, parsed by a strict grammar. This is deliberate: it removes the encoding ambiguity that lets two readers disagree about the same bytes.
- R3.1
Content MUST decode as strict UTF-8. Any invalid sequence, including an unpaired surrogate or an overlong encoding, rejects the inscription.
- R3.2
The decoded text MUST be exactly one JSON object. Whitespace is permitted before the opening brace, around keys, colons, commas, and after the closing brace. Any non-whitespace content after the closing brace rejects the inscription.
- R3.3
Every key and every value MUST be a JSON string. A number, boolean,
null, array, or nested object anywhere in the payload rejects the inscription.{"amt":5}is invalid;{"amt":"5"}is the valid form. - R3.4
A repeated key rejects the inscription. The reader MUST NOT apply last-wins or first-wins recovery.
- R3.5
A raw control character below
U+0020inside a string rejects the inscription. Escaped forms such as\nare parsed as JSON escapes and are permitted by the grammar, though no BLOCK-20 field accepts a control character in its value. - R3.6
An unterminated string, a missing colon between key and value, or a missing comma between members rejects the inscription.
- R3.7
A payload without a
pfield, or whosepis not exactly the lowercase stringblock-20, is not a BLOCK-20 message and MUST be ignored. The comparison is case sensitive and exact. - R3.8
Each operation defines an exact field set. A payload missing a required field, or carrying any field outside the allowed set for its operation, MUST be rejected. There are no reserved extension fields.
- R3.9
opMUST be exactlydeploy,mint, ortransfer. Any other value is ignored.
deploy required: p, op, tick, max, lim optional: des
mint required: p, op, tick, amt, hash optional: none
transfer required: p, op, tick, amt optional: none
4. Identity and numbers
- R4.1
tickMUST match^[A-Za-z0-9]{1,5}$: one to five ASCII letters or digits. No spaces, punctuation, emoji, or non-ASCII characters. - R4.2
Ticker identity is the lowercase form of
tick.BLK,Blk, andblkare the same token. The display form recorded for a ticker is the exact casing of its deploy. - R4.3
The first valid deploy of an identity wins. Every later deploy of the same identity MUST be ignored, whatever supply or limit it declares.
- R4.4
Every numeric field (
max,lim,amt) MUST match^[1-9]\d*$. Leading zeros, a leading+or-, a decimal point, an exponent, whitespace, thousands separators, and the value0are all invalid. - R4.5
Numeric values MUST NOT exceed 2128 minus 1, that is
340282366920938463463374607431768211455. - R4.6
BLOCK-20 has zero decimal places. Every amount is a whole unit. There is no smaller subdivision and no display scaling.
5. Deploy
{"p":"block-20","op":"deploy","tick":"BLK","max":"21000000","lim":"1000"}
- R5.1
Field set is exactly
p,op,tick,max,lim, withdespermitted as the only optional field. - R5.2
maxis the maximum supply andlimis the maximum amount creditable by a single mint. Both MUST satisfy R4.4 and R4.5. - R5.3
limMUST NOT exceedmax. A deploy declaring a per-mint limit larger than the whole supply is rejected. - R5.4
des, when present, MUST be at most 256 bytes of UTF-8 and MUST NOT contain a control character. It is descriptive metadata with no effect on token state. - R5.5
A deploy has no economic effect. Total, circulating, minted, and burned supply all start at zero. The deploying address receives nothing.
- R5.6
The deploy height is the height of the block that reveals the deploy inscription. It becomes the lower bound for every mint anchor of that ticker (R6.6).
- R5.7
Deployment metadata is immutable. A reader MUST NOT allow a later inscription to change a ticker's display form, decimals, maximum supply, or deployer.
6. Mint and the anchor
{"p":"block-20","op":"mint","tick":"BLK","amt":"1000","hash":"<64 lowercase hex>"}
The anchor is the part of BLOCK-20 that is not shared with other inscription token protocols. A mint does not merely claim an amount, it names a block, and the reader tests that claim against the chain.
6.1 The anchor derivation
Let H be the value of hash, R the height of the block revealing the mint inscription, D the ticker's deploy height, and W the reader's freshness window in blocks.
step 1 H matches ^[0-9a-f]{64}$ else reject
step 2 A := height of the block whose ID is H,
resolved against the active chain else reject
step 3 A >= D else reject
step 4 A < R else reject
step 5 R - A <= W else reject
step 6 remaining := max - minted
if remaining <= 0 then ignore
step 7 credited := min(amt, remaining)
- R6.1
Field set is exactly
p,op,tick,amt,hash. No optional fields. - R6.2
The ticker MUST have an active deployment at the mint's block. A mint for an unknown or deactivated ticker is ignored.
- R6.3
amtMUST satisfy R4.4 and MUST NOT exceed the deployment'slim. A mint above the per-mint limit is rejected outright and is not reduced to the limit. - R6.4
hashMUST match^[0-9a-f]{64}$. Uppercase hex, a0xprefix, surrounding whitespace, a truncated hash, a transaction ID that is not a block ID, an inscription ID, a numeric height, a Merkle root, and a payload digest are all rejected by this rule or by R6.5. - R6.5
The reader MUST resolve
hashto a height on the active chain. Resolution succeeds only when a Bitcoin node knows the header, the header has at least one confirmation, and the block currently at that height hashes to the same value. A header from an orphaned branch fails this test, so its hash cannot anchor a mint. - R6.6
The anchor height MUST be greater than or equal to the ticker's deploy height. A mint cannot point at a block from before the ticker existed.
- R6.7
The anchor height MUST be strictly less than the reveal height. A mint cannot anchor to its own block or to any later block.
- R6.8
The distance
R - AMUST NOT exceed the freshness window. The reference reader uses 144 blocks by default, roughly one day of Bitcoin. The window is an operator setting in the range 1 to 10000, so a mint near the boundary can be accepted by one reader and rejected by another. - R6.9
If remaining supply (
maxminus already-minted) is zero or less, the mint is ignored and produces no event. Supply is not exceeded and no partial credit is given after exhaustion. - R6.10
When remaining supply is positive but smaller than
amt, the mint is credited for exactly the remaining amount. This partial final mint is valid, and the reader records both the requested and credited amounts. - R6.11
A credited mint increases the reveal owner's balance, total supply, circulating supply, and minted supply by the credited amount. Burned supply is unchanged.
- R6.12
Failing any of R6.3 through R6.8 credits nothing. There is no partial credit for an invalid anchor.
Where the writer gets the hash. Bitcoin Universe Inscribe reads the current chain tip from its configured block source while creating the order, and writes that block ID into both the order record and the payload. It selects the value before funding, reveal, or confirmation, and it does not refresh the value before reveal. Because the tip is always mined before the later reveal block, a normal order satisfies R6.7 naturally. It can still fail R6.8 if the order sits unfunded for more than the freshness window.
7. Transfer
{"p":"block-20","op":"transfer","tick":"BLK","amt":"250"}
A transfer is two chain events, not one. Inscribing reserves an amount. Moving the inscription decides where that amount lands.
- R7.1
Field set is exactly
p,op,tick,amt. - R7.2
The ticker MUST have an active deployment. A transfer inscription for an unknown ticker is ignored.
- R7.3
Available balance is the owner's balance minus the sum of that owner's unsettled reservations for the same ticker. If available balance is less than
amt, the transfer inscription is ignored and no reservation is created. - R7.4
An inscription that already holds a reservation MUST NOT create a second one.
- R7.5
Revealing a valid transfer inscription creates a reservation. Holder balance and every supply figure are unchanged. Only available balance falls.
- R7.6
The first later move of that inscription settles the reservation. Settlement is evaluated once and only once.
- R7.7
If the move sends the inscription to a supported address, the reserved amount moves from the owner to that address. Supply figures are unchanged. When the destination equals the owner, the reservation is released and the balance is unchanged, with the movement still recorded.
- R7.8
If the inscription is spent in transaction fees, the reservation is released back to the owner. No balance and no supply figure changes.
- R7.9
If the inscription is burnt, or moves to a destination the reader cannot interpret as a supported address, the reserved amount is destroyed. The owner's balance falls, total and circulating supply fall, and burned supply rises by the same amount. Minted supply is unchanged, so a burn does not free capacity for new mints.
- R7.10
Any move of the inscription after settlement has no token effect.
8. Ordering and state transitions
- R8.1
Blocks MUST be processed in ascending height with no gaps. A reader that cannot obtain a contiguous range MUST stop rather than skip.
- R8.2
Within a block, items are ordered by transaction index ascending. Where a move and a new inscription share a transaction index, the move is processed first. Ties are broken by ordinal (block transfer index for a move, inscription number for a reveal), then by inscription ID.
- R8.3
State is applied in that order within the block. Two mints in the same block compete for the same remaining supply, and the earlier one wins.
- R8.4
A deploy and a mint of the same ticker in the same block are valid when the deploy is ordered first. In that case the anchor lower bound (R6.6) is that same block height, so the anchor must be an earlier block, which R6.7 already requires.
- R8.5
Each accepted operation MUST produce a stable event identity that does not change when the same block is replayed. The reference reader derives it from the operation and the inscription ID, plus the moving transaction ID for a settlement.
- R8.6
Minted supply never decreases through a burn. The supply invariant is: total equals minted minus burned, and circulating equals total.
9. Invalidity conditions
Every condition below results in no change to token state. The distinction between "ignored" and "rejected" matters only for reader diagnostics.
| Condition | Applies to | Rule |
|---|---|---|
| Content type is not text/plain or application/json | All | R2.2 |
| Content is empty or larger than 2048 bytes | All | R2.3 |
| Operating address absent or unsupported for the network | Deploy, mint, transfer reveal | R2.5 |
| Content is not strict UTF-8 | All | R3.1 |
| Trailing content after the JSON object | All | R3.2 |
| Any non-string key or value | All | R3.3 |
| Duplicate field | All | R3.4 |
| Raw control character in a string | All | R3.5 |
| Missing required field or any unknown field | All | R3.8 |
| Ticker outside one to five ASCII alphanumerics | All | R4.1 |
| Amount with leading zero, sign, decimal point, or value zero | All numeric fields | R4.4 |
| Amount above 2128 minus 1 | All numeric fields | R4.5 |
| Ticker identity already deployed | Deploy | R4.3 |
| Per-mint limit exceeds maximum supply | Deploy | R5.3 |
| Description longer than 256 bytes or containing a control character | Deploy | R5.4 |
| Ticker has no active deployment | Mint, transfer | R6.2, R7.2 |
| Mint amount above the per-mint limit | Mint | R6.3 |
| Hash not 64 lowercase hex characters | Mint | R6.4 |
| Hash does not resolve to a block on the active chain | Mint | R6.5 |
| Anchor height below the deploy height | Mint | R6.6 |
| Anchor height at or above the reveal height | Mint | R6.7 |
| Anchor older than the freshness window | Mint | R6.8 |
| Supply already exhausted | Mint | R6.9 |
| Available balance below the transfer amount | Transfer | R7.3 |
| Inscription already holds a reservation | Transfer | R7.4 |
| Move of an inscription that is already settled | Transfer settlement | R7.10 |
10. Reorganization and finality
- R10.1
When a block the reader has applied is no longer on the active chain, the reader MUST invalidate every event from the replaced branch before applying the replacement branch.
- R10.2
Invalidation MUST proceed newest first: descending block height, then descending transaction index, then descending event index.
- R10.3
Invalidation MUST reverse the exact recorded wallet and supply deltas of each event, restore transfer reservations that were settled on the replaced branch, and remove reservations that were created on it.
- R10.4
Invalidating a deploy deactivates the ticker. A reader MUST NOT leave an applied mint or transfer depending on an inactive deployment, and MUST fail closed if that state is reached.
- R10.5
Events at or below the finalized height MUST NOT be invalidated. A reorganization that would require this is a fatal condition: the reader stops rather than producing a state it cannot justify.
- R10.6
The finalized height is the tip height minus the reader's finality depth, defaulting to 6 confirmations, and it MUST NOT move backward.
- R10.7
Because R6.5 resolves anchors against the active chain at the time of processing, a mint whose anchor block is orphaned by a reorganization does not survive the replay. The same inscription bytes can be valid on one chain history and invalid on another.
11. Conformance
An implementation conforms to bitcoin-universe-block20-v1 when it satisfies every MUST above and reproduces the outcomes in the test vectors. At a minimum, cover:
- the flat string object grammar, including duplicate keys, non-string values, trailing content, and control characters;
- exact field sets for each operation and rejection of unknown fields;
- ticker case folding and first-deploy-wins;
- every branch of the anchor derivation, including the exact boundaries at
A = D,A = R - 1,A = R, andR - A = W; - partial final mints and the ignoring of mints after exhaustion;
- reservation, transfer settlement, fee return, burn, and inert later moves;
- within-block ordering with two competing mints;
- reorganization invalidation ordering and the finalized-height refusal.
A reader that relaxes any rule here MUST NOT describe itself as implementing this profile, because a relaxed rule changes balances.