OP_RETURN

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.

  1. The intent lives in the OP_RETURN payload. It says what you want to happen.
  2. 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.
  3. 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

byte 06aOP_RETURN
byte 134push, 0x34 = 52 bytes
bytes 2..537b2270223a226f702d3230222c...227dthe JSON above

scriptPubKey, 54 bytes: 6a347b2270223a226f702d3230222c226f70223a226d696e74222c227469636b223a2250495045222c22616d74223a2231303030227d

The transaction

Output layout for an OP-20 mint
OutputValueScriptPurpose
vout 00 satsOP_RETURN payloadthe intent
vout 1546 sats or moreyour addresswhere the mint lands
vout 2remainderyour change addressfee 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.

Two mints in one transaction

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.

Receipt selection, verified against the reader
LayoutReceipt indexResult
vout 0 = OP_RETURN, vout 1 = your address1owner is the vout 1 address
vout 0 = your address, vout 1 = OP_RETURN0owner is the vout 0 address
vout 0 = OP_RETURN, no other output1missing_receipt_output, the name is burned
vout 0 = OP_RETURN, vout 1 = another OP_RETURN1ambiguous_op_return, nothing is applied at all
One data output only

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.

What the catalogue labels mean

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

Which product surface can do what with each protocol
SurfaceOP-20OP NamesOP Inscriptions
Coreview, discover, view-collection, view-activity, view-transactionview, discover, view-collection, view-activity, view-transactionthe same five, plus list, unlist, buy, settle, reconcile
Walletview, send, receiveview, send, receiveview, send, receive
Inscribedeploy, mint, inscribe, transfernot recordednot 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

OP-20, registry id op_return. Availability read-only, mode read-only
ActionSupportedRecorded reason when not
view, view-collection, view-activityyes, read-only
listnoLegacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410.
update-listingnoLegacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410.
unlistnoLegacy random-key/replay marketplace mutations are retired and fail closed; the backend returns HTTP 410.
buynoLegacy 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, sellnoOP-20 has no executable offer workflow on this marketplace surface.
settlenoOP-20 has no executable settle authority on this marketplace surface.
reconcilenoOP-20 has no executable reconcile authority on this marketplace surface.
Read that buy reason twice

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.

OP Names, registry id op_names. Availability read-only, mode read-only
ActionSupportedRecorded reason when not
view, view-collection, view-activityyes, read-only
list, update-listing, unlist, buynoLegacy 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, sellnoOP Names has no executable offer workflow on this marketplace surface.
settlenoOP Names has no executable settle authority on this marketplace surface.
reconcilenoOP Names has no executable reconcile authority on this marketplace surface.
OP Inscriptions, registry id op_inscriptions. Availability enabled, mode in-app-execution
ActionSupportedRecorded reason when not
view, view-collection, view-activityyes, in app
listyes, in app
unlistyes, in app
buyyes, in app
settleyes, in app
reconcileyes, in app
update-listingnoOP Inscriptions listings must be cancelled and relisted; no atomic listing update is implemented.
make-offer, accept-offer, cancel-offer, sellnoOP 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

  1. Check the network. Every rule here is mainnet.
  2. Read the payload, not the interface. Decode the actual scriptPubKey with the decoder and confirm the ticker, amount and recipient are what you expect.
  3. Count the OP_RETURN outputs. One. Always one.
  4. Check the value on the data output is zero. Anything else is destroyed.
  5. Identify the receipt output. For an OP Names registration without add, know which output will own the name.
  6. 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.
  7. Accept the fee. A rejected payload still costs the fee. There is no reversal.