Transactions
What a write hook does with your variables
Every write hook forwards its mutation variables to the core action verbatim. It defaults only
chainId (to the connected chain) and injects the values its own docs name — useDeallocate’s
upnlSig when you omit it, useWithdraw’s account and isolationType. Anything a hook takes over
is removed from its variables type with Omit<>.
That is the contract, and it is what makes the type honest: a field that type-checks is a field that
is honored. So an optional parameter on the core action — gasless, from, simulateBeforeWrite —
reaches the action just by being passed to mutate.
from is worth calling out because it plays three roles depending on the path:
- on a simulation, it is the dry-run
msg.sender(theuseSimulate*hooks default it to the connected address); - on a write, it is a hint handed to
config.getWalletClient({ chainId, from })— write hooks forward it but never default it, so a custom multi-signer resolver keeps whatever default it chose; - on a relayed write, the wallet that resolver returns signs the
InstantLayer operation, so
fromselects the EIP-712 signer. That is separate from the billing sub-account, which comes from the action itself or fromgasless.account.
Transactions store
A small, opt-in zustand store for tracking in-flight tx hashes in the UI — the data backing pending-tx badges, post-confirmation toasts, and “view in explorer” links.
The SDK’s mutation hooks (useEditAccountName, future writes) do not populate this store automatically. You wire it up where it makes sense for your app.
API
interface TrackedTx {
hash: Hash;
chainId: number;
label: string; // human-readable: "Rename account"
status: "pending" | "confirmed" | "failed";
createdAt: number; // Date.now() at insertion
}
interface TransactionsStoreState {
txs: readonly TrackedTx[];
add: (tx: Omit<TrackedTx, "status" | "createdAt">) => void;
markConfirmed: (hash: Hash) => void;
markFailed: (hash: Hash) => void;
clear: () => void;
}Usage
Subscribe
Standard zustand. Pass a selector for fine-grained subscriptions:
import { useTransactionsStore } from "@symmio/trading-react";
function PendingBadge() {
const pendingCount = useTransactionsStore((s) => s.txs.filter((t) => t.status === "pending").length);
if (pendingCount === 0) return null;
return <span className="badge">{pendingCount} pending</span>;
}Track a tx from a mutation
const { mutateAsync } = useEditAccountName({ waitForReceipt: false });
const add = useTransactionsStore((s) => s.add);
const markConfirmed = useTransactionsStore((s) => s.markConfirmed);
const markFailed = useTransactionsStore((s) => s.markFailed);
async function rename() {
const { hash } = await mutateAsync({ account, name });
add({ hash, chainId, label: "Rename account" });
try {
await publicClient.waitForTransactionReceipt({ hash });
markConfirmed(hash);
} catch {
markFailed(hash);
}
}If you’d rather have the hook await the receipt for you and skip the store, set waitForReceipt: true (the default) and ignore this whole section.
Why opt-in
Tx tracking is UI plumbing — every app’s needs differ (toast positioning, persistence, deduplication). The store is small enough that you can replace it with your own zustand or Redux slice without changing the hooks that produce tx hashes.