Practical guide · Bitcoin mainnet

SRC-20, worked through

Three operations, followed from the JSON you write to the bytes that land in a block and the balance that comes out the other side. Every example below is computed, not sketched.


1. The mental model

Hold two ideas apart and everything else follows.

Bitcoin's job

Order the transactions and confirm them. Bitcoin never reads your JSON, never checks your ticker, and cannot fail your mint. If you paid the fee, it confirms.

The indexer's job

Read every confirmed transaction in order, apply the SRC-20 rules, and keep the balances. This is where an operation succeeds or fails.

The second idea: balances belong to addresses, not to outputs. SRC-20 is an account ledger. There is no such thing as "the UTXO holding my PLATE". You can spend every output your wallet owns and your token balance is untouched, because the balance lives in the indexers' books, keyed by your address.

Two roles, two positions

In every SRC-20 transaction, input 0 decides who is acting, and output 0 decides who receives. That is the whole addressing scheme. A mint credits output 0, which is why you can mint straight to a cold address by paying output 0 to it.


2. Worked example: DEPLOY

Creating a token called PLATE with a supply of 21,000,000, a per-mint limit of 1000, and 8 decimal places.

Step 1. Write the payload

{"p":"src-20","op":"DEPLOY","tick":"PLATE","max":"21000000","lim":"1000","dec":"8"}

83 bytes of JSON. PLATE is 5 code points, which is exactly the maximum. dec is supplied here; had it been omitted the token would have deployed with 18 decimals, not 0.

Step 2. Frame it

Prepend the six ASCII bytes stamp:, then a two-byte big-endian length covering the prefix and the JSON together.

Framing the DEPLOY payload
PartBytesValue
Length prefix20059, that is, 89
Stamps prefix6stamp:
JSON payload83the object above
Total91 

