Mutation hooks
Transaction-producing hooks handle execution, proving, and submission. In React SDK 0.17.0, locking is not uniform across hooks: useMultiSend and useImportAccount do not use the provider's shared concurrency lock. Await these operations before starting other client operations.
These examples assume the provider configuration from Setup.
For automatic delivery of private notes, configure its noteTransportUrl setting.
Two result-shape families show up across this page:
Transaction-producing hooks (useSend, useMultiSend, useMint, useConsume, useSwap, useTransaction):
{
[action]: (options) => Promise<Result>; // send, sendMany, mint, ...
result: Result | null;
isLoading: boolean;
stage: TransactionStage;
error: Error | null;
reset: () => void;
}
Account-creation hooks (useCreateWallet, useCreateFaucet, useImportAccount) don't go through the prove/submit pipeline, so they expose isCreating / isImporting instead of isLoading + stage:
{
createWallet: (opts?) => Promise<Account>;
wallet: Account | null;
isCreating: boolean; // or `isImporting` for useImportAccount
error: Error | null;
reset: () => void;
}
Polling helpers (useWaitForCommit, useWaitForNotes) are simpler still — they expose just the action. Per-hook exact shapes are called out below.
See setup for the TransactionStage progression.
A successful result or stage: "complete" does not generally mean the transaction has been confirmed onchain. Use useWaitForCommit when confirmation is required. Private sends through useSend (without returnNote) and useMultiSend wait for confirmation before delivering notes through the transport.
useCreateWallet
Creates a new wallet account. Returns the Account object.
With the 0.17.0 packages, pass the low-level authentication enum explicitly.
The React hooks' default refers to an enum member missing from the high-level
SDK export, causing invalid enum value passed. Obtain the numeric enum from
getWasmOrThrow() as shown below for wallets, faucets, and seed imports.
import { useCreateWallet } from "@miden-sdk/react";
import { getWasmOrThrow } from "@miden-sdk/miden-sdk/lazy";
function NewWalletButton() {
const { createWallet, wallet, isCreating, error } = useCreateWallet();
const handleCreate = async () => {
const { AuthScheme } = await getWasmOrThrow();
const account = await createWallet({
storageMode: "private", // "private" | "public" (default private)
authScheme: AuthScheme.AuthRpoFalcon512,
});
console.log("Created:", account.bech32id());
};
return (
<button onClick={handleCreate} disabled={isCreating}>Create wallet</button>
);
}
CreateWalletOptions (all optional):
| Field | Default | Description |
|---|---|---|
storageMode | "private" | "private" / "public" |
authScheme | Pass explicitly in 0.17.0 | Numeric WASM signing scheme |
initSeed | random | 32-byte seed for deterministic account-ID derivation |
useCreateFaucet
Creates a fungible-token faucet.
import { useCreateFaucet } from "@miden-sdk/react";
import { getWasmOrThrow } from "@miden-sdk/miden-sdk/lazy";
function NewFaucetButton() {
const { createFaucet, faucet, isCreating } = useCreateFaucet();
const handleCreate = async () => {
const { AuthScheme } = await getWasmOrThrow();
const created = await createFaucet({
tokenSymbol: "TEST",
decimals: 8, // default 8
maxSupply: 10_000_000n, // number | bigint
storageMode: "public", // default "private"; public allows FPI reads
authScheme: AuthScheme.AuthRpoFalcon512,
});
console.log("Faucet:", created.bech32id());
};
return (
<button onClick={handleCreate} disabled={isCreating}>Create faucet</button>
);
}
CreateFaucetOptions:
| Field | Default | Description |
|---|---|---|
tokenSymbol | required | Display symbol (e.g. "USDC") |
tokenName | tokenSymbol | Human-readable token name |
maxSupply | required | bigint | number |
decimals | 8 | Token decimals |
storageMode | "private" | Public faucets are discoverable/readable onchain |
authScheme | Pass explicitly in 0.17.0 | Numeric WASM signing scheme |
useSend
Sends tokens from one account to another.
import { useSend } from "@miden-sdk/react";
function SendForm({ from, to, usdcFaucetId }: Props) {
const { send, isLoading, stage, error } = useSend();
const handleSend = async () => {
const { txId, note } = await send({
from,
to,
assetId: usdcFaucetId,
amount: 100n,
noteType: "private", // default "private"
});
console.log("Transaction:", txId);
};
return (
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? `${stage}…` : "Send"}
</button>
);
}
SendOptions:
| Field | Required | Description |
|---|---|---|
from | yes | Sender AccountRef |
to | yes | Recipient AccountRef |
assetId | yes | Token faucet AccountRef |
amount | unless sendAll | bigint | number |
noteType | — | "private" / "public" (default "private") |
recallHeight | — | Block height after which sender can reclaim the note |
timelockHeight | — | Block height after which recipient can consume the note |
attachment | — | bigint[] | Uint8Array | number[] — payload attached to the note |
skipSync | — | Skip the pre-send auto-sync (default false) |
sendAll | — | Drain full balance of assetId — when true, amount is ignored |
returnNote | — | Return the Note object in the result (for out-of-band delivery, QR codes, etc.) |
SendResult: { txId: string; note: Note | null }. note is non-null only when returnNote: true.
In 0.17.0, returnNote: true creates a plain P2ID note and returns it for the application to deliver; it does not send it through the note transport. This path ignores attachment, recallHeight, and timelockHeight, so omit those options when requesting the note object. Combining an attachment with either height option is rejected, regardless of returnNote.
useMultiSend
Batches multiple recipients into one transaction. All outputs must share the same sender and asset. The action function is named sendMany.
import { useMultiSend } from "@miden-sdk/react";
const { sendMany, isLoading } = useMultiSend();
await sendMany({
from: senderAccountId,
assetId: faucetId,
recipients: [
{ to: recipient1, amount: 500n, noteType: "private" },
{ to: recipient2, amount: 300n },
],
noteType: "private", // default for recipients that don't override
});
MultiSendOptions:
| Field | Description |
|---|---|
from, assetId | Single sender + single faucet for the whole batch |
recipients | MultiSendRecipient[] — each { to, amount, noteType?, attachment? } |
noteType | Default for recipients that don't specify one (default "private") |
skipSync | Skip pre-send auto-sync |
useMint
Mints tokens from a faucet you control into a recipient account.
import { useMint } from "@miden-sdk/react";
const { mint, isLoading, stage } = useMint();
const result = await mint({
targetAccountId: recipient,
faucetId: myFaucet,
amount: 10_000n,
noteType: "private", // default "private"
});
console.log("Mint tx:", result.transactionId);
MintOptions:
| Field | Description |
|---|---|
targetAccountId | Recipient AccountRef |
faucetId | Faucet AccountRef (must be owned by the caller) |
amount | bigint | number |
noteType | "private" / "public" (default "private") |
Returns TransactionResult: { transactionId: string }.
useConsume
Claims one or more notes into an account.
import { useConsume } from "@miden-sdk/react";
const { consume, isLoading } = useConsume();
await consume({
accountId: myAccountId,
notes: [noteIdHex1, noteIdHex2], // hex IDs, NoteId objects, InputNoteRecord, or Note
});
ConsumeOptions:
| Field | Description |
|---|---|
accountId | Account consuming the notes |
notes | (string | NoteId | InputNoteRecord | Note)[] — mix-and-match accepted |
useSwap
Atomic swap between two assets.
import { useSwap } from "@miden-sdk/react";
const { swap, isLoading } = useSwap();
await swap({
accountId: myWallet,
offeredFaucetId: usdcFaucet,
offeredAmount: 100n,
requestedFaucetId: dagFaucet,
requestedAmount: 200n,
noteType: "public", // default "private"
paybackNoteType: "private", // default "private"
});
useImportAccount
Imports an account by ID (fetches from network), by previously-exported file, or by seed.
import { useImportAccount } from "@miden-sdk/react";
import { getWasmOrThrow } from "@miden-sdk/miden-sdk/lazy";
const { importAccount, account, isImporting, error } = useImportAccount();
// By ID — public accounts only
const imported = await importAccount({
type: "id",
accountId: "mtst1qy35...",
});
// By seed — public accounts only
const { AuthScheme } = await getWasmOrThrow();
await importAccount({
type: "seed",
seed: initSeed, // Uint8Array
authScheme: AuthScheme.AuthRpoFalcon512,
});
// By file — works for both public and private accounts
await importAccount({
type: "file",
file: accountFileBytes, // AccountFile | Uint8Array | ArrayBuffer
});
The type discriminant is required. For private accounts, use the "file" variant — private account state isn't reconstructible from a seed alone.
useWaitForCommit / useWaitForNotes
Polling helpers for transaction confirmation and note inbox arrivals. Both are minimal hooks — they only expose the action function.
useWaitForCommit
Signature: waitForCommit(txId, options?) — txId is positional (hex string or TransactionId), options are merged with defaults.
Pass result.txId for useSend. The other transaction-producing hooks listed above return result.transactionId.
import { useWaitForCommit } from "@miden-sdk/react";
const { waitForCommit } = useWaitForCommit();
await waitForCommit(result.txId, { // result from useSend
timeoutMs: 30_000, // default 10_000
intervalMs: 1_000, // default 1_000
});
useWaitForNotes
Exposes waitForConsumableNotes(options) and returns the matching ConsumableNoteRecord[] when the threshold is reached.
import { useWaitForNotes } from "@miden-sdk/react";
const { waitForConsumableNotes } = useWaitForNotes();
const notes = await waitForConsumableNotes({
accountId: recipient,
minCount: 1, // default 1
timeoutMs: 30_000,
});
Both reject on timeout — wrap them in try/catch when you want a graceful fallback.