MezcalProtocol documentation

Normative specification

Mezcal specification

Every rule below is drawn from the reference implementation at bitapeslabs/mezcal, revision 0f3323ff, which is the revision the Bitcoin Universe Mezcal indexer pins as its state schema. Where the deployed behaviour differs from the prose of the upstream README, the code decides and the difference is called out.

Reading the rule ids. C is carrier, P is payload grammar, I is identifiers, U is units, E is etching, M is minting, A is allocation, R is remainder, B is burning, X is cenotaph, O is ordering. Ids are stable; the test vectors and the validator refer to them.

1. Transaction carrier

A Mezcal instruction, called a mezcalstone, travels in one OP_RETURN output of an ordinary Bitcoin transaction. There is no commitment transaction, no witness envelope, and no inscription.

Anatomy of the OP_RETURN output that carries a mezcalstone The output script begins with the OP_RETURN opcode 0x6a, followed by one or more push-data chunks. The chunks are concatenated into a single buffer, decoded as UTF-8, and parsed as a JSON object whose keys are p, edicts, etching, mint, and pointer. scriptPubKey of the first nulldata output 0x6a OP_RETURN push len 1 to 5 bytes 7b 22 70 22 3a 22 68 74 74 70 73 3a ... payload chunk, UTF-8 bytes of the JSON text further chunks concatenated in order Parsed object, validated strictly { "p": required, "edicts"?, "etching"?, "mint"?, "pointer"? } Any other key, any type error, or malformed JSON makes the whole transaction a cenotaph.
The carrier is deliberately dull: an OP_RETURN, push data, UTF-8 JSON. Anyone with a block explorer can read a Mezcal instruction without a decoder.
  1. C-1

    Outputs are examined in index order. The mezcalstone is taken from the first output whose scriptPubKey.type is nulldata, or whose disassembly begins with OP_RETURN. Later OP_RETURN outputs are ignored.

  2. C-2

    If the transaction has no such output, it carries no mezcalstone. It is not a cenotaph, and Mezcal balances on its inputs move by rule R-1.

  3. C-3

    The selected script is decompiled. The transaction is a cenotaph if the script cannot be decompiled, does not begin with OP_RETURN, or carries no push-data after OP_RETURN.

  4. C-4

    Every push-data chunk after OP_RETURN is concatenated, in script order, into a single payload buffer. Non push-data opcodes among them are skipped.

  5. C-5

    A payload buffer shorter than one byte is ignored. The transaction carries no mezcalstone and is not a cenotaph.

  6. C-6

    The payload buffer is decoded as UTF-8 and parsed as JSON. A parse failure makes the transaction a cenotaph.

2. Payload grammar

  1. P-1

    The parsed value must be a JSON object. An array, string, number, boolean, or null is a cenotaph.

  2. P-2

    Validation is strict. The object may contain only the keys p, edicts, etching, mint, and pointer. Any other key, at the top level, is a cenotaph.

  3. P-3

    p is required and must be exactly one of three literal strings:

    "mezcal"
    "https://mezcal.sh"
    "https://t.me/mezcalbtc"

    Any other value, including a trailing slash or a different case, is a cenotaph.

  4. P-4

    edicts, when present, is an array. Each element is either the tuple [id, amount, output] or the object {"id":…,"amount":…,"output":…}. The two forms are equivalent; the tuple is what wallets and marketplaces in practice emit.

  5. P-5

    etching, when present, is an object (section 5). mint, when present, is a string (section 6). pointer, when present, is a JSON number that is an integer in 0 … 4294967295.

  6. P-6

    An empty object with only p is valid. It carries no operation, and balances on the inputs move by rule R-1.

3. Identifiers

  1. I-1

    An asset is identified by block:tx: the height of the block that contains the etching, and the position of the etching transaction inside that block, counting from zero. Both parts are decimal, with no padding and no sign. The string form must match ^\d+:\d+$.

  2. I-2

    In a mint field, both parts must additionally be integers in 0 … 4294967295.

  3. I-3

    The first asset, etched from the coinbase transaction of the genesis block 898750, has the reserved id 1:0.

  4. I-4

    Inside an edict, the id 0:0 is a self-reference: it is replaced with the id of the asset etched by the same transaction, so a single transaction can etch an asset and allocate its premine.

  5. I-5

    An edict whose id has block 0 and a non-zero tx part is a cenotaph. 0:0 is the only permitted block-zero id.

  6. I-6

    An asset name is separate from its id. Names are unique across the protocol; ids are unique by construction. Marketplaces and indexers in Bitcoin Universe key on the id, not the name.

4. Amounts and base units

  1. U-1

    Every protocol amount (amount, cap, premine, and the amount inside an edict) is a JSON string holding a decimal integer. A JSON number is a type error and therefore a cenotaph.

  2. U-2

    The string must parse as an integer in 0 … 2^128 - 1. An empty string, a sign, a decimal point, exponent notation, or whitespace fails.

  3. U-3

    Amounts are in base units. A display amount is baseUnits / 10^divisibility. With divisibility: 8, the display amount 77 is written "7700000000".

  4. U-4

    Price amounts inside mint terms are satoshis, and are a JSON number (a numeric string that fits is accepted and converted). The value must be an integer in 0 … 2100000000000000.

