Beam charms across chains
Beaming moves a charm from one chain to another by destroying it at a source UTXO and re-creating it at a destination UTXO, tied together by a hash commitment and a finality proof. This guide walks the flow using eBTC — a token that can live on both Bitcoin and Cardano. Read Beaming first for the concepts.
A beam is always a pair of spells: a send on the source chain and a receive on the destination chain.
1. Send (on the source chain)
Section titled “1. Send (on the source chain)”The send spell marks an output as beamed out: it carries no token, only a
hash committing to the destination UTXO. In beamed_outs, the value is
SHA-256(destination_utxo_id ‖ nonce):
version: 15tx: ins: - ${in_utxo_0} # the eBTC you're sending outs: - 0: ${send_amount} # output 0 nominally holds the token... beamed_outs: 0: 7a0fae5c… # ...but it's beamed out: hash of the destination UTXO (+ nonce) coins: - amount: 300 # a dust carrier output on the source chain dest: ${dest_1}app_public_inputs: t/${app_id}/${app_vk}: ~Prove it on the source chain. On Bitcoin:
cat ./spells/beam-send.yaml | envsubst | charms spell prove \ --prev-txs=$prev_tx \ --change-address=$change_addressOn Cardano the send is --chain=cardano and needs a collateral UTXO:
cat ./spells/beam-send-c2b.yaml | envsubst | charms spell prove \ --chain=cardano \ --prev-txs=$prev_tx \ --collateral-utxo=$collateralSign and broadcast the resulting transaction as usual. Note the destination UTXO id and the nonce — you need them to receive.
2. Receive (on the destination chain)
Section titled “2. Receive (on the destination chain)”The receive spell spends the destination (placeholder) UTXO you committed to,
re-materializing the charm. You authorize the claim with --beamed-from, mapping
the input index to [source_utxo, nonce], and you pass the source
transaction together with its finality proof in --prev-txs.
cat ./spells/beam-receive.yaml | envsubst | charms spell prove \ --prev-txs="$placeholder_tx" \ --prev-txs="$source_tx_with_finality_proof" \ --beamed-from '{0: [<source_utxo>, <nonce>]}' \ --change-address=$change_address \ --chain=cardano --collateral-utxo=$collateral # when receiving on CardanoThe finality-proof form of --prev-txs depends on the source chain:
-
Source = Bitcoin → a proof of work:
--prev-txs="!bitcoin {tx: <hex>, proof: <merkleblock hex>, headers: [<hdr>, …]}"(about six block headers of accumulated work).
-
Source = Cardano → a Scrolls finality signature:
--prev-txs="!cardano {tx: <hex>, signature: <ed25519 hex>}"
The prover checks that the destination UTXO (plus nonce) hashes to the value the
source committed in beamed_outs, and that the source transaction is proven
final — otherwise it refuses to prove. If it succeeds, sign and broadcast the
destination transaction.
- A pure beam (just moving the token) is a simple transfer and needs no app
binary. If the receive also mints/burns — as eBTC does when releasing BTC
from its vault — pass
--app-binsso the contract runs. - The hash commitment binds a specific destination UTXO, so only its holder can claim the beamed charm. Keep the nonce.
- See the eBTC scripts (
beam-send.sh,beam-receive.sh, and the*-c2bcounterparts) for complete, runnable examples in both directions.