42161): make sure the user’s wallet is on Arbitrum before signing, or the venue rejects the signature. The stablecoin swap is the exception — a single step with no prepare/execute and no signature.
Prerequisites: an active VOOI API token, Hyperliquid connected via Register on Hyperliquid or Use your own keys, and access to the private key of the main Hyperliquid wallet that holds your funds. The VOOI-managed agent cannot sign these actions.
Move assets within Hyperliquid
POST /exchange/hyperliquid/send-asset/prepare builds an EIP-712 message your main wallet signs to move tokens within Hyperliquid — between the default USDC perp DEX and a HIP-3 DEX, between your main account and a sub-account, between your spot and perp accounts, or to another user.
For cross-exchange moves (Arbitrum ↔ Hyperliquid ↔ Lighter), use cross-exchange transfers instead.
1. Prepare
The response carries:
signData— EIP-712 typed data (domain,types,primaryType,message) for thesendAssetaction.pendingData— opaque payload to pass back unchanged in step 2.fee— Hyperliquid-side fee, always0forsendAsset.
2. Sign and execute
SignsignData as EIP-712 typed data with the main wallet’s private key, with the wallet on Arbitrum (chain id 42161) so the signature is accepted. The message.nonce is a uint64 — convert numeric values to BigInt if your signer requires it (same pattern as Register on Hyperliquid). Then post pendingData and the signature to the execute endpoint:
Account abstraction mode
The abstraction mode controls how Hyperliquid shares collateral between your spot and perp accounts, and which assets count as collateral for perps. Read it withGET /exchange/account-info?exchange=hyperliquid and change it through the prepare → execute pair below.
VOOI accepts four settable values on set-abstraction. The default value is read-only — it means the user has never selected a mode on Hyperliquid and cannot be passed back. For the venue-side margining math, see Hyperliquid’s docs on account abstraction modes and portfolio margin — the summary below covers the part a VOOI client needs to reason about.
disabled — standard mode
Spot and perp balances are kept separate, and each perp DEX (the default USDC perp DEX and any HIP-3 DEX the user has touched) maintains its own cross-margin pool. Moving funds between them requires an explicit send-asset action.
This is the right mode for market makers, high-frequency strategies, and builder-fee deployers — it has no daily action cap. Hyperliquid only accrues builder fees while the account is in standard mode.
unifiedAccount
Hyperliquid keeps a single per-asset balance that funds spot trading, every perp DEX, and validator operations at once. A user with USDC in spot can open a perp position on the default DEX without first moving funds; a user with USDH in spot can collateralize a position on a USDH-quoted DEX the same way (none of the DEXes VOOI currently surfaces is USDH-quoted, but individual HIP-3 DEXes pick their own quote currency).
This is the recommended mode for most end users. One caveat to surface in a UI: Hyperliquid caps the account at 50,000 user actions per day.
In this mode GET /exchange/accounts returns only the two "spot" rows (USDC and USDH). The spot balances back perp positions on every DEX directly, so no separate perp rows are returned. See Account balances.
portfolioMargin
Builds on unifiedAccount by margining all eligible spot and perp positions together as one portfolio, with cross-asset offsets. Eligible collateral assets at time of writing are USDC, USDH, BTC, and HYPE.
This mode is pre-alpha on Hyperliquid and currently restricted: master accounts need >$5M weighted volume to enable it, and per-asset borrow/supply caps apply. When a cap is exceeded, the venue temporarily reverts margining for that asset to non-portfolio behavior. Treat it as opt-in for sophisticated users and link out to Hyperliquid’s portfolio margin page for the current eligibility rules.
dexAbstraction
Legacy mode. USDC sits in the perp balance, every other collateral asset sits in the spot balance, and cross-margin on HIP-3 DEXes behaves unintuitively. Hyperliquid recommends new clients not offer this mode; VOOI still returns it so existing accounts can read their current state and migrate off.
Read the current mode
GET /exchange/account-info?exchange=hyperliquid returns { abstractionMode: "disabled" | "unifiedAccount" | "portfolioMargin" | "dexAbstraction" | "default" }. default means the user has never picked a mode on Hyperliquid; treat it the same as disabled for UX purposes (separated balances, no daily-action cap).
A typical client polls this on a low cadence (every 30 s is enough — the value only changes when the user explicitly switches it) and caches it next to the user’s HL connection state.
Decide whether to prompt a switch
Most clients want to keep users inunifiedAccount so a single funded balance covers every perp DEX without send-asset hops. portfolioMargin extends unifiedAccount with cross-asset margining, so for routing decisions it counts as “unified” too. The minimal predicate:
needsUnifiedSwitch(mode) returns true and the user is about to deposit, transfer in, or open a cross-DEX position, surface a “Switch to unified” call-to-action before the action so they don’t end up with funds stranded on the wrong DEX. If the user prefers to stay in standard mode (e.g. they’re a market maker collecting builder fees), respect that — don’t auto-switch silently.
Change the mode
POST /exchange/hyperliquid/set-abstraction/prepare builds the EIP-712 envelope your main Hyperliquid wallet has to sign — the VOOI-managed agent cannot sign this action. POST /exchange/hyperliquid/set-abstraction/execute submits it.
signData (EIP-712 typed data for the userSetAbstraction action) and pendingData (opaque, pass back unchanged).
Make sure the user’s wallet is on Arbitrum (chain id 42161) before signing — signData.domain.chainId is fixed and a signature produced on a different chain won’t be accepted. Then post pendingData and the signature to the execute endpoint:
account-info and /exchange/accounts data — the row set changes when the user enters or leaves unifiedAccount (perp DEX rows drop on entry and reappear on exit, see Account balances).
If the user rejects the wallet prompt or signs on the wrong chain, execute returns 400 and the venue-side mode is unchanged — safe to re-run the prepare/sign/execute flow without cleanup.
Approve a builder fee
Skip this flow unless the user’s broker uses Hyperliquid builder fees. Hyperliquid only requires the main wallet to sign a one-timeapproveBuilderFee action when an order carries a non-zero builder fee — without that approval, the venue rejects fee-bearing orders. A broker that monetizes this way will have set defaults via POST /broker/exchanges for hyperliquid. (Aster bakes the equivalent into the agent registration step — see Customize the builder approval.)
The three endpoints below are unified across venues — the same /exchange/broker/* paths handle Hyperliquid, Lighter, Robinhood, and Aster. Pass exchange: "hyperliquid" for the Hyperliquid main-wallet flow described here. The other venues use the same shape with a different exchange value and signer.
GET /exchange/broker/approved reports whether the user has already approved a given builder address on the venue — call it before showing an approval prompt.
1. Prepare
POST /exchange/broker/prepare builds the EIP-712 envelope for the approveBuilderFee action.
identifier (builder address) and maxFeeBps (cap) are optional. The expected call shape depends on how the broker is set up:
- Broker default configured for this user (the common case). You may pass either field, both, or neither — each is validated independently. Any value you do pass must equal the broker’s configured value exactly, or the request returns
400; any field you omit falls back to the broker’s configured value. (The body’smaxFeeBpscorresponds to the cap, so it matches the broker record’smaxFeeBps, not its per-orderfeeBps.) - No broker default configured. Pass both
identifierandmaxFeeBpsexplicitly, otherwise the call returns400.
signData (EIP-712 typed data for the approveBuilderFee action) and pendingData (opaque, pass back unchanged in step 2).
2. Sign and execute
SignsignData as EIP-712 typed data with the main wallet’s private key (the agent cannot sign this action), with the wallet on Arbitrum (chain id 42161) so the signature is accepted. message.nonce is a uint64 — cast numeric values to BigInt if your signer requires it, same pattern as Move assets within Hyperliquid.
{ "status": "ok" } on success. The approval is one-shot — re-run the flow only if the broker raises the cap or rotates to a different builder.
Swap USDC ↔ USDH
POST /exchange/hyperliquid/stablecoins-swap converts between USDC and USDH on the Hyperliquid spot market. The endpoint is single-step — no main-wallet signature required, just the standard bearer token.
amount is the human-readable size of tokenIn to spend. The response returns amountOut — the size of tokenOut you received after the swap fills.
The swap routes through Hyperliquid’s USDH/USDC spot market with a price guard (no worse than 1.005 when buying USDH, no better than 0.995 when selling). If the book can’t fill within that band, the order is rejected.
One supported way to fund a USDH-quoted DEX when the user holds USDC, and to convert back when closing positions there.
Next steps
Cross-exchange transfers
Move USDC between Hyperliquid, Lighter, and Arbitrum
Positions and accounts
Read positions, balances, and per-market settings