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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Inputs | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Inputs | Outputs | Era | Expected |
|---|---|---|---|
| 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)
| Inputs | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Inputs | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 0: 10000 / 10000 | 0: 3000 1: 6800 |
current | Outputs carry 3000 and 6800. Burned: 200 (the fee). |
B5. No outputs able to take color
| Input | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Inputs | Outputs | Era | Expected |
|---|---|---|---|
| 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
| Inputs | Payload (y) | Outputs | Expected |
|---|---|---|---|
| 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
| Inputs | Payload (z) | Outputs | Expected |
|---|---|---|---|
| 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
| Inputs | Payload (z) | Outputs | Expected |
|---|---|---|---|
| 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
| Case | Input | Expected |
|---|---|---|
| E-V1 valid marker | Witness element beginning 20<32 bytes>ac0063 0461746f6d 03646d74 … | Valid operation dmt, payload parsed from the pushes that follow. |
| E-X1 short element | Witness element of 38 bytes | Skipped minimum length is 39 bytes. |
| E-X2 wrong first byte | Element beginning 21 instead of 20 | Skipped a 32-byte push is required. |
| E-X3 wrong marker | OP_IF followed by 046f7264 ("ord") | Skipped not an Atomicals envelope. |
| E-X4 unknown operation | Marker followed by 0378797a ("xyz") | Skipped unrecognized operation code. |
| E-X5 payload not a map | CBOR encoding of the array [1,2,3] | Rejected the payload must decode to a CBOR map. |
| E-X6 undecodable payload | Random bytes after the operation push | Rejected 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 tapscript | Witness with no leaf scripts | No operation returns an empty result rather than an error. Corresponds to test_parse_operations_from_empty_tap_leafs. |
5. Mint validity
| Case | Setup | Expected |
|---|---|---|
| M-V1 direct mint | ft with request_ticker: "quark", reveal output 0 of 100000 sats | Valid supply 100000 units, all at reveal output 0. |
| M-V2 dmt claim | Deploy with mint_amount: 1000; claim pays exactly 1000 sats to output 0, at or after mint_height, below the cap | Valid 1000 units minted. |
| M-X1 wrong claim amount | Same deploy; claim pays 999 or 1001 sats to output 0 | Invalid output 0 must equal mint_amount exactly. |
| M-X2 premature claim | Claim confirmed one block before mint_height | Invalid both the claim and its commit must be at or above mint_height. |
| M-X3 cap exceeded | Claim number max_mints + 1 | Invalid the mint is closed; counts are re-derived from durable state each block. |
| M-X4 envelope on input 1 | Valid dmt payload placed on the second input | Invalid mint operations require input index 0. |
| M-X5 late reveal | Reveal 101 blocks after commit | Invalid general commit window is 100 blocks. |
| M-X6 late named reveal | Ticker mint revealed 4 blocks after commit | Invalid named mints must reveal within 3 blocks (expired_revealed_late). |
| M-X7 nonzero commit index | Named mint whose commit outpoint index is 1, after height 822800 | Invalid commit index must be 0. |
| M-X8 out-of-range deploy | mint_amount: 545, or mint_amount: 100000001 | Invalid bounds are 546 to 100000000. |
| M-X9 immutable fungible | ft or dft with args.i: true | Invalid fungible tokens can never be immutable. |
| M-X10 two name requests | args containing both request_ticker and request_realm | Invalid at most one name request per mint. |
6. Bitwork
| Case | Bitwork | Txid begins | Expected |
|---|---|---|---|
| BW-V1 | 1a2b | 1a2b7f… | Satisfied prefix matches. |
| BW-X1 | 1a2b | 1a2c00… | Not satisfied prefix mismatch. |
| BW-V2 | 1a2b.8 | 1a2b9… | Satisfied the next digit (9) is at least 8. |
| BW-X2 | 1a2b.8 | 1a2b7… | Not satisfied the next digit (7) is below 8. |
| BW-V3 | 1a2b.0 | any 1a2b… | Satisfied an extension of 0 imposes no extra requirement. |
| BW-X3 | 1a2g | any | Malformed g is not a hex character. |
| BW-X4 | 1a2b.16 | any | Malformed the extension must be 0 to 15. |
| BW-T1 target | 1a2b.8 | n/a | Difficulty target = 4 × 16 + 8 = 72. |
| BW-T2 perpetual step | bv: "abcd", start 64, increment 16, max_mints 100, after 250 claims | n/a | Steps = floor(250/100) = 2, target = 64 + 32 = 96, required prefix = the first 6 characters of abcd padded with zeros: abcd00. |
7. Names and tickers
| Case | Value | Expected |
|---|---|---|
| N-V1 | ticker quark | Valid lowercase alphanumeric, within 21 characters. |
| N-V2 | ticker a | Valid one character is the minimum. |
| N-X1 | ticker QUARK | Invalid uppercase is not permitted. |
| N-X2 | ticker my-token | Invalid hyphens are not permitted in tickers. |
| N-X3 | ticker of 22 characters | Invalid the maximum is 21. |
| N-V3 | realm alpha-one | Valid realms may contain hyphens after the first character. |
| N-X4 | realm 1alpha | Invalid a realm must begin with a letter. |
| N-X5 | realm alpha- | Invalid no trailing hyphen. |
| N-X6 | realm claim with bitworkc: "1a2" | Invalid top-level names require a commit bitwork prefix of at least 4 characters. |
| N-T1 race | Two valid claims for quark: X commits at height 900000 and reveals at 900002; Y commits at 900001 and reveals at 900001 | X wins. Resolution is by earliest commit, not earliest reveal. Both are pending until X's commit is 3 blocks deep. |
| N-X7 subname payment | Payment confirmed 16 blocks after commit | Invalid the payment window closes at commit + 15 (expired_payment_not_received). |
| N-X8 payment in a split | Correct payment output in a transaction that also carries a y operation | Refused payments are never satisfied in a split transaction. |
| N-X9 dmitem proof | Merkle proof item with a data hash of 63 hex characters | Invalid 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.