Conformance

Test vectors

Cases with expected outcomes, for checking an implementation. Vectors marked from suite are transcriptions of cases in the indexer's own test file tests/lib/test_atomicals_blueprint_builder.py, with the same inputs and the same asserted results. The remainder are derived directly from the rules in the specification and are reproducible in the simulator.

Notation: inputs are listed as index: sats / units, outputs as index: sats. Unless stated otherwise, one fungible token is involved, FIFO ordering is active (height 819181 or later), and no envelope operation is present (a plain spend).

1. Valid coloring

V1. Exact single output from suite

InputOutputsEraExpected
0: 11000 / 11000 0: 11000 exact cover Output 0 carries 11000 units. Burned: none. Cleanly assigned: yes.

The token exactly fills the only output. Corresponds to test_spends_ft_valid.

V2. Ordinary two-way split

InputOutputsEraExpected
0: 10000 / 10000 0: 3000
1: 7000
either Output 0 carries 3000, output 1 carries 7000. Burned: none.

The everyday transfer: outputs are filled left to right until the pool is exhausted. Both outputs are covered exactly, so this behaves identically in both eras.

V3. Uncolored change beyond the pool

InputsOutputsEraExpected
0: 10000 / 10000
1: 50000 / uncolored
0: 3000
1: 7000
2: 49800
either Outputs 0 and 1 carry 3000 and 7000. Output 2 carries no color. Burned: none. Fee 200 sats paid from plain bitcoin.

V4. Two tokens, FIFO order, clean boundaries

InputsOutputsEraExpected
0: 8000 / 8000 of A
1: 2000 / 2000 of B
0: 6000
1: 2000
2: 2000
either A colors output 0 with 6000 and output 1 with 2000, exhausting it exactly. B then begins at output 2 and colors it with 2000. Burned: none. Cleanly assigned: yes.

A is processed first because it appears at the lower input index, and B starts at the output after the last one A colored. This is the case drawn in figure 3 of the specification. Choosing output values so each token's balance ends on an output boundary is what keeps a multi-token transfer predictable.

V4b. The non-clean fallback (rule C7)

InputsOutputsEraExpected
0: 8000 / 8000 of A
1: 2000 / 2000 of B
0: 6000
1: 4000
current A's assignment reaches the last output without a clean stop, and B then finds no output at or after its start index. The whole map is discarded and both tokens restart from output 0: output 0 carries 6000 of A and 2000 of B, output 1 carries 2000 of A. Burned: none. Cleanly assigned: no.

The same two tokens as V4 with only the output values changed, and the outcome is qualitatively different: both tokens end up stacked on output 0. This is the behavior that catches implementers who assume each token simply continues where the previous one stopped. Run both shapes in the simulator to compare the step traces.

V5. Partially colored input carried forward from suite

InputOutputsEraExpected
0: 11001 sats / 500 units 0: 11000 current Output 0 carries 500 units against 11000 satoshis. Burned: none. Cleanly assigned reported as false (the output is only partially colored).

Corresponds to test_spends_single_ft_partially_colored_transfer. Note that a partially colored UTXO is normal since height 848484: the satoshi value and the token value are tracked separately.

2. Burn cases

B1. Remainder with nowhere to go from suite

InputsOutputsEraExpected
0 through 10: 1000 / 1000 each (11000 total, one token) 0: 1000 exact cover Output 0 carries 1000 units. Burned: 10000. Burn flag: true.

Corresponds to test_spends_ft_burned. Eleven colored inputs consolidated into a single small output: everything beyond the first 1000 units is destroyed. This is the classic consolidation accident.

B2. Output larger than the remaining balance from suite

InputOutputsEraExpected
0: 10999 / 10999 0: 11000 exact cover No output is colored at all. Burned: 10999. Burn flag: true.

Corresponds to test_spends_ft_single_burned_under. Under exact-cover rules an output must be fully covered; one satoshi short destroyed the entire balance. Under current rules the same shape would color output 0 partially with 10999 units and burn nothing, which is exactly why the rule changed.

