Skip to main content
Version: 0.16

Advanced hooks

Hooks beyond the core send / mint / consume trio: custom scripts, anchored transaction previews, MASM compilation, session wallets, store backup, note serialization, and sync control.

useTransaction​

General-purpose transaction runner that accepts either a prebuilt TransactionRequest or a builder callback. This is the escape hatch when the higher-level hooks don't cover your flow.

import { useTransaction } from "@miden-sdk/react";
import { TransactionRequestBuilder } from "@miden-sdk/miden-sdk";

const { execute, isLoading, stage } = useTransaction();

// Direct request
await execute({
accountId: contractAccount,
request: prebuiltRequest,
});

// Builder callback — receives the raw WebClient
await execute({
accountId: contractAccount,
request: (_client) =>
new TransactionRequestBuilder()
.withCustomScript(txScript)
.build(),
});

UseTransactionResult exposes execute (not executeTransaction), plus result, isLoading, stage, error, and reset.

ExecuteTransactionOptions:

FieldDescription
accountIdAccount the transaction applies to
requestTransactionRequest or (client: WebClient) => TransactionRequest | Promise<TransactionRequest>
skipSyncSkip pre-send auto-sync (default false)
privateNoteTargetDeliver private output notes to this account after commit (any AccountRef form)
anchorExecute against a reference block captured with useChainAnchor

The privateNoteTarget field is the 4-step pipeline shortcut: execute the tx, commit onchain, then auto-deliver the private note through the note transport to the target. Useful for "send private note" UIs where the recipient already has the React SDK running.

useChainAnchor and usePreview​

Use these hooks when a transaction summary is proposed on one client and authorized or executed on another, such as multisig and offline co-signing flows. useChainAnchor pins the request to one reference block; usePreview derives the summary awaiting authorization at that block.

Capture and preview in separate UI steps. anchoredRequest is React state, so it becomes available on the render after captureAnchor() completes:

import { useState } from "react";
import { useChainAnchor, usePreview, useTransaction } from "@miden-sdk/react";
import type { TransactionRequest, TransactionSummary } from "@miden-sdk/miden-sdk";

type MultisigProposalProps = {
accountId: string;
buildRequest: () => TransactionRequest | Promise<TransactionRequest>;
sendProposal: (anchor: Uint8Array, summary: Uint8Array) => Promise<void>;
collectAuthorization: (
summary: TransactionSummary,
request: TransactionRequest,
) => Promise<TransactionRequest>;
};

function MultisigProposal({
accountId,
buildRequest,
sendProposal,
collectAuthorization,
}: MultisigProposalProps) {
const { captureAnchor, anchor, anchoredRequest, isCapturing } = useChainAnchor();
const { preview, isPreviewing } = usePreview();
const { execute, isLoading } = useTransaction();
const [authorizedRequest, setAuthorizedRequest] =
useState<TransactionRequest | null>(null);
const [isAuthorizing, setIsAuthorizing] = useState(false);
const busy = isCapturing || isPreviewing || isAuthorizing || isLoading;

const capture = async () => {
setAuthorizedRequest(null);
await captureAnchor({ request: buildRequest });
};

const previewAndShare = async () => {
if (!anchor || !anchoredRequest) return;
setAuthorizedRequest(null);
setIsAuthorizing(true);
try {
const summary = await preview({
accountId,
request: anchoredRequest,
anchor,
});
await sendProposal(anchor.serialize(), summary.serialize());
setAuthorizedRequest(await collectAuthorization(summary, anchoredRequest));
} finally {
setIsAuthorizing(false);
}
};

const executeAnchored = async () => {
if (!anchor || !authorizedRequest) return;
await execute({ accountId, request: authorizedRequest, anchor });
setAuthorizedRequest(null);
};

return (
<>
<button onClick={capture} disabled={busy}>
Capture reference block
</button>
<button
onClick={previewAndShare}
disabled={!anchor || !anchoredRequest || busy}
>
Preview and share
</button>
<button
onClick={executeAnchored}
disabled={!anchor || !authorizedRequest || busy}
>
Execute authorized request
</button>
</>
);
}

collectAuthorization is supplied by your application. It collects the required signatures and returns the request with the authorization advice attached. Keep the proposed actions, fee conversion salt, and reference block unchanged. For a multisig account, buildRequest must declare a fresh salt with withFeeConversionSalt. The execute button becomes available after authorization has been collected.

preview() rejects with TRANSACTION_ALREADY_AUTHORIZED when the request needs no additional authorization; execute it directly in that case. A ChainAnchor owns a WASM allocation, so call anchor.free() when the proposal workflow no longer needs it.

useExecuteProgram​

View call — executes a transaction script locally and returns the stack output. No prove, no submit, no state change. Think of it as Miden's eth_call.

import { useExecuteProgram } from "@miden-sdk/react";

const { execute, isLoading, error } = useExecuteProgram();

const result = await execute({
accountId: contractAccount,
script: compiledTxScript,
foreignAccounts: [counterAccount], // optional
});

// result.stack is a bigint[] — read indices directly
const count: bigint = result.stack[0];
console.log("Count:", count);

UseExecuteProgramResult exposes execute (not executeProgram), plus result, isLoading, error, and reset. No stage — view calls don't prove or submit.

The React hook flattens the 16-element stack into a plain bigint[]. useMidenClient() exposes the underlying WASM WebClient directly — its method is client.executeProgram(...) (not namespaced under client.transactions). See the Web SDK transactions guide for the imperative MidenClient.transactions.executeProgram equivalent and the FeltArray shape.

