Test vectors · Strict numeric rules, height 833000 and above
SRC-20 test vectors
Every vector below states the outcome the reference indexer produces and the rule that decides it. Outcomes are one of three: valid, invalid with a status code, or excluded with no record at all.
- Valid
- Recorded and state changes. Note that a clamped mint is in this category.
- Invalid
- Recorded with a status code, appears in activity feeds, changes no balance.
- Excluded
- Never becomes an SRC-20 record. No status code, appears nowhere.
Contents
1. Carrier framing vectors
Given a JSON payload, the framed buffer is a two-byte big-endian length, then stamp:, then the JSON. The length covers the prefix and the JSON, not itself.
| Payload | JSON bytes | Length prefix | Framed total | OLGA outputs | Multisig outputs |
|---|---|---|---|---|---|
DEPLOY PLATE | 83 | 0059 (89) | 91 | 3 | 2 |
MINT PLATE | 54 | 003c (60) | 62 | 2 | 1 |
TRANSFER PLATE | 59 | 0041 (65) | 67 | 3 | 2 |
Vector F-1: the first 32 bytes of a framed DEPLOY
Payload: {"p":"src-20","op":"DEPLOY","tick":"PLATE","max":"21000000","lim":"1000","dec":"8"}
00 59 73 74 61 6d 70 3a 7b 22 70 22 3a 22 73 72
63 2d 32 30 22 2c 22 6f 70 22 3a 22 44 45 50 4c
Bytes 0 to 1 are the length, 89. Bytes 2 to 7 are stamp:. Byte 8 onward is the JSON. Under OLGA this becomes 3 P2WSH witness programs with 5 zero bytes of padding on the last; under bare multisig it is ARC4-encrypted first, then split into 3 data public keys of 31 bytes.
| Vector | Condition | Outcome | Rule |
|---|---|---|---|
| F-2 | 1-of-3 multisig, third key is a recognised burn key | Decodes | E-1, E-2 |
| F-3 | 1-of-3 multisig, third key is an ordinary public key | Excluded, keyburn not set | E-2, X-1 |
| F-4 | 2-of-3 multisig carrying otherwise valid data | Excluded, wrong script shape | E-1 |
| F-5 | Declared length 200, only 91 bytes present | Excluded, invalid data length | E-6, X-2 |
| F-6 | Decrypted bytes 2 to 7 are not stamp: | Excluded, not a Stamps transaction | E-5 |
| F-7 | P2WSH data output at index 0 | Excluded as data; output 0 is always the recipient | E-8 |
| F-8 | P2WSH data present but its prefix check fails, valid multisig data also present | Excluded. The multisig branch is not tried. | E-12, X-3 |
| F-9 | OLGA P2WSH transaction below block 865000 | Excluded, carrier not yet active for SRC-20 | E-8 |
| F-10 | Counterparty-encoded SRC-20 at block 800000 | Excluded, at or above 796000 | CP-3, X-4 |
It is tempting to fall back to the multisig branch when the P2WSH data fails. The reference implementation deliberately does not, and the exclusion is load-bearing: it matches the other reference implementation in the ecosystem. Adding the fallback produces a different set of valid transactions and a different ledger.
2. Valid payloads
Each of these passes every field-level rule. Whether it changes state also depends on chain state, noted where relevant.
V-1 Valid Standard DEPLOY
{"p":"src-20","op":"DEPLOY","tick":"PLATE","max":"21000000","lim":"1000","dec":"8"}
Creates plate with 8 decimals, if no earlier deploy exists. Ticker is 5 code points, the maximum.
V-2 Valid DEPLOY without dec
{"p":"src-20","op":"DEPLOY","tick":"NODEC","max":"1000","lim":"10"}
Deploys with 18 decimals, not 0. The default is 18. This is a common and expensive misreading. Rule N-5.
V-3 Valid Mixed case in p and op
{"p":"SRC-20","op":"deploy","tick":"CaSe","max":5,"lim":1}
p is compared case-insensitively, op is uppercased, and the ticker is lowercased to case. Numeric values as JSON numbers rather than strings are accepted. Rules P-3, P-4, T-4, P-7.
V-4 Valid Fractional max and lim, truncated
{"p":"src-20","op":"DEPLOY","tick":"TRUNC","max":"1000.9","lim":"10.75","dec":"0"}
Deploys with max 1000 and lim 10. Both are truncated downward. Rule N-3.
V-5 Valid Amount at the numeric ceiling
{"p":"src-20","op":"MINT","tick":"MAXED","amt":"18446744073709551615"}
Exactly 264 minus 1, which is inside the range. One more would be excluded. Rule N-1.
V-6 Valid Trailing zeros in amt
{"p":"src-20","op":"MINT","tick":"DEC1","amt":"1.500"}
Against a token with dec 1 this is valid: the amount normalises to 1.5, which is one decimal place. Trailing zeros do not count. Rule N-7.
V-7 Valid Unknown extra fields
{"p":"src-20","op":"DEPLOY","tick":"META","max":"1","lim":"1",
"web":"https://example.invalid","desc":"an example","nonsense":42}
Field presence is a superset test. web and desc are stored as metadata with no protocol effect; nonsense is carried and ignored. Rule P-5.
V-8 Valid Emoji ticker
{"p":"src-20","op":"MINT","tick":"🔥","amt":"1"}
U+1F525 is in the allowlist. One code point of a permitted five, occupying 4 bytes. Rule T-3.
3. Excluded payloads
These never become SRC-20 records. There is no status code to look up, and nothing appears in any feed.
E-1 Excluded Unquoted scientific notation
{"p":"src-20","op":"DEPLOY","tick":"SCI","max":1e6,"lim":1000}
The JSON parser raises on the exponent literal before any SRC-20 logic runs. Rule P-2.
E-2 Excluded Thousands separators
{"p":"src-20","op":"DEPLOY","tick":"COMMA","max":"21,000,000","lim":"1,000"}
At or above height 833000 the string is not readable as a decimal. Below 833000 this same payload deployed a supply of 21000000, because every character that was not a digit or a dot was stripped first. Rules N-2, N-6.
E-3 Excluded Ticker too long
{"p":"src-20","op":"MINT","tick":"TOOLONG","amt":"1"}
7 code points against a limit of 5. Rule T-1.
E-4 Excluded Ticker with a disallowed character
{"p":"src-20","op":"MINT","tick":"A-B","amt":"1"}
The hyphen is not in the allowed set, and neither is a space, comma, plus, slash, colon, quote or bracket. Rule T-2.
E-5 Excluded Wrong protocol string
{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"}
A BRC-20 payload placed in a Stamps carrier. Also excluded: "p":"src20" without the hyphen. Rule P-3.
E-6 Excluded Amount above the ceiling
{"p":"src-20","op":"MINT","tick":"BIG","amt":"18446744073709551616"}
One above 264 minus 1. Rule N-1.
E-7 Excluded Negative amount
{"p":"src-20","op":"TRANSFER","tick":"NEG","amt":"-5"}
Outside the permitted range of 0 to the uint64 maximum. Rule N-1.
E-8 Excluded Malformed JSON
{"p":"src-20","op":"MINT","tick":"BAD","amt":}
A parse failure excludes the transaction silently. Rule P-1.
4. Invalid payloads
These are recognised, recorded, and given a status code. They appear in activity feeds and change no balance.
I-1 Invalid UO Unsupported operation
{"p":"src-20","op":"BULK_XFER","tick":"PLATE","amt":"1","destinations":[]}
The reference indexer contains a handle_bulk_transfer implementation, but dispatch matches only DEPLOY, MINT and TRANSFER, so the code is unreachable and this lands on the unsupported-operation branch. Rule P-4.
I-2 Invalid NN Quoted exponent
{"p":"src-20","op":"DEPLOY","tick":"QEXP","max":"1e6","lim":"1000"}
Worth contrasting with E-1. Quoted, a decimal parser reads it, so the carrier check passes and the payload becomes a record; it then fails the stricter numeric regex and is marked NN. Unquoted, it is excluded before any of that. Rule N-2.
I-3 Invalid NN Decimals out of range
{"p":"src-20","op":"DEPLOY","tick":"DEC25","max":"1000","lim":"10","dec":"25"}
Above the maximum of 18. Rule N-5.
I-4 Invalid DE DEPLOY missing lim
{"p":"src-20","op":"DEPLOY","tick":"NOLIM","max":"1000"}
The status message reads "DEPLOY EXISTS" even though no earlier deploy exists. The condition that produces DE covers both a taken ticker and a missing or zero max or lim. Do not read the message literally. Rule V-1.
I-5 Invalid DE Zero supply
{"p":"src-20","op":"DEPLOY","tick":"ZERO","max":"0","lim":"1"}
Both max and lim must be non-zero after truncation. Rule V-1.
I-6 Invalid NA Missing amount
{"p":"src-20","op":"MINT","tick":"PLATE"}
Also produced by "amt":"", since an empty string normalises to absent. Rules P-6, and the NA row of the status table.
I-7 Invalid ID Too many decimal places
{"p":"src-20","op":"TRANSFER","tick":"WHOLE","amt":"1.5"}
Against a token deployed with dec 0. Against a token with dec 8 the same payload is valid. Rule N-7.
I-8 Invalid ND No deploy
{"p":"src-20","op":"MINT","tick":"GHOST","amt":"1"}
Where ghost has never been deployed. Rule V-4.
5. Ticker vectors
| Ticker | Code points | Outcome | Why |
|---|---|---|---|
A | 1 | Accepted | Minimum length is 1. |
PLATE | 5 | Accepted | Exactly at the limit. |
plate | 5 | Accepted | Same token as PLATE. Tickers are case-insensitive. |
K3V!N | 5 | Accepted | Digits and ! are in the ASCII set. |
~#$%^ | 5 | Accepted | All five are in the ASCII set. |
🔥 | 1 | Accepted | U+1F525 is in the emoji allowlist. |
🔥🔥🔥🔥🔥 | 5 | Accepted | 5 code points, 20 bytes. The limit counts code points. |
STAMPS | 6 | Excluded | Over the 5 code point limit. |
A B | 3 | Excluded | Space is not in the set. |
A-B | 3 | Excluded | Hyphen is not in the set. |
A+B | 3 | Excluded | Plus is not in the set. |
A,B | 3 | Excluded | Comma is not in the set. |
| (empty) | 0 | Excluded | A ticker must be non-empty. |
| Flag or family emoji | 2 or more | Excluded | Composed sequences use joiners and modifiers that are not in the allowlist, which contains single code points only. |
The allowed ASCII set is exactly .!#$%&()*0123456789<=>?@A-Z^_a-z~, and the emoji allowlist holds 1154 single code points between U+1F004 and U+1FAD6.
6. Stateful lifecycle
The vectors above test a payload in isolation. This one tests the state machine. Every row is applied in order to a fresh index.
Token: plate, max 1000, lim 400, dec 0. Addresses are labelled A, B, C and D.
| # | Operation | Output 0 | Outcome | Credited | Minted after | Balances after |
|---|---|---|---|---|---|---|
| 1 | DEPLOY max=1000 lim=400 dec=0 | A | Valid | – | 0 | all zero |
| 2 | MINT amt=400 | A | Valid | 400 | 400 | A 400 |
| 3 | MINT amt=500 | B | Valid, status ODL | 400, clamped to lim | 800 | A 400, B 400 |
| 4 | MINT amt=400 | C | Valid, status OMA | 200, clamped to remaining supply | 1000 | A 400, B 400, C 200 |
| 5 | MINT amt=1 | D | Invalid, status OM | nothing | 1000 | unchanged |
| 6 | TRANSFER amt=100 from A | B | Valid | 100 moved | 1000 | A 300, B 500, C 200 |
| 7 | TRANSFER amt=1000 from A | C | Invalid, status BB | nothing, no partial fill | 1000 | unchanged |
| 8 | TRANSFER amt=300 from A | A | Valid | self-transfer, no net change | 1000 | unchanged |
| 9 | DEPLOY plate again | D | Invalid, status DE | nothing | 1000 | unchanged |
An implementation that gets these three right almost certainly agrees with the reference on everything else. Rows 3 and 4 are valid mints that credited less than the payload asked for. Row 5 is the only genuinely failed mint, and it fails because the supply was already exhausted, not because the amount was wrong.
If your indexer marks rows 3 and 4 invalid, your circulating supply for this token will read 400 instead of 1000.
Ordering within a block
If rows 6 and 7 were both in the same block, the outcome is unchanged: the running balance already reflects row 6 when row 7 is evaluated. If their order were reversed, row 7 would still fail, because A held 400 and the transfer was for 1000. But had row 7 been for 400, order would decide: first in block order succeeds, the other fails with BB.
7. Historical behaviour
These vectors have a different outcome depending on block height. An indexer replaying history must reproduce both.
| Vector | Below 833000 | At or above 833000 |
|---|---|---|
"max":"21,000,000" |
Valid, read as 21000000 after stripping | Excluded |
"amt":"1 000" |
Valid, read as 1000 | Excluded |
"lim":"abc500" |
Valid, read as 500 | Excluded |
| Vector | Height | Outcome |
|---|---|---|
| Counterparty-encoded SRC-20, numeric asset, supply 0 | 790000 | Recognised |
| Counterparty-encoded SRC-20, same shape | 796000 | Excluded, at the cutoff |
Counterparty-encoded SRC-20, named asset not starting with A | 790000 | Excluded |
| Direct-to-Bitcoin multisig SRC-20 | 793068 onward | Recognised |
| OLGA P2WSH SRC-20 | 864999 | Excluded, carrier not yet active |
| OLGA P2WSH SRC-20 | 865000 onward | Recognised |
Try any payload vector from this page in the validator. It applies the strict rules, that is, height 833000 and above.