B3. One unit over from suite

InputOutputsEraExpected
0: 11001 / 11001 0: 11000 exact cover Output 0 carries 11000 units. Burned: 1. Burn flag: true.

Corresponds to test_spends_ft_single_burned_over. The single leftover unit is the miner fee, and it is burned token supply.

B4. Fee taken from colored value

InputOutputsEraExpected
0: 10000 / 10000 0: 3000
1: 6800
current Outputs carry 3000 and 6800. Burned: 200 (the fee).

B5. No outputs able to take color

InputOutputsEraExpected
0: 5000 / 5000 0: OP_RETURN (0 sats) either Burned: 5000. Unspendable outputs are skipped by regular assignment, leaving nowhere for the balance to land.

3. Split and custom color

S1. Merged inputs, single output from suite

InputsOutputsEraExpected
0: 900 / 900
1: 555 / 555 (same token)
0: 1454 exact cover Output 0 carries 1454 units. Burned: 1.

From test_spends_ft_split_one_token. The token pool is 1455; the output takes 1454 and the single remaining unit is the fee, burned.

S2. Split skip

InputsPayload (y)OutputsExpected
0: 3000 / 3000 of A {A: 1000} 0: 1000
1: 2000
Output 0 is skipped (its 1000 satoshis satisfy the skip amount). A colors output 1 with 2000. Burned: 1000, since A's remaining value exceeds the remaining outputs.

The split payload maps each token to a satoshi amount to pass over before its coloring begins. It is the tool for separating two tokens that share one input.

S3. Custom color with over-request clamping from suite

InputsPayload (z)OutputsExpected
input 1 carries
1000 of A and
1000 of B
A: {0: 1200}
B: {1: 600, 2: 600}
0: 1200
1: 600
2: 600
Output 0 carries 1000 of A (clamped from the requested 1200 to A's actual balance). Output 1 carries 600 of B, output 2 carries 400 of B (clamped by B's remaining balance). Burned: none. Cleanly assigned: false.

From test_custom_colored_ft_overflow. The payload requests more value than exists for both tokens; the engine clamps to the output's satoshi value and to the remaining token value, so no inflation is possible.

S4. Custom color leaving value unassigned

InputsPayload (z)OutputsExpected
0: 1000 / 1000 of A A: {0: 400} 0: 1000
1: 500
Output 0 carries 400 units of A. Burned: 600, because the payload directed nothing at any other output.

S5. Transaction with no atomicals at all from suite

A transaction spending no token-bearing inputs produces an empty blueprint: no NFT outputs, no FT outputs, no burns. Corresponds to test_empty_spends. Implementations must not fabricate assignments for uncolored transactions.

4. Envelope parsing

CaseInputExpected
E-V1 valid markerWitness element beginning 20<32 bytes>ac0063 0461746f6d 03646d74 …Valid operation dmt, payload parsed from the pushes that follow.
E-X1 short elementWitness element of 38 bytesSkipped minimum length is 39 bytes.
E-X2 wrong first byteElement beginning 21 instead of 20Skipped a 32-byte push is required.
E-X3 wrong markerOP_IF followed by 046f7264 ("ord")Skipped not an Atomicals envelope.
E-X4 unknown operationMarker followed by 0378797a ("xyz")Skipped unrecognized operation code.
E-X5 payload not a mapCBOR encoding of the array [1,2,3]Rejected the payload must decode to a CBOR map.
E-X6 undecodable payloadRandom bytes after the operation pushRejected CBOR decode failure.
E-X7 bytes in meta{"meta": {"x": h'0102'}}Rejected byte strings are not permitted inside meta or ctx.
E-X8 empty tapscriptWitness with no leaf scriptsNo operation returns an empty result rather than an error. Corresponds to test_parse_operations_from_empty_tap_leafs.

5. Mint validity

