Reference
Reference
The operational half of the protocol: what an indexer must do with the chain, what an OP_DROP transaction costs, and everything that can go wrong.
Terminology
| Term | API field | Meaning |
|---|---|---|
| Carrier marker | - | The fixed ASCII string bip110-op-drop at the head of every leaf. Never a user-facing name. |
| Protocol identifier | p | The string op-drop in every payload. The public name. |
| Leaf script | scriptHex | The tapscript revealed in the reveal witness. |
| Control block | controlBlockHex | The 33-byte Taproot proof that the leaf belonged to the spent output. |
| Anchor | anchor | {txid}:0. Output 0 of the reveal. Its address owns the event; spending it settles a transfer. |
| Owner | owner | The address of output 0 of the reveal transaction. |
| Available | available | Confirmed units usable in a new transfer. |
| Reserved | transferable | Confirmed units held by an unsettled transfer. The interface word is "reserved"; the API field is transferable. |
| Total | total | Always available + transferable. Never stored separately. |
| Event id | eventId | {txid}:i{vin}:w1 for a reveal, {txid}:s{vin}:{transferEventId} for a settlement. |
| Minted supply | minted | The sum of all credited mint amounts for a ticker. |
| Remaining | remaining | max - minted. |
| Holder | holders | An address whose available or reserved balance for a ticker is non-zero. |
Indexer semantics
Confirmation
- The ledger reflects confirmed chain history only. An event affects state when its block reaches the deployment's configured confirmation depth.
- Confirmation depth is per deployment and is reported by the indexer status endpoint. The software refuses to run below one confirmation on mainnet, and the reference configuration uses six.
- Blocks are processed in batches, and the scan interval is configurable. Neither affects which events are valid, only how quickly they appear.
- The scanner is a single writer. Read replicas serve the last fully committed state while the scanner works, so a partially processed block never becomes a visible balance.
- Before its cursor is treated as synchronised, the reference scanner checks finalized block hashes against two independently operated Bitcoin nodes.
- While the scanner is catching up, an interface may report that state is warming up. Existing confirmed records stay readable; new events should not be treated as admitted until synchronisation is restored.
Determinism
- Events apply in the order defined by OD-9.1: block height, then transaction index, then operation index. Nothing depends on discovery order or wall-clock time.
- Within one transaction, settlements (phase 0) apply before reveal leaves (phase 1). Units that arrive by settlement are therefore usable by a later operation in the same transaction.
- An event id is unique per network. Re-observing one is a no-op.
- An indexer must refuse to project state for a block whose reduced-data deployment status it cannot determine. Stopping is correct; guessing is not.
Mempool
The ledger never includes mempool activity. There is no such thing as an unconfirmed OP_DROP balance.
The reference indexer can separately track a submitted transaction id so an interface can tell a user where their transaction is. That tracking reports one of three chain states and always carries the same warning.
| State | Meaning | Effect on balances |
|---|---|---|
mempool | The transaction is known to the node but is in no block. | None |
mined | It is in a block that has not reached the confirmation depth. | None |
finalized | It is in a block at or beyond the confirmation depth. | Applied by the ledger |
The warning attached to every provisional record is exact: "Does not affect balances until finalized." Tracking records are retained for a bounded period and are not part of the ledger.
Reorg behaviour
A confirmed OP_DROP result can change if the block containing it is replaced. That is a property of Bitcoin, not a defect in the protocol.
- When a reorganisation is detected, the ledger is reconciled to the current chain: events in replaced blocks are removed and projections are rebuilt from the surviving immutable event rows.
- Automatic recovery is deliberately bounded. The reference default allows a rollback of at most 24 blocks (configurable up to 144) and a rebuild of at most 100,000 surviving rows.
- A divergence beyond those bounds is not handled automatically. It requires an authenticated operator replay, on the reasoning that anything deeper than a day of Bitcoin blocks deserves a human looking at the node first.
- Rollback and rebuild happen inside one database transaction per block, so neither the ledger nor the scan cursor can advance halfway.
- A reorg can change a settled transfer back to pending, or a valid mint back to nothing. Consumers should treat a recent event as provisional until it is deep.
Fee and size
Where the bytes go
A reveal transaction's weight is base bytes × 4 + 2 + witness bytes, and virtual size is that weight rounded up to the next whole vbyte. The leaf script and the control block sit in the witness, so they cost a quarter of what the same bytes would cost in an output.
| Operation | Payload | Leaf script | Base | Witness | Weight | vsize |
|---|---|---|---|---|---|---|
deploy drop | 73 B | 177 B | 94 B | 278 B | 656 WU | 164 vB |
| mint 1000 | 54 B | 158 B | 94 B | 259 B | 637 WU | 160 vB |
| transfer 250 | 57 B | 161 B | 94 B | 262 B | 640 WU | 160 vB |
| largest deploy | 101 B | 206 B | 94 B | 307 B | 685 WU | 172 vB |
| Fee rate | Reveal fee |
|---|---|
| 1 sat/vB | 160 sat |
| 5 sat/vB | 800 sat |
| 15 sat/vB | 2,400 sat |
| 40 sat/vB | 6,400 sat |
What the table leaves out
- The commit transaction. Funding the Taproot address is a separate transaction, roughly 155 vB for one input and two outputs. Budget for both.
- The anchor output's value. Output 0 must be above the dust threshold, which is 330 satoshis for a P2TR output. That value is not a fee; it stays spendable, and for a transfer it is spent again at settlement.
- The settlement transaction. A transfer needs a third transaction to spend the anchor.
- Batching. One leaf carries one event. Five mints are five inscriptions, so five commits and five reveals, not one transaction with five payloads.
- The media attachment. A 256 KiB image adds at least 1024 chunks, each with a push header and an
OP_DROP, so roughly 262 KB of witness data, about 65,500 additional vbytes.
Standardness and relay
An OP_DROP transaction is an ordinary Taproot script-path spend. No node has any concept of OP_DROP, which is both the reassuring part and the risky part.
- No special policy applies. There is no data-carrier size switch for witness data, and no relay rule targets this construction. It is relayed on the same terms as any other tapscript spend.
- Standard transaction weight applies. A very large media attachment can push a reveal toward the standard transaction weight ceiling that most nodes enforce, at which point it will not relay even though it would be consensus valid inside a block.
- The dust threshold applies to output 0. Below it, the transaction is non-standard and will not relay.
- Relay is per node. Operators can and do run custom policies, including filters against transactions they judge to be carrying data. Nothing in OP_DROP prevents an operator from refusing it.
- Nothing here is consensus. Relay and mining are best-effort. A valid OP_DROP transaction can sit unconfirmed indefinitely if nobody mines it.
- The reduced-data construction profile in section 13 is stricter than any current policy. It is a design constraint chosen in advance, not a response to a rule that exists today.
Limitations
- One JSON object per leaf. No batching of events into a single reveal.
- 256 bytes per data push, which the token payload never approaches but which forces small chunks for the media attachment.
- Tickers are exactly four lowercase alphanumeric characters. There is no longer form and no namespace.
- No decimals. Every quantity is a whole number of units.
- No self-mint mode. The carrier will encode
self_mint, the ledger always rejects it. - Two transactions minimum, three for a completed transfer.
- Output 0 is load bearing in both the reveal and the settlement, which makes the protocol sensitive to how a wallet orders outputs.
- Only one transfer per transaction. Competing transfers are all rejected.
- The payload is invisible until the reveal, so intent cannot be read from the mempool at commit time.
- The payload lives in witness data, which a pruned node discards. Recovering an old payload requires an archival node or an indexer copy.
- No third-party support. Nothing outside Bitcoin Universe indexes, displays, or trades OP_DROP.
- Tickers are unreserved and first-come. A deploy can be front-run by anyone watching the mempool for your commit and reveal.
- Strict serialization means a small encoder bug produces confirmed transactions that change nothing, and the fee is still spent.
- Lifecycle is experimental. The rules may change, and a change would be published in the changelog before it is deployed.
Security considerations
For anyone creating events
- The payload is permanent and public. Once the reveal confirms, the bytes are in the blockchain forever. Do not put anything private in a payload.
- The key in the leaf must be one you control. The leaf's
OP_CHECKSIGis the only spend condition. Committing a leaf with someone else's key hands them the output. - Output 0 discipline. If your wallet places change at output 0 of a settlement, the change address receives the transferred units. Verify output ordering before signing.
- Anchor control is settlement control. Whoever can spend the anchor decides the destination. If an anchor is held in escrow by someone else, they control where the units go.
- Deploys can be front-run. A commit is opaque, but a reveal is not. Between your reveal broadcasting and confirming, another party can attempt the same ticker with a higher fee.
- A failed event still costs the fee. Losing a mint race, breaking a serialization rule, or exceeding the supply all produce confirmed transactions that change nothing.
- Reusing one internal key across many commits links those events to each other on chain.
For anyone building an indexer or a decoder
- Never accept a leaf without the commitment proof. A witness item that decodes as an OP_DROP leaf proves nothing on its own. Check the control block against the spent output every time.
- Enforce the rebuild check. Comparing decoded fields is not enough. Recompile the leaf from those fields and compare the bytes (OD-4.8), or a non-minimal encoding will slip through and your index will diverge.
- Enforce the payload round trip. Re-serialize the parsed object and compare it with the pushed bytes (OD-6.12), or whitespace and key-order variants will be accepted.
- Resolve previous outputs from your own node. The previous output script is the whole basis of the commitment check. Taking it from a third-party API means trusting that API with the validity of your ledger.
- Use exact integers. Amounts reach 2128 minus 1. Parsing them as floating point silently corrupts balances. Keep them as strings or big integers end to end.
- Apply events in the specified order, always. Any other order changes which deploy wins and which mint gets the last of the supply.
- Record invalid events. Silently dropping them makes divergence between implementations impossible to diagnose.
- Handle reorgs before you handle scale. An index that cannot roll back is wrong, not just slow.
- Do not derive OP_DROP state from an Ordinals or BRC-20 index. They are unrelated records.
For anyone consuming an OP_DROP API
- An indexer is an interpretation, not the chain. For anything valuable, verify against block data yourself.
- A "warming up" or partial response is not evidence that a token or balance does not exist.
- Recent events can be reversed by a reorg. Depth matters.
- Reserved units are already deducted from available. Do not add them twice.
- Report suspected vulnerabilities privately through GitHub private vulnerability reporting, never in a public issue.
Implementation checklist
Work down this list. Each item is testable against the test vectors.
Encoder
- Serialize the payload compactly, in the required key order, with string values only.
- Validate ranges before encoding: four-character ticker, positive integers with no leading zero,
limnot greater thanmax, values inside the 64-bit and 128-bit bounds. - Compute SHA-256 of the payload bytes and place it in element 5.
- Use minimal push encoding for every element.
- Reject any payload above 256 bytes rather than chunking it.
- Verify by decoding your own output and comparing the bytes.
- Build the commit output as a single-leaf Taproot tree at version
0xc0. - Fund the commit output above the dust threshold and budget for the reveal.
Decoder
- Require exactly three witness items, a 64 or 65-byte signature, and a 33-byte control block whose first byte is not
0x50. - Require a P2TR previous output.
- Verify the tapleaf version, the Merkle root, the key parity, and the output key against the previous output.
- Parse the ten script elements and check the marker, the content type, both 32-byte fields, and the four
OP_DROPpositions plusOP_CHECKSIG. - Recompile and compare byte for byte.
- Verify the digest against the payload.
- Validate the payload and re-serialize to confirm compactness.
- Reproduce every reason code on the test vectors page.
Indexer
- Scan confirmed blocks with a reorg-safe cursor and your own full node.
- Resolve previous outputs from that node, never from a third party.
- Derive the owner from output 0 of the reveal, and treat output 0 of the settling transaction as the only possible destination.
- Apply the ordering rule exactly, with settlements before reveals inside a transaction.
- Implement first valid deploy wins, the mint limit, the partial final mint, and supply exhaustion.
- Implement two-stage transfers, including the return path when a destination cannot be identified.
- Reject a transaction carrying more than one valid transfer leaf.
- Record invalid events with a reason and change no balances.
- Roll back and rebuild on reorg, inside one transaction per block, with an explicit bound on automatic recovery.
- Refuse to project state for a block whose construction-rule status is indeterminate.
- Expose available, reserved, and total separately, as decimal strings.
- Reproduce every ledger and transfer sequence on the test vectors page.