Practical guide
What a transaction actually looks like.
Three worked examples, byte by byte, plus a straight answer about what Bitcoin Universe products will and will not do with each protocol.
The mental model
Every transaction on this carrier has the same three parts, and confusing them is where almost all mistakes come from.
- The intent lives in the OP_RETURN payload. It says what you want to happen.
- The result holder is a different, spendable output. For OP Names it is called the receipt; for OP-20 it is the recipient address or the transfer UTXO.
- The interpretation happens later, in an indexer that replays blocks. Bitcoin confirms your bytes. An indexer decides what they mean.
A transaction can confirm perfectly and still change nothing at the protocol layer, because step three rejected it. That is not a failure of Bitcoin, and there is no refund.
Worked example: an OP-20 mint
Minting 1000 units of the token whose ticker is written PIPE.
The payload
{"p":"op-20","op":"mint","tick":"PIPE","amt":"1000"}
52 bytes of UTF-8. The tick is written in uppercase but the reader normalises it to the reference pipe, which is what identity is compared on. The stable asset identity becomes op_return:op20:b64.cGlwZQ.
The script
scriptPubKey, 54 bytes: 6a347b2270223a226f702d3230222c226f70223a226d696e74222c227469636b223a2250495045222c22616d74223a2231303030227d
The transaction
| Output | Value | Script | Purpose |
|---|---|---|---|
| vout 0 | 0 sats | OP_RETURN payload | the intent |
| vout 1 | 546 sats or more | your address | where the mint lands |
| vout 2 | remainder | your change address | fee change |
The whole scriptPubKey is 54 bytes, well inside the 83 byte default relay limit, so this propagates everywhere. A ticker long enough to push the script past 83 bytes will still be valid but may not relay on older default policy.
Putting two OP-20 payloads in one transaction is rejected outright with protocol_mismatch. Neither one applies. Send two transactions.
Worked example: an OP Names registration
Registering satoshi.btc. Two encodings are accepted and they behave differently.
Legacy plain text, 11 bytes
satoshi.btc
scriptPubKey, 13 bytes: 6a0b7361746f7368692e627463
No protocol tag at all. The reader accepts it because the trimmed text has exactly one literal dot. Ownership comes from the receipt output.
Explicit JSON with an address
{"p":"opns","op":"reg","name":"satoshi.btc","add":"bc1q..."}
Here the owner is stated in the payload, so the name enters address custody with that address and the receipt output does not decide the owner.
Which output is the receipt
This is the rule people get wrong. Without add, the receipt is output 0 when the OP_RETURN sits at any index above 0, and output 1 otherwise.
| Layout | Receipt index | Result |
|---|---|---|
| vout 0 = OP_RETURN, vout 1 = your address | 1 | owner is the vout 1 address |
| vout 0 = your address, vout 1 = OP_RETURN | 0 | owner is the vout 0 address |
| vout 0 = OP_RETURN, no other output | 1 | missing_receipt_output, the name is burned |
| vout 0 = OP_RETURN, vout 1 = another OP_RETURN | 1 | ambiguous_op_return, nothing is applied at all |
OP Names is stricter than OP-20 here. If your wallet adds any second OP_RETURN output for its own purposes, the entire transaction becomes ambiguous_op_return and your registration does not happen.
Moving a name later
A name in address custody moves only by an explicit transfer payload whose input 0 spends an output belonging to the exact current owner. That authorised transfer places the name into UTXO custody at the receipt outpoint. From then on the name follows spends of that exact outpoint to output 0, which is what makes a name behave like a physical object rather than a balance.
Worked example: OP Inscriptions content
Content written directly into the carrier, with an explicit protocol tag.
{"p":"op-inscriptions","op":"inscribe","add":"bc1q..."}
The record identity is the outpoint, txid:vout. Anything larger than a single carrier is written as several OP_RETURN records that share a group identifier and a declared total; the indexer assembles them only when every part is present exactly once. A group missing one part is never rendered and never partially shown.
The OP Inscriptions scanner tags foreign payloads it encounters, so an OP-20 mint appears in its catalogue labelled OP-20 and a dotted name appears labelled OP_NAMES. That label is a catalogue convenience. It does not make the payload an OP inscription, and it does not mean the owning protocol's own reader accepted it. The bare text label in particular is looser than the real OP Names identity rule.
Support matrix
Generated from the Core capability snapshot, which is generated in turn from the marketplace protocol registry in bitcoinuniverseio/core. Only claims backed by that snapshot appear here.
Bitcoin Universe surfaces
| Surface | OP-20 | OP Names | OP Inscriptions |
|---|---|---|---|
| Core | view, discover, view-collection, view-activity, view-transaction | view, discover, view-collection, view-activity, view-transaction | the same five, plus list, unlist, buy, settle, reconcile |
| Wallet | view, send, receive | view, send, receive | view, send, receive |
| Inscribe | deploy, mint, inscribe, transfer | not recorded | not recorded |
OP-20 is the only one of the three with a recorded Inscribe surface. OP Names and OP Inscriptions have no creation surface recorded in the registry, so this site does not claim one.
Marketplace actions and the recorded reason for each refusal
| Action | Supported | Recorded reason when not |
|---|---|---|
| view, view-collection, view-activity | yes, read-only | |
| list | no | Legacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410. |
| update-listing | no | Legacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410. |
| unlist | no | Legacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410. |
| buy | no | Legacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410. The current UI can reach a broadcast step before registration fails, so buy is not declared supported. |
| make-offer, accept-offer, cancel-offer, sell | no | OP-20 has no executable offer workflow on this marketplace surface. |
| settle | no | OP-20 has no executable settle authority on this marketplace surface. |
| reconcile | no | OP-20 has no executable reconcile authority on this marketplace surface. |
The interface can reach a broadcast step before the registration fails. A user can therefore spend real fees on a purchase that never registers. That is exactly why buy is not declared supported for OP-20 or OP Names, and why this documentation will not describe an OP-20 purchase flow.
| Action | Supported | Recorded reason when not |
|---|---|---|
| view, view-collection, view-activity | yes, read-only | |
| list, update-listing, unlist, buy | no | Legacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410. For buy, the current UI can reach a broadcast step before registration fails. |
| make-offer, accept-offer, cancel-offer, sell | no | OP Names has no executable offer workflow on this marketplace surface. |
| settle | no | OP Names has no executable settle authority on this marketplace surface. |
| reconcile | no | OP Names has no executable reconcile authority on this marketplace surface. |
| Action | Supported | Recorded reason when not |
|---|---|---|
| view, view-collection, view-activity | yes, in app | |
| list | yes, in app | |
| unlist | yes, in app | |
| buy | yes, in app | |
| settle | yes, in app | |
| reconcile | yes, in app | |
| update-listing | no | OP Inscriptions listings must be cancelled and relisted; no atomic listing update is implemented. |
| make-offer, accept-offer, cancel-offer, sell | no | OP Inscriptions has no executable offer workflow on this marketplace surface. |
This is the useful contrast between three protocols on the same carrier: sharing an output type buys them nothing in common at the product layer. What decides tradeability is whether a settlement authority exists that can prove a fill from chain data. OP Inscriptions has one. OP-20 and OP Names do not.
Before you sign
- Check the network. Every rule here is mainnet.
- Read the payload, not the interface. Decode the actual scriptPubKey with the decoder and confirm the ticker, amount and recipient are what you expect.
- Count the OP_RETURN outputs. One. Always one.
- Check the value on the data output is zero. Anything else is destroyed.
- Identify the receipt output. For an OP Names registration without
add, know which output will own the name. - Check your coin selection. If your wallet does not know about pending OP-20 transfer UTXOs, name custody outpoints and listed assets, it can spend one as fee change and destroy it.
- Accept the fee. A rejected payload still costs the fee. There is no reversal.