Errors
Every SDK error extends MirageError, which carries a machine-readable code property. Subclasses add context specific to the failure.
import {
MirageError,
ApiError,
ContractError,
TransferAbortedError,
TransferTimeoutError,
WhitelistRequiredError,
} from "@mirageprivacy/sdk";
try {
for await (const event of prepared.execute(walletClient)) {
// ...
}
} catch (e) {
if (e instanceof TransferAbortedError) {
// e.escrowAddress is set if the deploy completed before the abort
} else if (e instanceof TransferTimeoutError) {
// Transfer event not observed within pollTimeout (2 minutes by default)
} else if (e instanceof WhitelistRequiredError) {
// e.amountUsd and e.thresholdUsd are present when the API supplies them
} else if (e instanceof MirageError && e.code === "ACCOUNT_CHANGED") {
// The active wallet does not match the quoted sender
} else if (e instanceof ApiError) {
// e.statusCode, e.body
} else if (e instanceof ContractError) {
// e.txHash
}
}Recovering a deployed escrow
TransferAbortedError, TransferTimeoutError, and the ACCOUNT_CHANGED case can all surface after the escrow is already on chain. When they carry an escrowAddress, the funds are recoverable, but only through the TransferSecrets saved at the deploy step: resume the transfer, or cancel it and withdraw. Every branch below assumes that save happened, which is why persisting secrets is the one step in the lifecycle that cannot be skipped.
import { getEscrowStatus, cancelTransfer } from "@mirageprivacy/sdk";
const { bonded, cancellable } = await getEscrowStatus({
escrowAddress: secrets.escrowAddress,
publicClient,
selectorMapping: secrets.selectorMapping,
});
if (cancellable) {
const hash = await cancelTransfer({
escrowAddress: secrets.escrowAddress,
walletClient,
publicClient,
account,
selectorMapping: secrets.selectorMapping,
});
}An escrow stops being cancellable once a node has bonded against it, at which point the transfer is in flight and should be resumed rather than withdrawn.
selectorMapping is required here because the deployed bytecode is obfuscated per deployment, so the withdraw function is not necessarily reachable at its standard selector. It comes from the saved secrets, as does escrowAddress. Without them there is no way to address the escrow and no recovery path.
MissingBlindingScalarError is the narrower loss: the secrets survived but the scalar did not, or the resume is being attempted on a different device. The transfer cannot be resumed, though a cancel and withdraw still works while the escrow is unbonded.
Other codes
INVALID_PARAMS is thrown when neither input form is complete: supply either transfers or all three of tokenAddress, recipientAddress, and amount.
SENDER_REQUIRED means preparation could not determine the sender to commit into the quote. Supply senderAddress or a walletClient.
ACCOUNT_CHANGED covers both the mid-transfer wallet switch and the case where the active wallet does not match the sender the quote was signed for.
WALLET_REQUIRED means an execution method was called with no wallet client available.
INVALID_STAGE means the staged methods were called out of order, such as completing before deploying, starting a second approval sequence while one is in flight, or re-quoting after the quote has been locked by an approval.
INVALID_APPROVAL means the API's execution approval did not match the deployment it was requested for, so the SDK stops rather than submitting a Signal Nomad would reject.