CaseSetupExpected
M-V1 direct mintft with request_ticker: "quark", reveal output 0 of 100000 satsValid supply 100000 units, all at reveal output 0.
M-V2 dmt claimDeploy with mint_amount: 1000; claim pays exactly 1000 sats to output 0, at or after mint_height, below the capValid 1000 units minted.
M-X1 wrong claim amountSame deploy; claim pays 999 or 1001 sats to output 0Invalid output 0 must equal mint_amount exactly.
M-X2 premature claimClaim confirmed one block before mint_heightInvalid both the claim and its commit must be at or above mint_height.
M-X3 cap exceededClaim number max_mints + 1Invalid the mint is closed; counts are re-derived from durable state each block.
M-X4 envelope on input 1Valid dmt payload placed on the second inputInvalid mint operations require input index 0.
M-X5 late revealReveal 101 blocks after commitInvalid general commit window is 100 blocks.
M-X6 late named revealTicker mint revealed 4 blocks after commitInvalid named mints must reveal within 3 blocks (expired_revealed_late).
M-X7 nonzero commit indexNamed mint whose commit outpoint index is 1, after height 822800Invalid commit index must be 0.
M-X8 out-of-range deploymint_amount: 545, or mint_amount: 100000001Invalid bounds are 546 to 100000000.
M-X9 immutable fungibleft or dft with args.i: trueInvalid fungible tokens can never be immutable.
M-X10 two name requestsargs containing both request_ticker and request_realmInvalid at most one name request per mint.

6. Bitwork

CaseBitworkTxid beginsExpected
BW-V11a2b1a2b7f…Satisfied prefix matches.
BW-X11a2b1a2c00…Not satisfied prefix mismatch.
BW-V21a2b.81a2b9…Satisfied the next digit (9) is at least 8.
BW-X21a2b.81a2b7…Not satisfied the next digit (7) is below 8.
BW-V31a2b.0any 1a2b…Satisfied an extension of 0 imposes no extra requirement.
BW-X31a2ganyMalformed g is not a hex character.
BW-X41a2b.16anyMalformed the extension must be 0 to 15.
BW-T1 target1a2b.8n/aDifficulty target = 4 × 16 + 8 = 72.
BW-T2 perpetual stepbv: "abcd", start 64, increment 16, max_mints 100, after 250 claimsn/aSteps = floor(250/100) = 2, target = 64 + 32 = 96, required prefix = the first 6 characters of abcd padded with zeros: abcd00.

7. Names and tickers

CaseValueExpected
N-V1ticker quarkValid lowercase alphanumeric, within 21 characters.
N-V2ticker aValid one character is the minimum.
N-X1ticker QUARKInvalid uppercase is not permitted.
N-X2ticker my-tokenInvalid hyphens are not permitted in tickers.
N-X3ticker of 22 charactersInvalid the maximum is 21.
N-V3realm alpha-oneValid realms may contain hyphens after the first character.
N-X4realm 1alphaInvalid a realm must begin with a letter.
N-X5realm alpha-Invalid no trailing hyphen.
N-X6realm claim with bitworkc: "1a2"Invalid top-level names require a commit bitwork prefix of at least 4 characters.
N-T1 raceTwo valid claims for quark: X commits at height 900000 and reveals at 900002; Y commits at 900001 and reveals at 900001X wins. Resolution is by earliest commit, not earliest reveal. Both are pending until X's commit is 3 blocks deep.
N-X7 subname paymentPayment confirmed 16 blocks after commitInvalid the payment window closes at commit + 15 (expired_payment_not_received).
N-X8 payment in a splitCorrect payment output in a transaction that also carries a y operationRefused payments are never satisfied in a split transaction.
N-X9 dmitem proofMerkle proof item with a data hash of 63 hex charactersInvalid each proof step's d must be exactly 64 hex characters.

On reproducing suite vectors exactly. The indexer's tests supply full raw transactions plus a synthetic map of spent atomicals; the tables above transcribe the satoshi and unit quantities and the asserted outcomes, which is what an independent implementation needs to check. To run the original cases byte for byte, use the test file named at the top of this page in an Atomicals indexer checkout.