Carrier comparison
Four ways to put data on Bitcoin
OP_DROP is one of a small number of protocols Bitcoin Universe designed rather than adopted, so it has to earn its place next to the carriers that already work. This page states the trade honestly, including the cases where you should not use it.
Attribution. OP_RETURN is a Bitcoin Core feature, not a protocol of this organisation. Witness envelopes are the construction popularised by the Ordinals project. Bitcoin Stamps originated outside Bitcoin Universe. OP_DROP is a Bitcoin Universe design. Relay and standardness behaviour described below is node policy, which varies by implementation, version, and operator configuration, and is not consensus.
Where the bytes live
Every carrier answers the same question differently: which part of the transaction holds the data, and what does that part cost.
Property matrix
| Property | OP_RETURN | Witness envelope | OP_DROP | Stamps |
|---|---|---|---|---|
| Data location | Output script | Tapscript leaf, unexecuted branch | Tapscript leaf, executed pushes | Output scripts (bare multisig) |
| Weight per data byte | 4 | 1 | 1 | 4 |
| Transactions needed | 1 | 2 (commit, reveal) | 2 (commit, reveal) | 1 |
| Enters the UTXO set | No | No for the data; the reveal output does | Anchor output does | Yes, permanently |
| Survives node pruning | No | No | No | Yes |
| Practical payload size | Historically 80 bytes by default relay policy | Effectively block-sized, chunked into 520-byte pushes | 256 bytes per push; one JSON object per leaf | Limited by fee tolerance and output count |
| Data is executed | Never, the output is provably unspendable | Never, the branch is skipped | Yes, pushed then dropped | Never in practice |
| Conditional opcodes used | None | OP_IF / OP_ENDIF | None | None |
| Data readable without the spend | Yes, visible the moment it confirms | No, only after the reveal | No, only after the reveal | Yes |
| Ecosystem tooling | Universal | Broad | Bitcoin Universe only | Stamps ecosystem |
Cost, worked out
Take a 54-byte payload, the exact size of the OP_DROP mint event {"p":"op-drop","op":"mint","tick":"drop","amt":"1000"}, and price it four ways. Assumptions are stated so you can check the arithmetic: one Taproot key-path input, Taproot outputs, one change output where the carrier needs one, no consolidation, and no wallet-specific padding.
| Carrier | Transactions | Total vsize | Relative | Notes |
|---|---|---|---|---|
| OP_RETURN | 1 | ~165 vB | 1.0x | Cheapest for a small payload. The data output carries no value and creates no UTXO. |
| OP_DROP | 2 | ~315 vB | ~1.9x | Reveal alone is 160 vB (637 weight units: 94 base bytes, 259 witness bytes). The commit transaction adds roughly 155 vB. |
| Witness envelope | 2 | ~310 vB | ~1.9x | Nearly identical to OP_DROP at this size. The envelope wins as payloads grow, because it chunks at 520 bytes per push instead of 256. |
| Stamps | 1 | ~230 vB | ~1.4x | Plus a permanent cost every node pays forever: the outputs stay in the UTXO set. |
The OP_DROP figures come from the exact leaf scripts on the test vectors page and are reproduced by the builder. The other three are estimates for comparison, not authoritative figures for those protocols.
Against OP_RETURN
OP_RETURN is the carrier Bitcoin Core built on purpose. An output whose script begins with OP_RETURN is provably unspendable, so nodes drop it from the UTXO set immediately. Nothing about OP_DROP beats that property.
| Dimension | OP_RETURN | OP_DROP |
|---|---|---|
| Size limit | A relay-policy limit, historically 80 bytes of data by default in Bitcoin Core, with one such output per transaction. Bitcoin Core relaxed those defaults in its 2025 release line, and other implementations and older nodes still apply the old limits. Consensus itself does not impose 80 bytes. | A protocol limit of 256 bytes per push, enforced by the OP_DROP decoder rather than by any node. |
| Prunability | Best available. Never in the UTXO set. | Worse. Each event's reveal creates a spendable P2TR anchor output that does enter the UTXO set until spent. |
| Standardness | Explicitly recognised by node policy, with a dedicated configuration switch. | Not recognised by anything. It relies only on ordinary Taproot script-path spending, which is standard, but no node has a concept of OP_DROP. |
| Cost per byte | 4 weight units. | 1 weight unit, so roughly a quarter of the marginal cost, offset by needing a second transaction. |
| Time to readability | Immediate. The data is in the transaction that confirms. | Delayed. Nothing is readable until the commit output is spent in the reveal. |
| Payload framing | None. The bytes are whatever you put there. | A fixed grammar with a marker, a declared content type, and a digest that must match. |
Use OP_RETURN instead when the payload fits comfortably inside your target relay policy, one transaction matters more than marginal fee, and you want the data visible without a second step.
Against witness envelopes
The Ordinals-style envelope wraps content in OP_FALSE OP_IF ... OP_ENDIF inside a Taproot leaf. Execution reaches OP_IF, sees false, and skips everything until OP_ENDIF. The content is committed and provable, and it never touches the stack.
OP_DROP and the envelope live in the same place at the same price. The difference is the mechanism and the shape.
| Dimension | Witness envelope | OP_DROP |
|---|---|---|
| Mechanism | Data is skipped by a conditional branch. | Data is pushed and removed by an opcode that runs. |
| Conditional opcodes | Requires OP_IF and OP_ENDIF. | None. Every element in the leaf executes in order. |
| Chunk size | 520 bytes per push, so fewer opcodes per kilobyte. | 256 bytes per push, so more overhead per kilobyte. |
| Payload size | Practically unbounded, up to block weight. | One JSON object of at most 256 bytes for a token event. An optional image attachment chunks up to 256 KiB, at 256 bytes per chunk. |
| Grammar | Flexible. Fields, metadata, and pointers vary by version and by implementation. | Fixed. Five fields, a required order, a required digest, and a byte-exact rebuild check. |
| Ecosystem | Widely indexed and widely supported by wallets and marketplaces. | Indexed only by Bitcoin Universe. |
| Content addressing | Content hash is derived by indexers; the envelope does not require one in the script. | A SHA-256 digest of the payload is a required script field, checked before the payload is accepted. |
Use an envelope instead when the payload is larger than a few hundred bytes, when you need arbitrary content types, or when you need an ecosystem of existing indexers and marketplaces to see your data. Those are the majority of cases.
Against Stamps unprunable outputs
Bitcoin Stamps made the opposite trade on purpose. It stores data in outputs that look spendable, so the bytes land in every node's UTXO set and stay there. Pruning a node does not remove them, and no future policy change can make them disappear from the set until someone spends them.
| Dimension | Stamps | OP_DROP |
|---|---|---|
| Retention guarantee | Strongest of the four. Every validating node keeps the bytes in the UTXO set, whether or not it keeps old blocks. | Weaker. A pruned node discards the block, and with it the witness holding the payload. Recovering an old OP_DROP payload needs an archival node or a copy from an indexer. |
| Externalised cost | High and permanent, paid by every node operator in UTXO set growth. | Lower and temporary, except for the one small anchor output per event. |
| Cost to the creator | 4 weight units per byte, plus dust value locked into each data output. | 1 weight unit per byte, plus one anchor output. |
| Readability | Immediate, from the transaction that confirms. | Only after the reveal transaction confirms. |
| Node operator reaction | Openly contested. Some operators consider it abuse of the UTXO set. | Untested at scale. It has the same profile as any Taproot script-path spend, but it has no track record. |
Use Stamps instead when maximum retention is the requirement and you accept both the cost and the criticism that comes with it.
Where OP_DROP is worse
Collected in one place, so it does not have to be assembled from the tables above.
- Nothing outside Bitcoin Universe reads it. No third-party wallet, explorer, marketplace, miner, or indexer recognises an OP_DROP event. This is the largest practical drawback and it is not fixable by documentation.
- It costs about twice an OP_RETURN for a small payload, because it needs a commit transaction and a reveal transaction. The witness discount only pays off at sizes larger than a token event.
- It adds to the UTXO set. Every event's reveal creates a P2TR anchor output. OP_RETURN adds none.
- 256 bytes per push is restrictive. An envelope chunks at 520. For large content OP_DROP needs roughly twice the pushes and twice the per-chunk overhead.
- Data is invisible until the reveal. Anyone watching the commit transaction sees only an ordinary Taproot payment. That is fine for privacy and bad for anyone who wants to read intent from the mempool.
- Prunability is worse than Stamps and no better than an envelope. The payload lives in witness data that a pruned node throws away.
- The ledger profile is narrower than the carrier. Tickers are exactly four characters,
self_mintis rejected, and there are no decimals. A leaf can be a valid carrier and still change no balance. - One JSON object per leaf. There is no batching of several token events into a single reveal, so a five-mint batch is five separate inscriptions.
- Strict serialization is unforgiving. A single space, a reordered key, or a numeric instead of a string makes the event invalid. That is deliberate, but it means a careless encoder produces confirmed transactions that do nothing.
Choosing
| If you need | Use |
|---|---|
| A small commitment, one transaction, universal tooling | OP_RETURN |
| Arbitrary content of any size with an existing ecosystem | A witness envelope |
| Data every node keeps regardless of pruning | Stamps, accepting the cost |
| A small, strictly framed, self-verifying event with a fixed grammar and no conditional opcodes, inside Bitcoin Universe products | OP_DROP |
OP_DROP is not trying to replace the other three. It is a narrow carrier for a narrow job: a compact event with a digest, a fixed shape, a script that is trivial to validate, and a deliberately conservative profile. If your job is wider than that, one of the other three is the better answer.