Skip to main content
Version: 0.17 (unstable)

Compile

client.compile turns Miden Assembly (MASM) source into the three runtime artifacts the rest of the SDK consumes:

MethodProducesUsed by
client.compile.component({ code, namespace?, slots?, supportAllTypes? })AccountComponentaccounts.create({ components: [...] })
client.compile.txScript({ code, libraries? })TransactionScripttransactions.execute({ script })
client.compile.noteScript({ code, libraries? })NoteScriptNote construction utilities

Each call spins up a fresh CodeBuilder, so libraries linked in one call never leak into another.

Account components

import { MidenClient, StorageSlot } from "@miden-sdk/miden-sdk";

const client = await MidenClient.createTestnet();

const contractCode = `
use miden::protocol::active_account
use miden::protocol::native_account
use miden::core::word
use miden::core::sys

const COUNTER_SLOT = word("miden::tutorials::counter")

@account_procedure
pub proc get_count
push.COUNTER_SLOT[0..2] exec.active_account::get_item
exec.sys::truncate_stack
end

@account_procedure
pub proc increment_count
push.COUNTER_SLOT[0..2] exec.active_account::get_item
add.1
push.COUNTER_SLOT[0..2] exec.native_account::set_item
exec.sys::truncate_stack
end
`;

const component = await client.compile.component({
code: contractCode,
namespace: "external_contract::counter_contract",
slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
});

// Use the procedure hash when calling this contract via FPI
const getCountHash = component.getProcedureHash("get_count");
console.log("get_count hash:", getCountHash);

Options:

  • code — the MASM source for the component.
  • namespace — module path used to derive procedure identities. Reuse it when rebuilding the source as an inline library; linking { component } preserves the exact compiled identity.
  • slots — initial storage slots. Use the StorageSlot helpers (emptyValue, etc.).
  • supportAllTypes — defaults to true and calls withSupportsAllTypes() for compatibility. In 0.16, components already apply to every account type; this option does not inject an auth-kernel invocation.

Transaction scripts

Without libraries

A script with no libraries entry can only reference procedures that exist in the transaction kernel and the standard library — no custom external contracts:

const script = await client.compile.txScript({
code: `
use miden::core::sys

@transaction_script
pub proc main
push.0
exec.sys::truncate_stack
end
`,
});

If your script needs to call into an external contract (as in the FPI section below), pass either the exact compiled component or its source through libraries — the compiler only links what you explicitly provide.

With inline libraries

import { Linking } from "@miden-sdk/miden-sdk";

const script = await client.compile.txScript({
code: `
use external_contract::my_contract
use miden::core::sys

@transaction_script
pub proc main
call.my_contract::do_something
exec.sys::truncate_stack
end
`,
libraries: [
{
namespace: "external_contract::my_contract",
code: myContractCode,
linking: Linking.Dynamic, // default
},
],
});

Each inline library takes:

FieldRequiredDescription
namespaceyesMASM namespace, e.g. "counter::module".
codeyesMASM source.
linkingnoLinking.Dynamic (default) or Linking.Static. "dynamic" / "static" string literals are also accepted.

libraries also accepts { component, linking? }, which links the exact code installed by an AccountComponent, or a pre-built Library. Prefer the component form when a script calls a component installed on an account.

Linking modes

ValueBehaviourWhen to use
Linking.Dynamic (default)Retains external procedure MAST roots. The execution host must supply the referenced code.Linking account procedures without embedding their implementation.
Linking.StaticIncludes the linked library code in the compiled artifact.Offchain libraries that must be self-contained.

Note scripts

Note scripts run when an account consumes the note. The shape mirrors txScript; use it when you need custom logic on consumption.

const noteScript = await client.compile.noteScript({
code: `
use miden::protocol::active_note
use miden::core::sys

@note_script
pub proc main
# Runs when the consuming account redeems this note.
# Real note scripts inspect note storage, assets, and account state
# using procedures from miden::protocol::active_note.
exec.sys::truncate_stack
end
`,
});

Libraries accept the same inline { namespace, code, linking? }, compiled { component, linking? }, and pre-built Library forms as transaction scripts.

Procedure hashes (for FPI)

Foreign procedure invocation requires the hash of the target procedure. Extract it from a compiled component:

const component = await client.compile.component({
code: counterContractCode,
namespace: "external_contract::counter_contract",
slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
});

const getCountHash = component.getProcedureHash("get_count");

const script = await client.compile.txScript({
code: `
use external_contract::count_reader_contract
use miden::core::sys

@transaction_script
pub proc main
padw padw padw padw
push.${getCountHash}
push.${counterAccountId.prefix()}
push.${counterAccountId.suffix()}
call.count_reader_contract::copy_count
exec.sys::truncate_stack
end
`,
libraries: [
{ namespace: "external_contract::count_reader_contract", code: countReaderCode },
],
});

End-to-end: compile → create contract → execute script

import {
MidenClient,
AuthSecretKey,
StorageSlot,
} from "@miden-sdk/miden-sdk";

const client = await MidenClient.createTestnet();
await client.sync();

// 1. Compile the contract component
const component = await client.compile.component({
code: counterCode,
namespace: "external_contract::counter_contract",
slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
});

// 2. Create the contract account
const seed = crypto.getRandomValues(new Uint8Array(32));
const auth = AuthSecretKey.rpoFalconWithRNG(seed);

const contract = await client.accounts.create({
seed,
auth,
components: [component],
});

await client.sync();

// 3. Compile the transaction script
const script = await client.compile.txScript({
code: `
use external_contract::counter_contract

@transaction_script
pub proc main
call.counter_contract::increment_count
end
`,
// Link the exact component installed on the account so procedure identities match.
libraries: [{ component }],
});

// 4. Execute
const { txId } = await client.transactions.execute({
account: contract.id(),
script,
});

console.log("Tx:", txId.toHex());