MezcalProtocol documentation

Reference

Mezcal reference

Terminology, what an indexer must do with confirmations and reorganizations, what a payload costs, where the protocol's sharp edges are, and how the Bitcoin Universe marketplace constrains the one Mezcal payload it builds.

Terminology

TermMeaning
MezcalstoneThe JSON object carried in the OP_RETURN output. One per transaction, taken from the first such output.
EtchingThe creation of an asset, and the etching object that performs it.
Asset id, protocol idblock:tx. The permanent identifier of an asset. Core's marketplace calls this field the ticker.
EdictOne allocation instruction: asset id, base-unit amount, output index.
PointerThe output index that receives everything the edicts did not allocate.
Unallocated poolPer-asset balance inside a transaction being processed: inputs plus premine plus mint, minus what edicts have already moved.
CenotaphA transaction whose instruction is unreadable or invalid. Its payload is discarded and every Mezcal balance on its inputs is burnt.
Base unitsThe integer unit of an asset. Display amount times 10^divisibility.
Flex mintA mint whose amount is derived from the satoshis paid, rather than fixed by the terms.
TurboA boolean flag in an etching, default true. It is recorded with the asset.

Indexer semantics

Mezcal state is a pure function of confirmed Bitcoin blocks. Two indexers that replay the same chain from block 898750 with the same rules must reach the same balances.

Confirmation

  • Balances and events exist only for confirmed transactions. A transaction in the mempool has no protocol effect yet.
  • The Bitcoin Universe Mezcal indexer will not publish a row before Bitcoin Core confirms the block hash, the block timestamp, and the transaction's membership in that block. Membership is proven with a Merkle transaction proof against an explicit block hash, so a pruned node that no longer holds a required block fails closed rather than guessing.
  • Events carry the block height, the block hash, and a confirmation count derived from the node's own tip.

Mempool

  • There is no complete Mezcal mempool feed. Bitcoin Universe reports its Mezcal source coverage as partial for exactly this reason, and marks pending activity unavailable rather than inferring it.
  • Unconfirmed activity is never synthesized from local marketplace orders. An order is not protocol state.
  • An application that wants a pending view must decode the mempool itself, and must treat the result as a prediction: a conflicting transaction, a replacement, or a reorganization can change the outcome.

Reorganizations

Reorganization handling The indexer keeps a checkpoint of the last confirmed height and hash and a fingerprint for the tail of each ordered stream. When the chain replaces a block, the checkpoint no longer matches, readiness fails, the indexer rewinds to the first changed offset, emits invalidations for events with no surviving source row, and rebuilds balances forward. Accepted chain n-2 n-1 n n' new n orphan Checkpoint mismatch last confirmed hash no longer agrees Readiness fails immediately no new cursor may advance Recovery 1. Rewind to the first changed offset in each ordered stream, found by tail fingerprints. 2. Emit invalidations for events with no surviving source row, rebuild balances, requeue holder snapshots.
A reorganization is not a special case to patch around. It is the ordinary consequence of building on Bitcoin, and the indexer is expected to rewind rather than paper over it.
  • The indexer holds a checkpoint: the last confirmed height and its block hash. Both must still agree with Bitcoin Core before a new read view opens. A mismatch fails readiness immediately and stops cursors from advancing.
  • Fingerprints are kept for the bounded tail of every ordered stream. When upstream rows are replaced, the indexer rewinds at the first changed offset, emits durable invalidations for events that no longer have a source row, rebuilds wallet metrics, and queues fresh holder snapshots.
  • An identical event that reappears after a reorganization keeps its original event id, so a downstream projection can reactivate it instead of double counting it.
  • Holder snapshots are published as a whole generation or not at all. A generation that could not be completed against one consistent view is discarded and restarted rather than mixing balances from different blocks.