The most common payload defect is a display amount. Writing "77" where "7700000000" is meant does not fail validation: it is a perfectly valid edict that moves 0.00000077 of the asset and leaves the rest to the pointer. The validator converts both ways so the mistake is visible before broadcast.

5. Etching

An etching creates an asset. It is the etching object inside the mezcalstone, and its id is fixed by the position of the transaction that carries it.

"etching": {
  "mezcal": "taco",
  "symbol": "🌮",
  "divisibility": 8,
  "premine": "0",
  "terms": {
    "amount": "10000000000",
    "cap": "21000",
    "height": [898750, null],
    "offset": [null, null]
  },
  "turbo": true
}
  1. E-1

    Required keys: mezcal, symbol, divisibility, premine, and terms. terms may be null, but the key itself must be present. turbo is optional and defaults to true.

  2. E-2

    mezcal is the name: 1 to 15 characters matching ^[a-z0-9-]+$. Lowercase letters, digits, and the hyphen only.

  3. E-3

    symbol is exactly one visible character or emoji, counted in code points. Zero or two code points is a type error and therefore a cenotaph. An indexer that cannot store the given character substitutes the currency sign ¤.

  4. E-4

    divisibility is an integer in 0 … 18. It fixes the base-unit scale for the asset forever.

  5. E-5

    premine is a base-unit amount created immediately. It enters the transaction's unallocated pool, so it is distributed by the edicts and pointer of the same transaction.

  6. E-6

    An etching is ignored, without making the transaction a cenotaph, when an asset already exists at the same block:tx id, or when the name is already taken. The first etching of a name wins; a later one has no effect.

  7. E-7

    terms, when not null, requires amount (base units per mint), height, and offset. cap is optional and may be null. price is optional.

    height and offset are two-element arrays; each element is either null or an integer in 0 … 4294967295. height is an absolute block range; offset is a range relative to the etching block.

  8. E-8

    price, when present, is one object or an array of objects, each {"amount": satoshis, "pay_to": address}. pay_to must decode as a Bitcoin mainnet address. Every price term must be satisfied for a mint to count.

  9. E-9

    Flex mint. Setting terms.amount to "0" with exactly one price term that has a pay_to puts the asset in flex mode: each mint produces as many units as the payment buys. See M-8.

  10. E-10

    An etching is ignored when it is malformed in one of these ways:

    • terms.amount is "0" without a price term (flex mode was intended but not configured);
    • flex mode combined with a cap;
    • flex mode with zero or more than one price term.
  11. E-11

    An etching with "terms": null creates a valid asset that can never be minted. Its whole supply is its premine.

  12. E-12

    An etching cannot be edited. Name, symbol, divisibility, and mint terms are fixed at the etching transaction.

6. Minting

A mint is the string "block:tx" naming the asset to mint. One transaction mints at most once.

  1. M-1

    If no asset exists at that id, the mint does nothing. The transaction is not a cenotaph.

  2. M-2

    An asset marked unmintable cannot be minted. An asset is unmintable when its etching had no mint amount and was not in flex mode, or when its etching transaction was a cenotaph.

  3. M-3

    A transaction cannot mint the asset it etches. A mint whose block and transaction index equal the etching's is closed.

  4. M-4

    When the asset has a cap, the mint is closed once the completed mint count would exceed the cap. The cap counts mint transactions, not units.

  5. M-5

    The open window is derived from height and offset. Offsets are added to the etching block. When both a height bound and an offset bound are given for a side, the start takes the greater of the two and the end takes the lesser. A missing start defaults to the etching block; a missing end means no end. The mint is open when start <= block <= end.

  6. M-6

    When the asset has price terms, the mint transaction must pay each term: the sum of its outputs paying that term's pay_to address must be at least that term's amount in satoshis. A shortfall on any term means no mint, silently.

  7. M-7

    A fixed mint produces exactly terms.amount base units.

  8. M-8

    A flex mint produces floor(total satoshis paid to pay_to / price amount) base units, using the single price term. Payment above an exact multiple buys nothing extra and is not refunded.

  9. M-9

    A computed mint amount of zero or less produces nothing and does not consume a mint from the cap.

  10. M-10

    Minted units enter the transaction's unallocated pool. They are then distributed by the edicts and pointer of the same transaction, exactly like balances arriving on inputs.

Nothing about a mint is rejected loudly. A closed window, an exceeded cap, an unpaid price, or an unknown id all leave a perfectly valid Bitcoin transaction that simply mints nothing. Check the terms before paying.

7. Edicts and allocation

Balances arriving on the inputs, plus a premine, plus a mint, form one unallocated pool per asset id. Edicts move units out of that pool to specific outputs, in the order they appear.

