Spells
Spells are the magic that creates charms.
The idea is to add charms-related metadata (spells) to Bitcoin transactions (similar to Runes’ runestones).
Spells are client-side validated, meaning that the users choose to interpret or ignore them. If they choose to interpret them, they can use charms
— similar to Ordinals and Runes interpreted by ord
.
A spell is said to be correct if and only if all of these are true:
- the enhanced (enchanted) transaction is valid (can be included in a block as such)
- it is successfully parsed and interpreted
- makes sense for the transaction (e.g., doesn’t produce more charms than there are outputs)
- carries a valid proof
Correct spells can mint, burn and transfer tokens. The practical effect of a spell is that tokens are considered parts of the enhanced transaction outputs.
Incorrect spells have are ignored.
Double-spending is prevented by using Bitcoin UTXOs: one cannot simply double-spend a Bitcoin UTXO.
How spells look like
Spells create and transform charms via Bitcoin transactions.
A spell is included in a transaction witness spending a Taproot output. It is included in an envelope — a sequence of opcodes OP_FALSE
OP_IF
… OP_ENDIF
(effectively a no-op: it doesn’t affect the semantics of the witness) — inserted at the beginning of the witness. The actual data is put within OP_IF
… OP_ENDIF
brackets.
where:
OP_PUSH "spell"
shows that the envelope contains a spell.OP_PUSH $spell_data
binary data encoding the spell.OP_PUSH $proof_data
binary data encoding the proof of the spell.
Structure of a spell
Here is an example (of a logical structure) of a spell that sends an NFT and a token to some recipients (taken care of by the underlying Bitcoin transaction):
In this example we have:
apps
— a mapping of app key to app spec. The app key is an identifier of the app within the spell (here we have two:$00
— an NFT, and$01
— a token). These keys do not mean anything, but simply identify their apps within the spell. The app spec is very important and is a tuple oftag/id/VK
where:tag
is a single character representing the app type (n
for NFTs,t
for fungible tokens).tag
can be anything, butn
andt
have special semantics (simple transfers of NFTs and tokens don’t need app contract proofs, so the recursive spell proof can be generated faster).id
(in the form of UTXO ID —txid:vout
) is the unique identity of the app. It takes the form of a UTXO ID because any UTXO is guaranteed to be spent at most once. So it’s a great identifier for minting digital assets: the minting transaction spends a UTXO, and then it can never be spent again. Thus, the mint of this asset cannot be repeated.VK
is the verification key hash of the app implementing the logic of the asset: how it can be minted or burned, staked or used.
ins
— a list of UTXOs that are spent by the transaction. Each UTXO has an optionalcharm
field, which is a mapping of app key to some data. For tokens, the data is an unsigned integer, for NFTs and other apps — arbitrary data (defined by the app developer). Thecharm
field is optional because for each input we can only spend whatever charm it has (as created by the spell of the transaction that produced the output we’re spending).outs
— a list of charms that created by this spell. Each charm is a mapping of app key to some data. Data types are the same as for theins
field.