Skip to main content
Withdraw funds from a provider perps account.
The examples on this page use Hyperliquid (provider: 'hyperliquid'). Withdrawal mechanics (supported assets, destination chains, and processing times) vary by provider.

Overview

PerpsClient.withdraw() is a single-call helper that builds the withdrawal payload, authorizes it through the registered provider plugin, and submits the signed action. It dispatches through the same create → authorize → execute pipeline documented in Actions, but consumers do not have to compose the steps manually. The examples on this page use a PerpsClient with the provider plugin registered and, for Hyperliquid, the user’s wallet bound:
Withdrawal authorization is provider-specific. Hyperliquid withdrawals require the user’s wallet. Lighter withdrawals are signed by the provider plugin’s SDK-managed Lighter key. Ondo withdrawals are HMAC-signed SDK-side with the client-held API key and relayed by the backend through executeAction. Always follow the ProviderAction.signer and signingMethod metadata instead of assuming one signer for every venue.
Ondo withdraws USDC to the connected wallet address only. The destination must be in the Ondo address book, so call getWithdrawFlow() first. See Ondo Withdrawals.
Hyperliquid: Withdrawal processing typically takes 3–4 minutes. The bridge transfer from Hyperliquid L1 to Arbitrum is asynchronous.

Checking the Withdrawal Flow

Call getWithdrawFlow() to see if a venue accepts a withdrawal now. It returns undefined when the provider declares no withdrawal flow.
The result is a WithdrawFlow:

Running the setup actions

Each setup entry is an ActionType. Run each one in order with perps.execute, then read the flow again. executeProviderSetupAction() rejects them, because they are not in the provider’s setup list.
On Ondo the list can hold siweLogin and addWithdrawalAddress. See Ondo Withdrawals.

Discovering Withdrawable Balances

Call getWithdrawableBalances() before rendering a withdrawal picker. Providers that support the capability return one row per withdrawable (asset, category) pair; providers without the optional read return undefined, which means the form remains amount-only. Hyperliquid, Lighter, and Ondo all implement this read. Each provider’s own page describes its categories: Hyperliquid, Lighter, and Ondo.
Each WithdrawableBalance contains: The SDK joins provider rows with /assets, drops unknown assets, and filters amounts below each asset’s minWithdrawalAmount. Never merge balances across categories. The amount remains a human-readable decimal string — the provider signer applies the asset precision. What a row lets you submit depends on the provider: A WithdrawableBalance row is assignable to WithdrawalAssetRef, so you can pass the row’s categoryId and asset as source. The backend drops unknown withdrawal params without an error, so a misspelled field is never reported.
A provider plugin implements the optional getWithdrawableBalances read by returning ProviderWithdrawableBalance[] — the pre-join shape, keyed by the provider-native assetId, not the resolved Asset. It carries categoryId, available, max, and the optional withdrawalFee and isFeeDeducted. Only relevant when authoring a provider plugin; consumers use WithdrawableBalance as above.

Throws

getWithdrawableBalances() throws PerpsError with one code for the account’s access state. See Account access.
  • SetupRequired — the venue needs a credential (a Lighter auth token or an Ondo session) and none is stored.
  • Unauthorized — the venue rejects the credential.
  • AccountNotFound — the wallet has no venue account.
Lighter reads withdrawable balances without an auth token, so on Lighter the only access code is AccountNotFound. An account with nothing to withdraw returns an empty list.

Choosing a Withdrawal Type

Call getWithdrawalTypes() to read how the venue can settle a withdrawal from each holding. It returns undefined when the provider plugin declares no such read. Only Lighter implements it.
Each WithdrawalSourceTypes holds a source (WithdrawalAssetRef) and a list of WithdrawalTypeOption entries: The method throws PerpsError when the provider plugin is not registered or when the plugin read fails.
Withdrawal submission returns 202 Accepted. Processing is asynchronous — for Hyperliquid, this typically takes 3–4 minutes via the Arbitrum bridge. Poll getAccount() to verify the balance change.

PerpsClient.withdraw

Build, sign with the provider-declared signer, and submit a withdrawal in a single call.
Parameters: WithdrawParams WithdrawalParams:
For a Lighter spot or non-quote asset, pass source from a getWithdrawableBalances() row. For a Lighter fast withdrawal, pass withdrawalOptions from getWithdrawalTypes(). See Lighter Withdrawals.
Returns: ExecuteActionResponse — { results: ActionResult[] }. The array contains a single result for the WITHDRAWAL action:

Using Service Functions (advanced)

For complete control over the create/authorize/execute steps — for example, to surface the typed-data payload in a confirmation dialog before requesting the signature — call the lower-level createAction / executeAction service functions directly. See Actions for the full create → authorize → execute pattern. A Hyperliquid withdrawal step is user-signed EIP-712. The wallet must be on the chain named in typedData.domain.chainId; PerpsClient.withdraw() switches it for you through the switchChain hook when one is set. Prefer withdraw() unless you need the payload before it is signed.
API Reference: POST /createAction, POST /executeAction