How units flow from the unallocated pool to outputs Input balances, premine, and minted units enter one unallocated pool per asset id. Each edict in order moves an amount from the pool to a named output, clamped to what remains. After the last edict, everything still unallocated goes to the pointer output, or to the first non OP_RETURN output when no usable pointer is given. Input balances Premine (E-5) Minted units (M-10) Unallocated pool one balance per asset id Edicts, in order clamped to the pool Remainder pointer, else first output vout 0: recipient vout 1: change or second party vout 2: OP_RETURN, burns An edict naming the OP_RETURN output is legal, and the units it sends there are destroyed (B-1).
Allocation is one pass over the edicts, then one sweep of the remainder. Nothing is retried and nothing is rolled back.
  1. A-1

    An edict is [id, amount, output]. output is an integer in 0 … 255 and must also be a valid output index of the transaction: output <= vout.length - 1. An index equal to or above the number of outputs makes the transaction a cenotaph (X-7).

  2. A-2

    Edicts are applied in array order. Later edicts see the pool as earlier edicts left it.

  3. A-3

    An edict naming an asset that does not exist is skipped.

  4. A-4

    An edict naming an asset with nothing left in the pool is skipped.

  5. A-5

    An amount of "0" allocates everything remaining in the pool for that asset.

  6. A-6

    An amount larger than what remains is reduced to what remains. An edict never overdraws and never fails the transaction.

  7. A-7

    An allocation of zero units emits no transfer event, so a zero allocation is invisible in indexed activity.

  8. A-8

    An edict may name the OP_RETURN output. The allocation succeeds and the units are burnt (B-1).

  9. A-9

    The reference allocator also contains the Runes-style rule for output equal to the number of outputs, splitting the pool across every non-OP_RETURN output. At the pinned revision that branch is unreachable, because such an edict is already rejected by X-7. Do not build payloads that depend on it.

8. Pointer and remainder

  1. R-1

    After the last edict, everything still unallocated, for every asset id, goes to one single output: the output at index pointer when a usable pointer is given, otherwise the first non-OP_RETURN output.

  2. R-2

    A pointer that is not an index of this transaction falls back to the first non-OP_RETURN output. The pointer is not validated against the output count at parse time, so an out-of-range pointer does not make a cenotaph.

  3. R-3

    At the pinned revision, "pointer": 0 is treated as no pointer, because the implementation tests the value for truthiness. The remainder then goes to the first non-OP_RETURN output. On the usual transaction shape those are the same output; do not rely on the distinction.

  4. R-4

    If the transaction has no non-OP_RETURN output at all, the remainder goes to the OP_RETURN output and is burnt.

  5. R-5

    There is no implicit change output. Every unit that is not allocated by an edict lands on exactly one output, which is why a transfer that forgets its change destination can hand a whole balance to a counterparty.

9. Burning

  1. B-1

    Units allocated to an OP_RETURN output, by an edict or by the remainder rule, are burnt. They are recorded as burn activity, not as a transfer.

  2. B-2

    A cenotaph burns every Mezcal balance carried by the transaction's inputs. Nothing in the payload survives to move them.

  3. B-3

    Burning is permanent and reduces circulating supply. Total supply, as recorded at etching and minting, is not rewritten.

10. Cenotaph conditions

A cenotaph is a transaction whose Mezcal instruction is unreadable or invalid. The consequence is uniform and severe: the payload is discarded entirely, and every Mezcal balance on the inputs is burnt.

Every condition that produces a cenotaph, and the rule that states it.
IdConditionRule
X-1The OP_RETURN script cannot be decompiled, or has no push-data after the opcodeC-3
X-2The payload bytes are not valid JSONC-6
X-3The JSON is not an objectP-1
X-4An unknown top-level key is presentP-2
X-5p is missing or is not one of the three accepted literalsP-3
X-6Any field fails its type or range check: a numeric amount, an out-of-range divisibility, a bad name, a two-character symbol, a malformed id, a pay_to that is not a Bitcoin address, and so onU-1, E-2 to E-8
X-7An edict names an output index above the last output indexA-1
X-8An edict id has block 0 with a non-zero tx partI-5

A cenotaph is not a no-op. The Bitcoin transaction confirms normally and the fee is spent. What is lost is every Mezcal balance the transaction was carrying. Validate the payload before signing: that is what the validator is for.

Two things that are not cenotaphs, and are easy to confuse with one: a transaction with no OP_RETURN output at all (C-2), and one whose OP_RETURN carries an empty payload (C-5). Both simply move balances by the remainder rule.

11. Order of evaluation

  1. O-1

    Blocks are processed in height order, and transactions in their index order inside the block. Genesis is block 898750; nothing before it is processed.

  2. O-2

    Within a transaction the order is fixed: gather input balances into the pool, then etch, then mint, then apply edicts, then sweep the remainder, then record burns and write balances.

  3. O-3

    Because the etch runs first, an edict in the same transaction can allocate the premine using the self-reference id 0:0 (I-4).

  4. O-4

    Because the mint runs before the edicts, freshly minted units can be allocated by the same transaction, and a mint with no edicts sends its units to the pointer output.

Worked transactions for each operation are on the guide. Machine-checkable cases, valid and invalid, are on the test vectors page.