AccountLayer hooks
React-query wrappers over @symmio/trading-core’s AccountLayer slice.
The page is split into two groups:
- Reads — TanStack
useQuery-shaped hooks. Same call = same cached result. Refetch on invalidation. - Writes — TanStack
useMutation-shaped hooks. Triggermutate/mutateAsync, watchisPending/data/error, and let the hook invalidate the affected reads for you.
Every write also has a paired useSimulateXyz — see Simulate then write.
Reads
useUserSubAccounts
Read a user’s subaccounts. Returns react-query’s full UseQueryResult shape.
import { useUserSubAccounts, useWalletAccount } from "@symmio/trading-react";
const { address } = useWalletAccount();
const { data, isLoading, error, refetch } = useUserSubAccounts({ user: address });
if (!address) return <p>Connect a wallet.</p>;
if (isLoading) return <Spinner />;
if (error) return <ErrorView kind={error.kind} message={error.message} />;
return (
<ul>
{data?.map((sub) => (
<li key={sub.accountAddress}>{sub.name}</li>
))}
</ul>
);Parameters
| Name | Type | Default | Notes |
|---|---|---|---|
user | Address? | undefined | EOA whose subaccounts to fetch. The query is disabled while this is undefined, so you can pass the connected address without a manual enabled guard. |
offset | bigint? | 0n | Pagination offset. |
limit | bigint? | 200n | Pagination cap. |
chainId | number? | config default | Optional chain override. |
query | object? | undefined | TanStack Query overrides, including enabled. |
Return type
UseQueryResult<readonly SubAccountDetail[], SymmioRequestError> — every field react-query exposes, with the SDK error type.
useAccountBalanceOf
Read the account’s available balance — deposited collateral idle on the account. For the instant (lowcap) flow this is the trading balance: deposits credit it, instant opens spend it, and withdrawals draw from it. Under the hood: balanceOf(account) on the SYMMIO diamond. See Balance Model.
import { formatEther } from "viem";
import { useAccountBalanceOf } from "@symmio/trading-react";
const { data, isLoading, error } = useAccountBalanceOf({ account });
if (isLoading) return <Spinner />;
if (error) return <ErrorView kind={error.kind} message={error.message} />;
return <span>{data === undefined ? "-" : formatEther(data)}</span>;Parameters
| Name | Type | Default | Notes |
|---|---|---|---|
account | Address? | undefined | Account address passed to balanceOf. The query is disabled while undefined. |
chainId | number? | config default | Optional chain override. |
query | object? | undefined | TanStack Query overrides. |
config | Config? | provider value | Optional config override. |
live | boolean? | false | Subscribe to the account’s on-chain settle notifications and refetch on each open/close settle (a shared WebSocket; requires the provider tree). |
Return type
UseQueryResult<bigint, SymmioRequestError>.
The value is in the protocol’s 18-decimal internal units (not the collateral token’s decimals) — use formatEther / formatUnits(value, 18) to display.
useAccountBalanceInfo
Read the account’s classic-pool (allocated) balance info — collateral allocated to this account, plus the CVA / LF / margin locks currently held against it. Instant opens do not spend this pool — the instant flow trades from the available balance (useAccountBalanceOf); see Balance Model. Under the hood: balanceInfoOfPartyA(account) on the SYMMIO diamond.
import { formatEther } from "viem";
import { useAccountBalanceInfo } from "@symmio/trading-react";
const { data } = useAccountBalanceInfo({ account });
return <span>{data ? formatEther(data.allocatedBalance) : "-"}</span>;Parameters
| Name | Type | Default | Notes |
|---|---|---|---|
account | Address? | undefined | Account address passed as PartyA to balanceInfoOfPartyA. The query is disabled while undefined. |
chainId | number? | config default | Optional chain override. |
query | object? | undefined | TanStack Query overrides. |
config | Config? | provider value | Optional config override. |
live | boolean? | false | Same as useAccountBalanceOf, and it also refetches on any frame whose vaAddress equals account (the account’s Virtual Account just settled). |
Return type
UseQueryResult<AccountBalanceInfo, SymmioRequestError>.
interface AccountBalanceInfo {
allocatedBalance: bigint;
lockedCVA: bigint;
lockedLF: bigint;
lockedPartyAMM: bigint;
lockedPartyBMM: bigint;
pendingLockedCVA: bigint;
pendingLockedLF: bigint;
pendingLockedPartyAMM: bigint;
pendingLockedPartyBMM: bigint;
}All fields are in the protocol’s 18-decimal internal units (not the collateral token’s decimals).
usePredictedNextVirtualAccount
Predict the deterministic address of the Virtual Account (VA) a SubAccount will create for its next (isolationType, symbolId) position — before the trade lands on-chain and the VA exists. In lowcap, an instant open isolates each position into its own VA; the AccountLayer derives that address up front, so you can sign against it early. The main use: attaching TP/SL in the same flow as an instant open (the TP/SL order must be signed against the VA, not the SubAccount). Pair the side with isolationTypeForSide(positionType) to pick the isolation type.
import { isolationTypeForSide, PositionType, usePredictedNextVirtualAccount } from "@symmio/trading-react";
const positionType = side === "long" ? PositionType.LONG : PositionType.SHORT;
const { data: predictedVa } = usePredictedNextVirtualAccount({
subAccount,
isolationType: isolationTypeForSide(positionType), // MARKET_LONG / MARKET_SHORT
symbolId: BigInt(market.symbol_id),
});
// predictedVa → pass as `virtualAccount` to a TP/SL attached at open.Parameters
| Name | Type | Default | Notes |
|---|---|---|---|
subAccount | Address? | undefined | The SubAccount that will open the position. |
isolationType | VirtualAccountIsolationType? | undefined | Isolation bucket for the VA — use isolationTypeForSide(positionType). |
symbolId | bigint? | undefined | Solver market id of the position. |
chainId | number? | config default | Optional chain override. |
query | object? | undefined | TanStack Query overrides. |
config | Config? | provider value | Optional config override. |
The query is disabled until subAccount, isolationType, and symbolId are all set. The prediction is stable per (subAccount, isolationType, symbolId) only until the SubAccount’s virtual nonce advances (i.e. it opens that isolation), so a short staleTime applies — override via query.staleTime.
Return type
UseQueryResult<Address, SymmioRequestError> — the predicted VA address.
Writes
Every hook below returns react-query’s UseMutationResult — call mutate(inputs) (or mutateAsync), watch isPending / data / error, and the hook invalidates the reads it affected on your behalf.
Building the instant (lowcap) flow? You need neither useAllocate nor useDeallocate. Instant trading spends the
available balance; allocating moves funds into the classic pool and reduces what an instant open can use. Fund
with useDeposit, exit with the withdraw
hooks. See Balance Model.
useAllocate
Allocate a subaccount’s available balance into its allocated (tradeable) balance.
The call inputs (account, amount) are passed to mutate; amount is in 18 decimals.
import { useAllocate } from "@symmio/trading-react";
const { mutate, isPending, isSuccess, data, error } = useAllocate();
<button onClick={() => mutate({ account: "0xsub...", amount: 1_000000000000000000n })} disabled={isPending}>
{isPending ? "Sending…" : "Allocate"}
</button>;
{
isSuccess && <p>tx: {data.hash}</p>;
}Options
interface UseAllocateParameters {
waitForReceipt?: boolean; // default true — mutation resolves only after the receipt is mined
confirmations?: number; // default 1
}Cache invalidation
On success, the hook invalidates the subaccount’s useAccountBalanceInfo and useAccountBalanceOf queries in the active QueryClient, so allocated and available balances rerender automatically.
Dry run
useSimulateAllocate() runs the same call through simulateContract without sending — useful to surface a would-be revert before prompting the wallet.
useDeallocate
Deallocate a subaccount’s allocated (tradeable) balance back into its available balance — the reverse of useAllocate. The hook fetches a fresh Muon uPnL signature automatically before submitting (the contract requires it to prove the subaccount stays solvent), unless you pass one as upnlSig. amount is in 18 decimals.
import { useDeallocate } from "@symmio/trading-react";
const { mutate, isPending, isSuccess, data, error } = useDeallocate();
<button onClick={() => mutate({ account: "0xsub...", amount: 1_000000000000000000n })} disabled={isPending}>
{isPending ? "Sending…" : "Deallocate"}
</button>;
{
isSuccess && <p>tx: {data.hash}</p>;
}Options
interface UseDeallocateParameters {
waitForReceipt?: boolean; // default true — mutation resolves only after the receipt is mined
confirmations?: number; // default 1
}Cache invalidation
On success, the hook invalidates the subaccount’s useAccountBalanceInfo and useAccountBalanceOf queries in the active QueryClient, so allocated and available balances rerender automatically.
Muon signature & cooldown
The hook calls getDeallocateUpnlSig for you unless you pass a upnlSig variable. Deallocate is subject to the on-chain debounce — submitting too soon after a prior deallocate reverts — and the result must keep the subaccount solvent; both surface as a normalized SymmioRequestError. useSimulateDeallocate() dry-runs the call (supply a upnlSig there — it does not auto-fetch).
useEditAccountName
Submit a rename transaction.
import { useEditAccountName } from "@symmio/trading-react";
const { mutate, isPending, isSuccess, data, error } = useEditAccountName();
<button onClick={() => mutate({ account: "0xsub...", name: "Trading bot" })} disabled={isPending}>
{isPending ? "Sending…" : "Rename"}
</button>;
{
isSuccess && <p>tx: {data.hash}</p>;
}
{
error?.kind === "user-rejected" ? null : error && <p>{error.message}</p>;
}Options
interface UseEditAccountNameOptions {
waitForReceipt?: boolean; // default true — mutation resolves only after the receipt is mined
confirmations?: number; // default 1
}Cache invalidation
On success, the hook invalidates every cached useUserSubAccounts query in the active QueryClient, so mounted lists rerender with the new name automatically. No manual invalidation needed.
Why this hook needs a connected wallet
The hook reads useSymmioWalletClient() under the hood. If the user is disconnected or on the wrong chain, calling mutate throws a SymmioRequestError with kind: "sdk" before any contract call is made. UIs typically gate the button on useWalletAccount().isOnExpectedChain to avoid the error path entirely.