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.

The three outcomes
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.

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.

Framing, computed from the payloads in the guide
PayloadJSON bytesLength prefixFramed totalOLGA outputsMultisig outputs
DEPLOY PLATE830059 (89)9132
MINT PLATE54003c (60)6221
TRANSFER PLATE590041 (65)6732

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.

Carrier acceptance
VectorConditionOutcomeRule
F-21-of-3 multisig, third key is a recognised burn keyDecodesE-1, E-2
F-31-of-3 multisig, third key is an ordinary public keyExcluded, keyburn not setE-2, X-1
F-42-of-3 multisig carrying otherwise valid dataExcluded, wrong script shapeE-1
F-5Declared length 200, only 91 bytes presentExcluded, invalid data lengthE-6, X-2
F-6Decrypted bytes 2 to 7 are not stamp:Excluded, not a Stamps transactionE-5
F-7P2WSH data output at index 0Excluded as data; output 0 is always the recipientE-8
F-8P2WSH data present but its prefix check fails, valid multisig data also presentExcluded. The multisig branch is not tried.E-12, X-3
F-9OLGA P2WSH transaction below block 865000Excluded, carrier not yet active for SRC-20E-8
F-10Counterparty-encoded SRC-20 at block 800000Excluded, at or above 796000CP-3, X-4
F-8 is the one that forks ledgers

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 acceptance
TickerCode pointsOutcomeWhy
A1AcceptedMinimum length is 1.
PLATE5AcceptedExactly at the limit.
plate5AcceptedSame token as PLATE. Tickers are case-insensitive.
K3V!N5AcceptedDigits and ! are in the ASCII set.
~#$%^5AcceptedAll five are in the ASCII set.
🔥1AcceptedU+1F525 is in the emoji allowlist.
🔥🔥🔥🔥🔥5Accepted5 code points, 20 bytes. The limit counts code points.
STAMPS6ExcludedOver the 5 code point limit.
A B3ExcludedSpace is not in the set.
A-B3ExcludedHyphen is not in the set.
A+B3ExcludedPlus is not in the set.
A,B3ExcludedComma is not in the set.
(empty)0ExcludedA ticker must be non-empty.
Flag or family emoji2 or moreExcludedComposed 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.

Applied in order. "Minted" is the running total for the token.
#OperationOutput 0OutcomeCreditedMinted afterBalances after
1 DEPLOY max=1000 lim=400 dec=0A Valid0all zero
2 MINT amt=400A Valid400400A 400
3 MINT amt=500B Valid, status ODL400, clamped to lim800A 400, B 400
4 MINT amt=400C Valid, status OMA200, clamped to remaining supply1000A 400, B 400, C 200
5 MINT amt=1D Invalid, status OMnothing1000unchanged
6 TRANSFER amt=100 from AB Valid100 moved1000A 300, B 500, C 200
7 TRANSFER amt=1000 from AC Invalid, status BBnothing, no partial fill1000unchanged
8 TRANSFER amt=300 from AA Validself-transfer, no net change1000unchanged
9 DEPLOY plate againD Invalid, status DEnothing1000unchanged
Rows 3, 4 and 5 are the whole test

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.

Height-dependent outcomes
VectorBelow 833000At 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
Carrier availability by height
VectorHeightOutcome
Counterparty-encoded SRC-20, numeric asset, supply 0790000Recognised
Counterparty-encoded SRC-20, same shape796000Excluded, at the cutoff
Counterparty-encoded SRC-20, named asset not starting with A790000Excluded
Direct-to-Bitcoin multisig SRC-20793068 onwardRecognised
OLGA P2WSH SRC-20864999Excluded, carrier not yet active
OLGA P2WSH SRC-20865000 onwardRecognised

Try any payload vector from this page in the validator. It applies the strict rules, that is, height 833000 and above.