Numbers

  • Amounts are exact integers end to end. Values are carried as text out of the database and converted with big integers, never through a floating-point number.
  • Circulating supply is computed as total_supply - burnt_amount. That is a Bitcoin Universe normalization decision, not a protocol field.
  • A maximum supply is published only when a fixed cap and a fixed mint amount prove one, or when the asset is already unmintable. Flex mints are given no guessed maximum.
  • The sentinel values COINBASE, OP_RETURN, and UNKNOWN that appear in place of an address are preserved as evidence but represented as no wallet, and excluded from holder snapshots.

Core marketplace behaviour

The Mezcal marketplace surface in Bitcoin Universe Core is declared read-only in the protocol registry: discovery is supported, and list, update, unlist, and buy are declared unsupported until authoritative ownership, transferability, builder, signed-transaction validation, broadcast, settlement, and reorg recovery are deployed and proven. The rules below describe how the code behind that surface is constrained, because they are useful to anyone building a comparable flow.

Proving a listing

Before a Mezcal listing can be registered, updated, delisted, or bought, Core re-proves it against two independent sources: the Bitcoin prevout, and the Mezcal authority's record for the same outpoint. A listing is valid only when all of the following hold.

What a Mezcal listing claim must prove, and the reason returned when it does not.
CheckFailure reason
The claim itself parses: a well-formed outpoint, ticker, amount, owner address, and satoshi valueinvalid-claim
The Bitcoin prevout exists at that exact outpoint, holds exactly the claimed satoshi value, is confirmed, is unspent, and pays the claimed ownerbitcoin-prevout
The Mezcal record is for the same transaction and output index, the same satoshi value, the same owner, and shows neither a spending block nor a spending transactionmezcal-prevout
The outpoint carries a balance of the claimed asset idticker
The claimed amount, converted to base units with the asset's own divisibility, equals the indexed balance exactly and is greater than zeroasset-balance

An unreachable or unreadable authority returns an unknown verdict, which fails the operation closed. It never falls back to a public indexer: Mezcal ownership decisions in Core must come from Universe-operated infrastructure, and a configuration that points at a retired public authority is refused at startup.

The buy payload is derived, never accepted

The OP_RETURN in a purchase transaction decides where every purchased balance lands, so Core owns it. The server derives the only acceptable instruction from what the listing verification just proved, and compares the client's bytes against it:

{"p":"https://mezcal.sh","edicts":[[ticker,baseUnits,0]]}
  • ticker is the asset id the authority proved for the listing, not the one the client sent.
  • baseUnits is the exact verified balance total of the listings in the purchase, as a decimal string.
  • 0 is the buyer's receipt output, which the builder writes first.
  • The comparison is byte exact, including length. A different ticker, a different amount, a different destination index, an extra field, or any additional operation is refused and no transaction is built.
  • One purchase may carry one asset. A single edict cannot allocate two, so a request mixing tickers is refused with its own reason rather than misallocating silently.

Why this matters beyond Core. Any service that co-signs or assembles a transaction containing a client-supplied Mezcal payload is delegating the destination of the assets to that client. Derive the payload from what you have proven, compare it byte for byte, and refuse anything else.

Fee inputs

Every candidate fee input is classified against the Mezcal authority before it can be spent. An output with any Mezcal balance is skipped, and an authority that cannot answer stops the build with an explicit message instead of risking the balance.

Size and fee notes

A Mezcal payload is an ordinary OP_RETURN output. It carries zero satoshis and costs only its bytes. A payload of n bytes produces a script of n + 2 bytes below 76 bytes of payload, and n + 3 bytes from 76 to 255. The whole output serializes to the script plus 8 bytes of value plus the script-length prefix.

Cost of the instruction output alone, for the examples used in this documentation.
PayloadJSON bytesOutput bytesAt 5 sat/vBAt 20 sat/vB
Mint3344220 sats880 sats
One-edict transfer6576380 sats1520 sats
Transfer with a pointer7789445 sats1780 sats
Etching with terms186198990 sats3960 sats

