Skip to content

Build a Charms app

In this tutorial you’ll install the Charms tools, scaffold an app, build it, and check a spell against it — entirely on your machine. By the end you’ll have a working app and you’ll have seen its contract accept a mint.

Install the prerequisites and the CLI (details in Install Charms):

Terminal window
rustup target add wasm32-wasip1 # WebAssembly target
brew install protobuf # Protocol Buffers compiler (macOS)
cargo install charms --locked

Check it works:

Terminal window
charms spell vk
# { "prover": false, "version": 15, "vk": "0042…ae52" }
Terminal window
charms app new my-token
cd ./my-token
cargo update

This scaffolds a Git-initialized project. The interesting file is src/lib.rs — your app contract. The template implements a fungible token managed by a reference NFT: the NFT’s state holds the remaining supply, and whoever controls the NFT can mint tokens by reducing it. Read Write an app contract for a walkthrough of the code.

Terminal window
app_bin=$(charms app build)
charms app vk $app_bin

charms app build compiles the app to WebAssembly and prints the path to the .wasm file. charms app vk prints the app’s verification key — a 32-byte hex value that identifies your app’s code, something like:

8e877d70518a5b28f5221e70bd7ff7692a603f3a26d7076a5253e21c304a354f

The project ships example spells under spells/. Let’s check the NFT mint locally — no network, no proving, just running the contract. Set the environment variables the spell template uses. The app’s identity (app_id) is the SHA-256 of the UTXO you’re “spending”:

Terminal window
export app_vk=$(charms app vk $app_bin)
# the UTXO whose id seeds this NFT (any value works for a local check)
export in_utxo_0="d8fa4cdade7ac3dff64047dc73b58591ebe638579881b200d4fea68fc84521f0:0"
export app_id=$(echo -n "${in_utxo_0}" | sha256sum | cut -d' ' -f1)
export dest_0=$(charms util dest --addr tb1p3w06fgh64axkj3uphn4t258ehweccm367vkdhkvz8qzdagjctm8qaw2xyv)
export amount_0=20000
# the raw transaction that produced in_utxo_0 (a prerequisite for checking)
prev_txs=02000000000101a3a4c09a03f771e863517b8169ad6c08784d419e6421015e8c360db5231871eb0200000000fdffffff024331070000000000160014555a971f96c15bd5ef181a140138e3d3c960d6e1204e0000000000002251207c4bb238ab772a2000906f3958ca5f15d3a80d563f17eb4123c5b7c135b128dc0140e3d5a2a8c658ea8a47de425f1d45e429fbd84e68d9f3c7ff9cd36f1968260fa558fe15c39ac2c0096fe076b707625e1ae129e642a53081b177294251b002ddf600000000

Now run the check, passing the app binary, the prerequisite transaction, and the private inputs (the witness — here, the minting UTXO):

Terminal window
cat ./spells/mint-nft.yaml | envsubst | charms spell check \
--prev-txs=${prev_txs} \
--app-bins=${app_bin} \
--private-inputs=<(cat ./spells/mint-nft-private.yaml | envsubst)

If all is well, you’ll see that the NFT-mint contract is satisfied, along with the cycles the contract used. 🎉

Keep the same app_id as you explore the other example spells (mint-token.yaml, send.yaml): you create an app_id when you mint the NFT, then reuse it for the lifetime of that NFT and its associated tokens.

You’ve built an app and checked a spell locally. Next, put a spell on a real blockchain: Cast your first spell.