Normative document
Atomicals + ARC-20 specification
The rules below are stated as the indexer enforces them. The normative source is the Bitcoin Universe Atomicals indexer, a pinned fork of atomicals-electrumx; file references name modules inside it. The protocol originated with the upstream Atomicals project, and where this fork adds or narrows behavior the rule says so.
1. The atom envelope
Atomicals operations travel inside taproot leaf scripts revealed in witness data. The carrier is an ordinary Bitcoin transaction; consensus never sees the protocol.
OP_IF and expects the marker 0461746f6d immediately after it.- A witness element is considered only if it is at least 39 bytes long and its first byte is
0x20, a 32 byte push. Source:parse_protocols_operations_from_witness_for_inputinelectrumx/lib/util_atomicals.py. - After the key push the parser scans forward to the first
OP_IF(0x63). The five bytes immediately following it MUST be0461746f6d, a four byte push of ASCIIatom. Bytes between the key push andOP_IF, typicallyOP_CHECKSIG, are not interpreted, and a leadingOP_FALSEis not required by this implementation. - The operation name follows as a single push: three letter operations as
0x03plus name, two letter as0x02plus name, one letter as0x01plus name. An unknown code causes the envelope to be skipped, not the transaction to be rejected. - The payload is the concatenation of the data of every subsequent push, including
OP_PUSHDATA1,OP_PUSHDATA2, andOP_PUSHDATA4, up toOP_ENDIF. This is how payloads exceed the 520 byte single push limit. - All inputs are scanned in order; the first successfully parsed envelope wins and its input index is recorded. Most operations are valid only when that index is 0, see rule R1.
- Payment markers use a separate carrier: an
OP_RETURNoutput of the form6a 04 "atom" 01 "p"|"d" 24 <36 byte atomical id>, wherepmarks a subrealm payment andda dmitem payment. See rule N10.
2. CBOR payload
- The payload bytes MUST decode as a single CBOR map. Any other CBOR type, or undecodable bytes, invalidates the operation.
- The reserved top level keys
meta,args,ctx, andinitMUST be maps when present. Values inside them are restricted to integers, floats, strings, lists, and nested maps. Byte strings are rejected insidemetaandctxalways, and insideargsbefore activation height 828128.initmay always carry bytes. - Functional parameters live in
args. Recognized keys includerequest_ticker,request_realm,request_subrealm,request_container,request_dmitem,mint_ticker,claim_type,parent_realm,parent_container,parents,i,bitworkc,bitworkr,mint_bitworkc,mint_bitworkr,mint_height,mint_amount,max_mints,md,bv,bci,bri,bcs,brs,maxg,proof, andmain. - At most one name request may appear per mint. More than one of
request_realm,request_subrealm,request_container,request_ticker, orrequest_dmitemin the sameargsinvalidates the mint. - On an
nft,ft, ordftmint, every top level payload key other thanargsis stored as the object's initial state, equivalent to a firstmod. File content conventionally lives under named keys with metadata such as$ctfor content type alongside raw bytes. args.parents, when present, maps compact atomical ids to minimum satoshi values. Each listed parent atomical MUST be spent among the transaction's inputs carrying at least that total input value, or the mint is invalid.
3. Identifiers
- An atomical id is 36 bytes: the commit transaction id (32 bytes) plus the commit output index (4 bytes, little endian). The commit outpoint is the prevout of the input that carried the reveal envelope. Compact text form is
<txid_hex>i<index>, with the index bounded to 0 through 100000. - A location id has the same 36 byte shape but names where an atomical currently sits: the transaction id and output index of the UTXO holding it.
- On mint the atomical is imprinted at output 0 of the reveal transaction. The reveal location index is always 0.
- A secondary reference form is the Crockford base 32 encoding of the byte reversed commit transaction id, lowercased, suffixed
i0.
4. Commit and reveal
- Every mint operation (
nft,ft,dft,dmt, and this fork'sdefandnew) MUST carry its envelope on input 0 of the reveal transaction. - The commit transaction MUST itself be indexed and MUST be at or after the protocol activation height 808080.
- General window: a mint reveal is invalid unless
commit_height >= reveal_height - 100. - Name window: mints requesting any name (realm, subrealm, container, ticker, dmitem) are additionally invalid unless
commit_height >= reveal_height - 3. Late reveals are recorded with statusexpired_revealed_late. - From activation height 822800 the commit outpoint index MUST be 0 for name type mints and for
dmtmints. - The reveal transaction MUST have at least one output; the mint imprints on output 0, see rule I3.
dmtclaims are exempt from R3 and R4; their height gates are M3 and M4 instead.
5. Operations
| Op | Push hex | Meaning | Family |
|---|---|---|---|
nft | 036e6674 | Mint a non-fungible token | NFT |
ft | 026674 | Mint a fungible token with a direct fixed supply | ARC-20 |
dft | 03646674 | Deploy a decentralized mint fungible token | ARC-20 |
dmt | 03646d74 | Mint units of a deployed dft token | ARC-20 |
mod | 036d6f64 | Modify general state | state |
evt | 03657674 | Event or message, no state change | state |
dat | 03646174 | Store data on a transaction | data |
sl | 02736c | Seal an NFT and lock it forever | NFT |
x | 0178 | Splat: separate co-located NFTs into distinct outputs | transfer |
y | 0179 | Split: skip output value before coloring, per token | transfer |
z | 017a | Custom color: direct token value at chosen outputs, from height 848484 | transfer |
def, new | 03646566, 036e6577 | AVM protocol definition and contract instantiation. These are additions in the Bitcoin Universe fork's AVM support, not part of the original upstream operation set. | AVM |
A transaction that spends atomicals-bearing UTXOs with no envelope at all is a plain transfer, and the coloring rules in section 8 still apply to it.
6. ARC-20 issuance
6.1 Tickers
- A ticker MUST match
^[a-z0-9]{1,21}$: lowercase letters and digits only, 1 to 21 characters. No hyphens, no uppercase, no unicode. - Ticker claims resolve by earliest commit: among valid candidates the one with the lowest commit transaction number wins. Bitwork is an admission requirement, never a tiebreaker.
- A ticker request MUST carry commit bitwork (
args.bitworkc) with a prefix of at least 4 hex characters, like all top level name types. See rule N2. - A ticker claim becomes
verifiedonce its commit is at least 3 blocks deep; until then it ispending. A later mint attempt is rejected outright if any existing candidate committed earlier.
6.2 Direct mints (ft)
- An
ftmint MUST include a validargs.request_tickerand MUST NOT setargs.i; fungible tokens cannot be immutable. - The entire fixed supply equals the satoshi value of output 0 of the reveal transaction. Every supply unit is one colored satoshi from birth.
- The minted token UTXO is recorded at reveal output 0 with token value equal to satoshi value.
6.3 Decentralized mint deploys (dft)
- A
dftdeploy MUST include a validrequest_tickerand integersmint_amountin 546 to 100000000,mint_heightin 0 to 10000000, andmax_mintsof at least 1.max_mintsis capped at 500000 before activation height 828128 and at 21000000 from it. - Optional
mint_bitworkcandmint_bitworkrMUST be valid bitwork strings if present; they set the proof-of-work every future claim's commit or reveal transaction id must satisfy. args.iis forbidden.args.md, the mint mode, MUST be 0, 1, or absent, andmd = 1selects perpetual mode which is legal only from height 828128.- Perpetual mode requires a bitwork vector
bvmatching^[a-z0-9]+$of length at least 4; at least one ofbci(commit increment) orbri(reveal increment), each an integer in 0 to 64; a matching startbcsorbrsin 64 to 256, defaulting to 64, whenever an increment is set;max_mintsof at most 100000 per difficulty phase; and an optional global capmaxgin 1 to 21000000.mint_bitworkcandmint_bitworkrMUST NOT be set in perpetual mode. - Declared maximum supply: in fixed mode
mint_amount × max_mints; in perpetual mode withmaxg,mint_amount × maxg; in perpetual mode withoutmaxg, unbounded. - The deploy creates no token UTXO. Supply exists only as claims are minted under the M rules.
6.4 Mint claims (dmt)
- A claim names its token with
args.mint_ticker, which MUST resolve to averifieddeploy of the decentralized subtype. - The envelope MUST be on input 0, and from height 822800 the commit outpoint index MUST be 0.
- Both the claim's block height and its commit transaction's height MUST be at or above the deploy's mint height. An earlier claim is a premature mint and is invalid.
- Output 0 of the claim MUST carry exactly
mint_amountsatoshis. Any other value invalidates the claim. - In fixed mode the claim is valid only while the number of prior valid claims is below
max_mints. Ifmint_bitworkcis set the commit transaction id MUST satisfy it; ifmint_bitworkris set the reveal transaction id MUST satisfy it. - In perpetual mode the required bitwork for the current claim is derived from the phase schedule in rule B4, checked against the commit transaction id for
bciand the reveal transaction id forbri. From height 828628 a transaction id that satisfies the next longer prefix ofbvis also accepted, so a miner who overshoots a phase boundary does not lose the work. Wheremaxgis set, claims stop at the global cap. - A valid claim writes a token UTXO at output 0 with token value equal to satoshi value. At each block boundary the indexer re-counts claims from durable state and fails hard on any count above the cap, so no inflation is possible.
7. Bitwork proof-of-work
- A bitwork string is
<prefix>or<prefix>.<ext>, where the prefix is 1 to 64 lowercase hex characters andextis an integer 0 to 15. Example:123af.15. - A transaction id satisfies bitwork when its hex form starts with the prefix, and, when
extis present, the hex digit immediately after the prefix has numeric value at leastext. Anextof 0 is equivalent to no extension and anextof 15 is equivalent to one more prefix character. - A bitwork string encodes a difficulty target of
16 × prefix_length + ext. - Perpetual dmint difficulty: with
prior_mintsclaims recorded,current_target = start + floor(prior_mints / max_mints) × increment. The required string is derived by paddingbvwith zeros to 32 characters, takingfloor(target / 16)characters, and appending.<target mod 16>when that remainder is nonzero. Valid ranges are start 64 to 256, increment 1 to 64, andmax_mints1 to 100000. - Request level bitwork on any mint:
args.bitworkcis validated against the commit transaction id andargs.bitworkragainst the reveal transaction id. A declared but unmet bitwork invalidates the whole operation.
8. ARC-20 coloring rules
Coloring decides, for every transaction that spends token bearing UTXOs, which outputs inherit which token value. Two era switches matter: FIFO ordering from height 819181, and partial coloring plus the z operation from height 848484. Source: electrumx/lib/atomicals_blueprint_builder.py. The simulator executes exactly these rules.
- Value model. Each token bearing UTXO records a satoshi value and a token value. Classically they are equal, one unit to one satoshi. From height 848484 the token value may be lower than the satoshi value, a partially colored output, but never higher.
- Ordering. When several tokens enter one transaction they are processed first in, first out: by the first input index where each token appears, ties broken by ascending atomical id. Before height 819181 the order was ascending atomical id alone.
- Regular assignment. Each token, in order, colors outputs left to right starting at the output after the last one the previous token colored.
- Unspendable outputs. Outputs whose script is
OP_RETURN, with or without a leadingOP_FALSE, are skipped by regular assignment and never carry color. The split and custom color paths do not apply this filter, so directing value at an unspendable output underzdestroys that value. - Exact cover era, heights 819181 to 848483. An output could be colored only when the token's remaining value covered its entire satoshi value. Leftover value burned, and a token that could not be placed at all triggered rule C7.
- Partial coloring era, height 848484 onward. A token colors consecutive outputs and the final output it touches may be partially colored with whatever value remains. Value exceeding the total satoshis of the remaining outputs burns.
- Non-clean fallback. If any token fails to produce a clean assignment, or finds no colorable output at its starting position, the entire map is discarded and every token restarts assignment from output 0. In this fallback multiple tokens can color the same outputs, and leftovers burn per token.
- Split (
y). With a split envelope on input 0 the payload maps each compact atomical id to a non-negative satoshi amount to skip: outputs are passed over until their cumulative satoshi value reaches that amount, then coloring proceeds as in C5 or C6. This is how two tokens sharing one input UTXO are separated. NFTs present in a split transaction are forced to output 0. - Custom color (
z), from height 848484. The payload maps each compact atomical id to a map of output index to token value. Requested values are clamped to the output's satoshi value and to the token's remaining value, so no inflation is possible; value assigned to no output burns; an output colored below its satoshi value is partial. - NFT movement. NFTs move by input order: the n-th NFT bearing input maps to output n in the FIFO era. An out of range or unspendable target, or a split transaction, sends the NFT to output 0. The
xsplat operation gives each co-located NFT its own output in ascending id order, overflowing to output 0. NFTs are deliberately hard to burn by accident. - Burns. Token value that lands on no spendable output under these rules is recorded as burned and permanently leaves the supply. Burned value is reported per token by the indexer.
- Inflation guard. The summed colored output value of a token can never exceed its summed input value; the indexer treats any violation as a fatal error, and its broadcast time validator rejects raw transactions whose blueprint would burn or inflate unexpectedly.
9. State, sealing, immutability
modupdates an atomical's state. History folds in ascending transaction order: a payload with$a = 1deletes the named keys, where a value oftruedeletes and a nested map recurses; any other payload sets keys by recursive merge.argsand$aare never stored.evtrecords a message against an atomical without changing state, and like mints must ride input 0.- An NFT minted with
args.i = trueis immutable, and nomodorevtis ever applied to it. Realms, subrealms, and containers can never be immutable, and neither can fungible tokens. slpermanently seals an NFT: the indexer records the sealed location and stops advancing the atomical's UTXO, so it can never move or change again.
10. Realms, containers, dmitems
- Name shapes, each segment 1 to 64 characters with no leading or trailing hyphen: realm
^[a-z][a-z0-9-]{0,63}$; subrealm, container, and dmitem^[a-z0-9][a-z0-9-]{0,63}$. Names are ASCII at the protocol layer; unicode display forms are an application convention. - Top level names (realm, container, ticker) MUST carry commit bitwork with a prefix of at least 4 hex characters. Subrealms and dmitems are exempt.
- Name races resolve by earliest commit transaction number among valid candidates, the same rule as tickers. A claim is
verifiedwhen its commit is at least 3 blocks deep and it is the earliest candidate. - A subrealm mint declares
args.parent_realmand anargs.claim_typeofdirectorrule. Direct claims require the parent realm to be spent in the same transaction. Rule claims are evaluated against the parent's subrealm rule set as of one block before the commit. - A subname rule set is a list of 1 to 100 entries. Each entry carries a regex
pof 1 to 100000 characters which MUST compile and MUST NOT contain parentheses, plus at least one of payment outputso,bitworkc, orbitworkr, where a bitwork value may be the literalany. One invalid entry invalidates the entire rule set. - Payment outputs
omap an output script in hex to{"v": <sats>}or{"v": <amount>, "id": <compact atomical id>}. Withid, payment is measured in colored units of that ARC-20 token at the paying output. The minimum payment value is 0, so free subnames are legal. - Subname payment window: payment may be made from
commit_height + 3and MUST confirm bycommit_height + 15, or the candidate expires. - A container's dmint configuration requires
vequal to the string"1", a 64 hex charactermerkleroot, a non-negativemint_height, an optional booleanimmutable, and a valid rule set per N5. - A dmitem mint proves membership:
args.proofis a non-empty list of{"p": bool, "d": <64 hex>}merkle steps, andargs.mainnames a payload key whose bytes hash into the leaf, accepted under the four combinations of any or specific bitwork against the container's merkle root. Both the commit height and the current height must be at or above the container's mint height. - Subname payments are matched only in transactions carrying the OP_RETURN marker of rule E6, and never in a transaction that also carries a split operation, because a split can reassign ARC-20 value within the paying transaction.
11. Activation heights, Bitcoin mainnet
| Height | Name | Effect |
|---|---|---|
| 808080 | Activation | Atomicals parsing and indexing begins |
| 819181 | Dmint | Container dmitems; FIFO input ordering for transfers |
| 822800 | Commitz | Commit outpoint index must be 0 for name type and dmt mints |
| 828128 | Density | max_mints cap raised to 21000000; perpetual mint mode; bytes permitted in args |
| 828628 | Bitwork rollover | Perpetual mints may satisfy the next higher difficulty prefix |
| 848484 | Custom coloring | Partial coloring of outputs; the z operation |
| 900000 | AVM | def and new handling, Bitcoin Universe fork |
12. Invalidity conditions
A non-exhaustive checklist of conditions that void an operation. The transaction itself remains a valid Bitcoin transaction; only the protocol meaning is lost.
- Envelope structural failures: witness element under 39 bytes, missing 32 byte key push, a marker other than
atomafterOP_IF, an unknown operation code, a payload that is not a CBOR map, or byte values where rule E8 forbids them. - Mint placement failures: envelope not on input 0; commit not indexed or before height 808080; reveal outside the 100 block general window; name reveal outside the 3 block window; a nonzero commit index after height 822800 for name type or
dmtmints; a transaction with no outputs. - Name failures: a malformed ticker, realm, container, or dmitem string; multiple name requests in one mint; missing or short commit bitwork on a top level name; an earlier committed candidate already exists; an immutable flag on a realm, subrealm, container, or fungible token.
- Deploy failures:
mint_amountoutside 546 to 100000000;max_mintsoutside its era cap;mint_heightoutside 0 to 10000000; malformed mint bitwork; perpetual mode before height 828128, without a validbv, with static mint bitwork, or with out of rangebci,bri,bcs,brs, ormaxg. - Claim failures: an unknown or unverified ticker; a claim or its commit before the deploy's mint height; an output 0 value that is not exactly
mint_amount; a mint count at or above the cap; a commit or reveal transaction id failing the required or phase derived bitwork. - Payment failures: a subname payment inside a split transaction; a payment after
commit_height + 15; a payment output below the rule's value or in the wrong token; a missing OP_RETURN marker. - Declared but unmet request bitwork on any operation, and unmet
args.parentsminimums per rule E12. - Coloring never fails a transaction. A spend that violates no Bitcoin rule always confirms, and the coloring rules then decide assignment and burns. Invalid protocol intent shows up as burned or unmoved value, not as a rejected transaction. This is the single most important safety fact for wallet authors.
Attribution and scope. Sections 1 to 11 describe behavior implemented in the atomicals-electrumx lineage and are believed to match upstream Atomicals behavior, except where a rule is marked as a Bitcoin Universe fork addition. Where upstream documentation and this indexer's code disagree, this document follows the code and says so. The full list of Universe-specific indexing decisions is on the reference page.