The rest of the transaction is priced normally: inputs, recipient outputs, and change. Practical ways to keep the payload small:

  • Use "mezcal" for p rather than the URL form when your counterparties accept it. It saves 11 bytes.
  • Use an amount of "0" to move an entire balance instead of writing out a long base-unit figure.
  • Prefer the pointer over a second edict when all you need is a change destination.
  • Batch: one payload can carry many edicts, and one transaction can serve many recipients.

Payload size is also bounded by the relay policy of the nodes you depend on. That policy is a node setting, it has changed across Bitcoin Core releases, and it is not part of the Mezcal protocol. The upstream project describes the protocol as leaning on a relaxed OP_RETURN size policy to afford features such as priced mints, so a large etching payload depends on the nodes you actually broadcast through. Confirm the limit there before designing one.

Limitations

  • Failures are silent. A closed mint, an underpaid price, a duplicate name, and an edict for an unknown asset all leave a valid Bitcoin transaction that simply does nothing.
  • Cenotaphs burn. An invalid payload does not leave balances untouched; it destroys the ones on the inputs.
  • No implicit change. Every unallocated unit lands on one output, chosen by the pointer or by position.
  • Names are first-come. A losing etching is ignored, not rejected, so a wallet must check the result after confirmation.
  • The output index equal to the output count is a cenotaph. The reference allocator carries a Runes-style split branch for that index, but the decoder rejects such a payload first, so the branch cannot be reached at the pinned revision.
  • "pointer": 0 is treated as absent at the pinned revision, because the implementation tests the value for truthiness.
  • No mempool coverage. Pending state is not part of what a Universe Mezcal source publishes.
  • Divisibility is capped at 18 by the payload schema, even though the upstream prose describes a wider range.

Security considerations

  • The payload is unauthenticated text. Anyone can write any JSON into an OP_RETURN. An instruction only has power over units the transaction actually spends, so authority comes from the inputs, never from the payload.
  • Never accept a payload from a client you are signing for. Derive it from what you have proven and compare byte for byte, as the Core buy path does.
  • Screen fee inputs. Spending an asset-bearing output for its satoshis moves the asset too, and the remainder rule decides where it goes.
  • Beware display units. A payload written in display units instead of base units is valid and moves a thousandth or a hundred-millionth of what was intended, with the rest going to the pointer.
  • Confirm before you rely. A transfer that is unconfirmed can be replaced or reorganized away. Wait for confirmations in proportion to the value at stake.
  • Trust one authority, not any authority. An ownership answer from a public indexer is a convenience, not a proof. Core requires Universe-operated infrastructure for Mezcal ownership decisions and refuses to start when it is pointed at a retired public authority.
  • Report protocol issues upstream. A defect in the protocol or its reference implementation belongs to bitapeslabs/mezcal. A defect in this documentation belongs here; see SECURITY.md.

Implementation checklist

For an indexer that intends to agree with the reference implementation:

  1. Start at block 898750 and create the genesis asset 1:0 from the coinbase transaction of that block.
  2. Take the mezcalstone from the first nulldata output only; concatenate every push-data chunk; decode UTF-8; parse JSON.
  3. Validate strictly: unknown keys, wrong types, and out-of-range values all produce a cenotaph.
  4. Treat every amount as a string holding a u128 integer. Never parse one into a floating-point number.
  5. Process transactions in block order and index order; within a transaction, etch, then mint, then edicts, then the remainder.
  6. Rewrite the edict id 0:0 to the current transaction's own block:tx.
  7. Clamp every edict to the remaining pool; treat an amount of "0" as the whole remainder.
  8. Send the remainder to the pointer output when it resolves, otherwise to the first non-OP_RETURN output; if there is none, burn it.
  9. Burn everything on the inputs when the transaction is a cenotaph, and everything allocated to an OP_RETURN output otherwise.
  10. Record events with the block hash, and be able to rewind them when that block hash is replaced.
  11. Check your implementation against the test vectors before trusting it with balances.