Skip to content

SDK

App contracts are written in Rust against the charms-sdk crate, which re-exports the core data types as charms_sdk::data and provides the two macros that wire your contract into a runnable WebAssembly module. A generated app depends on it as:

[dependencies]
charms-sdk = { version = "15.0.0" }

For the patterns you’ll use, see Write an app contract.

use charms_sdk::data::{check, App, Data, Transaction, NFT, TOKEN};
pub fn app_contract(app: &App, tx: &Transaction, x: &Data, w: &Data) -> bool {
// return true iff `tx` is allowed by `app`
true
}
// in src/main.rs:
charms_sdk::main!(my_app::app_contract);

Generates the module’s main: it reads the (App, Transaction, Data, Data) tuple (CBOR) from stdin and asserts that $path(&app, &tx, &x, &w) returns true. This is the convention every app uses to expose its contract.

For versioned apps. Expands to pub const VERSION: u32 = $version; and exports an __app_version() function so the runtime can confirm the running binary matches the version pinned in the spell. Requires Rust edition 2024.

All under charms_sdk::data (re-exported from charms_data).

pub struct App {
pub tag: char, // 'n' NFT, 't' token, 's' Scroll, or any custom tag
pub identity: B32, // 32-byte asset identity
pub vk: B32, // app verification key
}

Displays / parses as the string tag/identity_hex/vk_hex.

pub struct Transaction {
pub ins: Vec<(UtxoId, Charms)>, // inputs and their charms
pub refs: Vec<(UtxoId, Charms)>, // reference inputs and their charms
pub outs: Vec<Charms>, // outputs and their charms
pub coin_ins: Option<Vec<NativeOutput>>,
pub coin_outs: Option<Vec<NativeOutput>>,
pub prev_txs: BTreeMap<TxId, Data>,
pub app_public_inputs: BTreeMap<App, Data>,
}
pub type Charms = BTreeMap<App, Data>; // the app -> data map carried by a UTXO

An opaque CBOR value. Construct it from any Serialize type, and read it back with value::<T>():

let n: u64 = x.value()?; // deserialize the public input
let d: Data = Data::from(&my_struct); // serialize anything
Data::empty(); // the empty value
data.is_empty();
pub struct UtxoId(pub TxId, pub u32); // "txid:index"; 36 bytes via to_bytes()
pub struct TxId(pub [u8; 32]); // hex (Bitcoin-style reversed)
pub struct B32(pub [u8; 32]); // 32-byte value, hex Display/FromStr
pub struct NativeOutput {
pub amount: u64,
pub dest: Vec<u8>,
pub content: Option<Data>,
}
pub const TOKEN: char = 't';
pub const NFT: char = 'n';
pub const SCROLL: char = 's';
check!(condition); // early-return false from a -> bool fn if false
check!(cond, "message"); // with a message printed to stderr

The idiomatic way to assert inside a contract: if the condition fails, the contract returns false (and the failing expression is logged).

pub fn charm_values<'a>(
app: &'a App,
charms: impl Iterator<Item = &'a Charms>,
) -> impl Iterator<Item = &'a Data>;

Yields the Data values for app across the given charms (e.g. all outputs). (Replaces the deprecated app_datas.)

pub fn sum_token_amount<'a>(
app: &App,
charms: impl Iterator<Item = &'a Charms>,
) -> anyhow::Result<u64>;

Sum a fungible token’s amounts across charms. Requires app.tag == TOKEN.

pub fn is_simple_transfer(app: &App, tx: &Transaction) -> bool;
pub fn token_amounts_balanced(app: &App, tx: &Transaction) -> bool;
pub fn nft_state_preserved(app: &App, tx: &Transaction) -> bool;

Useful when a contract wants to allow plain transfers and only enforce extra rules on mint/burn.

Used by the proving/verification layer (rarely by a simple contract):

pub struct VersionedApp { pub version: u32, pub wasm_hash: B32 }
pub struct AppSignature { pub public_key: B32, pub signature: [u8; 64] } // BIP-340

See Manage app keys.