The framed buffer begins:

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
 ^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
 len   "stamp:"         {"p":"src-20","op":"DEPL...

Step 3. Choose a carrier

OLGA P2WSH, height 865000 and above

Split the 91 bytes into 32-byte chunks: 3 chunks, with the last padded with 5 zero bytes. Each chunk becomes the witness program of one P2WSH output. Add the recipient at output 0, giving 4 outputs. Nothing is encrypted.

Bare multisig, the older carrier

ARC4-encrypt the 91 bytes with the txid of the output spent by input 0, reversed. Split into 31-byte chunks: 3 data public keys, two per output, so 2 multisig outputs, each 1-of-3 with a burn key third. Add the recipient, giving 3 outputs.

Step 4. What the indexer records

If no earlier DEPLOY of plate exists, the token is created with max 21000000, lim 1000 and dec 8, deployed by the address that funded input 0. The ticker is stored lowercased, alongside its SHA3-256 tick hash. Any later DEPLOY of the same ticker is invalid with status DE.

You get one shot

There is no re-deploy and no way to amend max, lim or dec afterwards. Check the payload in the validator before you broadcast. In particular, a DEPLOY that is missing lim or max is recorded as invalid with status DE, whose message reads "DEPLOY EXISTS" even when nothing of the sort happened. The status name is misleading; the cause is the missing field.


3. Worked example: MINT

Claiming 1000 PLATE.

{"p":"src-20","op":"MINT","tick":"PLATE","amt":"1000"}
Framing the MINT payload
PartBytesValue
Length prefix2003c, that is, 60
Stamps prefix6stamp:
JSON payload54the object above
Total622 P2WSH chunks, or 2 data public keys in 1 multisig output

The 1000 PLATE is credited to output 0's address. That is usually your own change or receive address, but it does not have to be. Paying output 0 to a friend mints to your friend.

  • Credited tooutput 0not necessarily the sender
  • Paid byinput 0the address funding the fee
  • Data outputs2OLGA, for this payload

4. Worked example: TRANSFER

Sending 250.5 PLATE.

{"p":"src-20","op":"TRANSFER","tick":"PLATE","amt":"250.5"}
Framing the TRANSFER payload
PartBytesValue
Length prefix20041, that is, 65
Stamps prefix6stamp:
JSON payload59the object above
Total673 P2WSH chunks, 29 bytes of zero padding on the last

The amount 250.5 has one decimal place, which is fine because PLATE deployed with dec 8. Against a token deployed with dec 0, the same payload would be invalid with status ID.

A transfer is all or nothing

If the sending address holds 200 PLATE and you transfer 250.5, the operation is invalid with status BB and nothing moves. You do not send 200 and fail on the rest. You still pay the Bitcoin fee. This is the opposite of how mints behave, which is the subject of the next section.

SRC-20 transfers are a single transaction. If you are coming from BRC-20, note there is no inscribe-then-send two-step here: one transaction, one transfer.


5. When your mint comes back smaller

This is the behaviour that most surprises people, and it is worth understanding before you mint rather than after.

Suppose PLATE has max 21000000 and lim 1000, and 20,999,600 have been minted. You broadcast a mint for 1000.

How the amount is decided
StepCheckResult
1Is total minted already at or above max?No, 20,999,600 is below 21,000,000, so the mint continues.
2Does amt exceed the remaining supply of 400?Yes. Reduced to 400. Status OMA. Still valid.
3Does the reduced amount exceed the effective limit of 1000?No. The amount stays 400.
OutcomeValid mint of 400 PLATE. Not a failure, not a refund, not zero.

The same clamping applies when you ask for more than the per-mint limit: a mint of 5000 against a lim of 1000 is reduced to 1000 with status ODL and remains valid.

For implementers

OMA and ODL carry an explicit "not invalid" flag in the reference implementation. If your indexer treats a clamped mint as a rejection, your circulating supply will be lower than everyone else's and your users will see mints that other explorers show as successful. See rules V-6 to V-8 and the test vectors.

Only one mint case is a genuine failure: minting a token whose supply is already fully minted. That is status OM, and it is invalid.


6. What a transaction costs

An SRC-20 transaction has two costs beyond the ordinary.

Locked value in data outputs
Every data output must carry at least a dust amount to be relayed, and that value is permanently unspendable. It is not a fee to miners; it is simply gone. The number of data outputs is set by your payload length: one P2WSH output per 32 bytes, or one multisig output per 62 bytes.
Full transaction weight
Output data gets no witness discount, unlike inscription data. A payload of a given size therefore costs meaningfully more in fees on SRC-20 than the same bytes would in a witness.

Payload length is worth minimising for that reason. The examples above show why the difference is real: a DEPLOY needs 3 data outputs while a MINT needs 2, purely because of JSON length.

Data outputs required by the worked examples
OperationFramed bytesOLGA P2WSH outputsMultisig outputs
DEPLOY9132
MINT6221
TRANSFER6732

Each row needs one further output for the recipient at index 0. Multisig figures assume two data public keys per 1-of-3 output at 31 bytes each.

Bitcoin Universe's Inscribe surface estimates SRC-20 cost as dust for each data output plus the recipient output, network fee at your chosen rate, and a service fee. Those figures are product policy rather than protocol rules, and they can change; the protocol only determines how many data outputs you need.


7. Reading the result

After confirmation, look up the transaction in a Stamps explorer and read the status, not just the confirmation count.

What a status code is telling you
You seeIt meansDid anything happen?
No status, no record at allThe transaction was excluded: the payload never parsed as SRC-20.No
OMA or ODLYour amount was reduced to what was available or allowed.Yes, at the reduced amount
OMThe token was already fully minted before your transaction.No
BBYour balance was below the transfer amount.No, nothing moved
NDThat ticker has never been deployed. Check spelling and case-folding.No
DEOn a second deploy, the ticker was taken. On a first deploy, max or lim was missing or zero.No
IDYour amount had more decimal places than the token allows.No
NAThe amount field was missing or empty.No
UOThe operation name was not DEPLOY, MINT or TRANSFER.No
NNA numeric field failed validation, or dec was out of range.No

In every "No" row you still paid the Bitcoin fee and still burned the dust in the data outputs. The chain does not refund a failed interpretation.


8. Product support matrix

This table reflects the Bitcoin Universe ecosystem capability registry, which is generated from the marketplace protocol registry in the Core repository. Nothing is listed here that is not declared there.

SRC-20 actions by Bitcoin Universe surface
SurfaceSupported actions
Coreview, discover, view-collection, view-activity, view-transaction
Walletview, send, receive
Inscribedeploy, mint, transfer
StampDEXview, discover, view-collection, view-activity, view-transaction, list, unlist, buy, cancel-offer, settle

Marketplace ownership

  • OwnerStampDEXnot Bitcoin Universe
  • Modeexternal-executionUniverse proxies, StampDEX executes
  • Availabilityenabledno feature gate

The order book, the ownership checks and the settlement authority all belong to StampDEX. Bitcoin Universe allowlists and validates the exact proxy route, body and challenge headers, and StampDEX re-checks indexed collateral before a fill. Universe does not compare the provider's tip against Bitcoin's.

Actions that are not supported, and why

These reasons are recorded in the registry itself, not paraphrased.

Unsupported marketplace actions for SRC-20
ActionRecorded reason
update-listingSRC-20 listings must be cancelled and relisted; no atomic listing update is implemented.
make-offerSRC-20 has no executable offer workflow on this marketplace surface.
accept-offerSRC-20 has no executable offer workflow on this marketplace surface.
sellSRC-20 has no executable offer workflow on this marketplace surface.
reconcileSRC-20 has no executable reconcile authority on this marketplace surface.

Freshness, confirmation and reorg policy

Freshness
Not enforced. Bitcoin Universe does not apply a StampDEX-versus-chain lag threshold.
Confirmation
No listing or settlement confirmation minimum is set by Bitcoin Universe. Confirmation policy is delegated to the StampDEX fill workflow.
Reorg
No automatic reconciliation. Bitcoin Universe retains broadcast receipts and delegates chain reconciliation to StampDEX.
Why there is no escrow

SRC-20 is an account ledger, which means a listing cannot be backed by locking a specific output. A seller who signs a listing can still transfer the same balance elsewhere before settlement, and no indexer can prevent that without inventing ownership rules the protocol does not have. Bitcoin Universe's own Stamps indexer therefore reports SRC-20 positions as browse-only, always flagging that no non-custodial escrow primitive is active. Any workable alternative would require either a cooperative multisig with seller liveness risk or outright custody, and both are product decisions rather than indexer settings.


9. Staying safe

  • Never share a seed phrase, recovery phrase or private key. No explorer, indexer or support channel needs one.
  • Never blind-sign. Before approving, check the network, the recipient, the amount, every output and the fee. An SRC-20 transaction has several unusual-looking outputs by design, so know what you expect to see.
  • Verify a token by its deploy transaction, not by a familiar ticker or logo. Tickers are only 5 code points and lookalikes are trivial to make. The deploy transaction hash is the thing that identifies a token.
  • Treat confirmation and success as separate. Follow the result in an explorer after the transaction confirms.
  • Remember the dust is gone. Value locked in data outputs is unspendable, on a failed operation as much as on a successful one.
  • External links are not identity. The x, web, email and tg fields in a DEPLOY are free text written by whoever deployed the token, and nothing verifies them.

To report a vulnerability in this documentation or its validator, use private vulnerability reporting.