useCompile​

Compiles Miden Assembly into AccountComponent, TransactionScript, or NoteScript. Each result method is independently callable — call only what you need for the current operation.

import { useCompile } from "@miden-sdk/react";
import { StorageSlot } from "@miden-sdk/miden-sdk";

const { component, txScript, noteScript, isReady } = useCompile();

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

// Transaction script (with optional libraries)
const script = await txScript({
code: `
use external_contract::counter_contract

@transaction_script
pub proc main
call.counter_contract::increment_count
end
`,
libraries: [{ component: counterComponent }],
});

// Note script — use the @note_script attribute on a library proc
const attachScript = await noteScript({
code: `
use miden::protocol::active_note
use miden::core::sys

@note_script
pub proc on_consume
# body runs when the consuming account redeems this note
exec.sys::truncate_stack
end
`,
});

UseCompileResult exposes the three compile methods plus isReady. Loading and error state are tracked internally per call — catch errors at the individual await site. See the Web SDK compile guide for the full CompileComponentOptions / CompileTxScriptOptions / CompileNoteScriptOptions shapes.

useSessionAccount​

Drives the "session wallet" pattern — create a throw-away wallet, wait for a funding note, consume it, then hand control back to your app. Useful for one-off interactions that shouldn't touch a long-lived account.

import { useMiden, useSessionAccount } from "@miden-sdk/react";
import { getWasmOrThrow } from "@miden-sdk/miden-sdk/lazy";

// Resolve the numeric authentication enum once, before rendering this module.
const { AuthScheme } = await getWasmOrThrow();

export function SessionWallet({
fund,
}: {
fund: (sessionAccountId: string) => Promise<void>;
}) {
const { isReady: clientReady } = useMiden();
const { initialize, sessionAccountId, isReady, step, error, reset } =
useSessionAccount({
// The callback receives the new wallet's hex ID. Send a funding note
// containing native fee tokens, plus any assets the session needs.
fund,
walletOptions: {
storageMode: "private",
authScheme: AuthScheme.AuthRpoFalcon512,
},
pollIntervalMs: 3_000,
maxWaitMs: 60_000,
});

return (
<>
<button
onClick={() => void initialize().catch(console.error)}
disabled={!clientReady || step !== "idle"}
>
{step === "idle" ? "Start session" : step}
</button>
{isReady && <p>Session ready: {sessionAccountId}</p>}
{error && <p role="alert">{error.message}</p>}
<button onClick={reset}>Reset session</button>
</>
);
}

In 0.16.0, pass the raw numeric authentication enum explicitly, as above; the hook's default resolves to an undefined enum member. The fund callback belongs to your application. Its note must include the native fee asset so the new wallet can pay for its first consume transaction.

The flow progresses through idle → creating → funding → consuming → ready.

UseSessionAccountReturn:

FieldDescription
initialize()Kicks off the create → fund → consume flow
sessionAccountIdHex ID of the session wallet once created
isReadytrue after the funding note has been consumed
stepSessionAccountStep — one of the five states above
errorNon-null if any step failed
reset()Clears session data (and any persisted state under storagePrefix)

Session state persists under the configurable storagePrefix (default "miden-session") so page reloads can resume mid-flow.

useExportStore / useImportStore​

Back up and restore the entire local store as a JSON dump. Handy for wallet backup/restore UIs.

import { useExportStore, useImportStore, useMidenClient } from "@miden-sdk/react";

// Export — returns a JSON string
const { exportStore } = useExportStore();
const dump: string = await exportStore();
download(new Blob([dump]), "wallet-backup.json");

// Import (destructive — overwrites the target store)
// Positional: (storeDump, storeName, options?)
const { importStore } = useImportStore();
const client = useMidenClient();
const storeName = await client.storeIdentifier();
await importStore(uploadedDump, storeName, { skipSync: false });

ImportStoreOptions exposes skipSync (default false) so you can defer the post-import sync. There's no second "raw bytes" form — importStore takes the JSON dump string as its first argument and the target store name as its second.

useImportNote / useExportNote​

Serialize notes to bytes for QR delivery or import notes handed over out-of-band. These complement the private-note transport layer — use the transport when the recipient is online, and QR/bytes when they aren't.

In 0.16.0, exportNote requires an output-note ID tracked by this client. An imported input note alone is insufficient and returns No output note found.

import { useExportNote, useImportNote } from "@miden-sdk/react";

const { exportNote } = useExportNote();
const noteBytes = await exportNote(noteId);
// encode noteBytes into a QR, link, email, etc.

const { importNote } = useImportNote();
await importNote(uploadedBytes);

useSyncControl​

Pause and resume the auto-sync loop without dismounting MidenProvider. Useful when a long operation needs consistent local state, or during battery-sensitive background work.

import { useSyncControl } from "@miden-sdk/react";

const { pauseSync, resumeSync, isPaused } = useSyncControl();

// Before a long sequence
pauseSync();
// ... operations that need a stable snapshot ...
resumeSync();

pauseSync() stops the timer but doesn't cancel an in-flight sync — wait for isSyncing from useSyncState() to settle if you need a truly quiescent state.

Next​

  • Signers — wire external wallets (Para, Turnkey, MidenFi) or build a custom signer.
  • Recipes — end-to-end patterns.