Skip to main content

Vault Operations

(assetId: string, amount: bigint, autoWait?: boolean, target?: DepositTarget, options?: DepositOptions) => Promise<TransactionResult>
Deposit tokens into vault. Uses asset ID (UUID) from trading pair.Parameters:
  • assetId: string - Asset UUID (from trading pair)
  • amount: bigint - Token amount in smallest unit
  • autoWait?: boolean - Wait for on-chain confirmation (default: true)
  • target?: "spot" | "margin" - Destination ledger (default: "spot")
    • "spot" — credits the spot wallet (unchanged behavior)
    • "margin" — routes the deposit straight into the parent margin account’s collateral, skipping the separate transferCollateralToParentMarginAccount step. Deposits that cannot be routed to margin (e.g. unsupported asset) fall back to spot automatically.
  • options?: DepositOptions - Deposit options. asErc20: true deposits the chain’s wrapped-native asset (e.g. WSEI) as its ERC20 through approve + depositERC20, instead of the native-coin default below; it is ignored for every other asset. The ERC20 path is not auto-approved — deposit throws a ContractError (code === "CONTRACT_ERROR") with revertReason === "APPROVAL_REQUIRED" if the allowance is short, so approve (or needsApproval with the same option) first.
Example:
Wrapped-native assets deposit as native coin. When the asset is the chain’s wrapped-native token (e.g. WSEI) — the wrappedNative: true flag on its balance row — deposit routes to the vault’s payable depositNative and sends amount as msg.value; the vault wraps it on entry. No ERC20 approval is required or checked for these assets, so you can skip the approve / needsApproval step. Every other asset keeps the approve + depositERC20 flow. Withdrawals of a wrapped-native asset pay out native coin (the vault unwraps), decided server-side with no request change.To deposit the wrapped-native asset from a wallet that already holds the wrapped ERC20 itself, pass { asErc20: true } as the fifth argument: deposit then takes the standard approve + depositERC20 path (not depositNative), and needsApproval(assetId, amount, { asErc20: true }) reads the real allowance instead of returning false.
(assetId: string, amount: bigint, autoWait?: boolean, source?: WithdrawalSource) => Promise<WithdrawResult>
Withdraw tokens from vault. Posts to /api/v1/withdrawals, waits for merkle-proof-backed executeWithdrawal(...) calldata, then submits the transaction on-chain.Parameters:
  • assetId: string - Asset UUID (from trading pair)
  • amount: bigint - Token amount in smallest unit
  • autoWait?: boolean - Wait for on-chain confirmation (default: true)
  • source?: "spot" | "margin" | "cross_risk_bucket" - Source ledger (default: "spot")
    • "spot" — debits the spot wallet
    • "margin" — directly debits withdrawable collateral from the parent margin account. Only unallocated collateral can leave: an amount that needs collateral still allocated to a risk bucket, the cross bucket included, is refused with 400 — see withdrawals.initiateWithdrawal below for the messages
    • "cross_risk_bucket" — takes the amount out of the default margin account’s cross risk bucket, up to the bucket’s withdrawableCollateral; see withdrawals.initiateWithdrawal below
When autoWait is true (default), the call waits for the withdrawal’s on-chain merkle-proof calldata before submitting. The default wait timeout is 5 minutes (300 seconds), polling every 500 ms. Override these via WithdrawalRetryOptions { pollIntervalMs?: number; timeoutMs?: number }.When autoWait is false, the call returns immediately after the withdrawal is registered — it does not poll for the merkle proof. Use this when you want to initiate the withdrawal and complete the on-chain step later:
Returns: WithdrawResult:
  • withdrawalIndex: number — the index allocated by the matching engine. Store this — you’ll need it if the on-chain submission fails and you want to retry
  • hash?: string — transaction hash; absent when autoWait is false
  • status: string — "awaiting_proof" when no transaction was submitted (the proof was not yet available); otherwise the transaction status, "pending", "confirmed" or "failed"
  • nonce?: bigint — nonce of the on-chain submission; absent with "awaiting_proof"
  • receipt?: transaction receipt, only when autoWait is enabled and a transaction was submitted
Withdrawal rate limit. The vault can cap, per token, how much withdrawals against recently confirmed roots pay out at once, refilling over time. A withdrawal over what the limit allows right now is refused on-chain, and withdraw throws a WithdrawalRateLimitedError (a ContractError with revertReason === "WithdrawalRateLimited" and retryable === true). It carries withdrawalIndex, token (WSEI’s address for native withdrawals), and available and amount in the token’s raw units. That holds when the vault refuses it at gas estimation, and when the limit runs out before the transaction is mined; in the second case transactionHash is set. To tell a mined revert’s reason, the SDK traces the transaction (debug_traceTransaction); on a node without it, it replays the call at the end of the block, which can miss a limit refilled later in that block, and then returns status: "failed" as for any other on-chain failure. Nothing is lost: the balance stays debited and the index stays valid, so call retryWithdrawal(withdrawalIndex) once the limit has refilled, or once the withdrawal’s root is a day old, when the limit no longer applies to it.
(withdrawalIndex: number, autoWait?: boolean) => Promise<WithdrawResult>
Re-submit a withdrawal whose on-chain transaction failed or was dropped. Fetches the same merkle-proof-backed calldata for that withdrawalIndex and resubmits it. Does not allocate a new withdrawal slot — safe to call multiple times.Parameters:
  • withdrawalIndex: number — returned by the original withdraw() call
Returns: Same WithdrawResult shape as withdraw(). Throws WithdrawalRateLimitedError while the vault’s withdrawal rate limit still refuses the withdrawal.
() => Promise<string>
Get the vault contract address for the current network.Returns: Vault contract address (e.g., 0x1234...)Use cases:
  • Add vault address to wallet for direct contract interaction
  • Verify transactions on blockchain explorers
  • Advanced integrations requiring direct contract access
Example:
Network-specific:
  • Mainnet: Returns the Sei mainnet vault address

Low-Level Withdrawals

The sdk.withdrawals API exposes the raw withdrawal calldata flow. Unlike sdk.vault.withdraw — which waits for the merkle-proof-backed calldata and broadcasts the transaction on-chain — sdk.withdrawals returns the ABI-encoded executeWithdrawal(...) calldata without broadcasting. Use it when you want to submit the transaction yourself (custom signer, relayer, batching, or inspection).
(request: InitiateWithdrawalRequest) => Promise<WithdrawalResponse>
Initiate a withdrawal. Authenticated (master accounts with the withdraw permission). Debits the caller’s balance via the matching engine and allocates a withdrawal index. Does not submit on-chain.Request:
  • assetId: Asset UUID to withdraw
  • amount: Raw token amount in the smallest unit, as a stringified positive integer
  • destination: On-chain address that will receive the withdrawal (EVM, 0x-prefixed)
  • source?: "spot" | "margin" | "cross_risk_bucket" — source ledger (default "spot"); "margin" debits withdrawable collateral from the parent margin account; "cross_risk_bucket" takes it from the default margin account’s cross risk bucket
A margin withdrawal can only take the margin account’s unallocated collateral: every risk bucket’s allocation, the cross bucket’s included, stays out of reach until it is released to the margin account. An amount past it is refused with 400, naming what to release only when releasing it alone would free enough — when the cross bucket’s allocation covers the gap, source=margin withdrawal cannot withdraw cross risk bucket collateral; withdraw it with source=cross_risk_bucket if the cross bucket alone holds the whole amount and release the cross risk bucket's collateral to the margin account first if the amount also needs the unallocated collateral, source=margin withdrawal cannot withdraw isolated risk bucket collateral when your isolated risk buckets’ allocations, taken together, cover it — and otherwise amount exceeds the margin account's withdrawable collateral of <amount>.A cross_risk_bucket withdrawal releases the amount from the default margin account’s cross risk bucket and withdraws it, in one step. It takes up to the bucket’s withdrawableCollateral — the same bound a transfer out of the cross risk bucket has — and the margin account’s own bound still applies, so an unrealized gain never funds it. A refused request moves nothing. If the withdrawal stops after its release (409 with serverCode COLLATERAL_TRANSFER_STOPPED_PARTWAY), the amount is on the margin account the message names: withdraw it from there with source: "margin".The destination must differ from the configured vault contract address; using that address returns 400 before the balance is debited.Throws: 400 for an invalid amount, address or asset, including the vault as destination; 401 without valid authentication; 403 without withdrawal permission or for an unsupported account type, or with serverCode ADDRESS_RESTRICTED when the destination is on the OFAC sanctions list (refused before any balance is debited); 404 when the asset is absent; 429 when the movement budget is exhausted; 500 on an internal failure; 503 when the matching engine is unavailable or maintenance blocks the write. Do not initiate a second withdrawal after an ambiguous transport failure; reconcile the original first.Returns: WithdrawalResponse:
  • withdrawalIndex: number — matches executeWithdrawal.index on-chain
  • vaultAddress: string — vault contract the calldata is submitted to
  • calldata: string — ABI-encoded executeWithdrawal(...) calldata; empty until the merkle proof is available
(withdrawalIndex: number) => Promise<WithdrawalResponse>
Re-fetch a previously-initiated withdrawal’s executable calldata. Public — no auth required.Parameters:
  • withdrawalIndex: number — returned by initiateWithdrawal()
Returns: Same WithdrawalResponse shape as initiateWithdrawal().
(params?: ListPendingWithdrawalsParams) => Promise<ListPendingWithdrawalsResponse>
List the caller’s withdrawals that are still awaiting on-chain root confirmation, newest first. Authenticated and scoped to the caller’s own account. An entry here has no calldata yet: once it is confirmed it leaves this list, and getWithdrawal() returns its calldata.Parameters: all optional
  • pageSize?: number — 1–1000, default 20. A larger value is rejected, not clamped
  • pageToken?: string — opaque cursor: omit it (or send "") to start from the newest pending withdrawal, then pass the prior response’s nextPageToken to continue. The walk seeks on (createdAt, withdrawalIndex) descending, so a withdrawal requested mid-walk cannot make the walk repeat or drop a row
There is no page: the SDK rejects a leftover one with a ValidationError before a request is sent, and the server answers 400.Returns: ListPendingWithdrawalsResponse:
  • withdrawals: PendingWithdrawal[] — each with withdrawalIndex, assetId, assetSymbol, raw amount (smallest unit, stringified integer), destination, status (always "pending") and RFC 3339 createdAt
  • total, pageSize, totalPages: number — exact counts
  • nextPageToken: string — pass back as pageToken; empty once the walk is exhausted. This listing is one ordered read, so a page shorter than pageSize is a definitive end
Throws: 400 for invalid pagination; 401 without valid authentication; 429 when the enforced read budget is exhausted; 500 on an internal failure.

Registered Deposit Addresses

Instead of the approve → deposit flow above, you can give a user a stable, deterministic deposit address and have deposits of the watched assets swept into the rollup automatically. Only the assets sweeper.getChains (below) lists are indexed — the transfer indexer filters logs to those token addresses — so an unlisted ERC20 sent to the address is not swept. Every (application, user, deposit target) triple maps to one such address; registering the triple makes the backend monitor it and forward a watched asset once its accumulated balance reaches that asset’s minimum, crediting the user under the application — no per-deposit signature or transaction from your app.
(body?: RegisterSweeperRequest) => Promise<RegisterSweeperResponse>
Register a deposit address so it is monitored and swept. Requires authentication — call sdk.login() first.Both halves of the identity come from the session: the application is the one sdk.login() was called with, and the credited address is the session’s own. Neither is a request field, so this can only ever register the caller’s own address under their own application — and the body carries nothing but the target.Request — RegisterSweeperRequest (optional; omit it entirely for a spot address):
  • depositTarget?: "spot" | "margin" — which ledger swept deposits ask to be credited to. Defaults to "spot".
    • "spot" — credits the spot wallet (unchanged behavior).
    • "margin" — asks for the parent margin account’s collateral, skipping the separate transferCollateralToParentMarginAccount step. A request, not a guarantee — see the note below.
Returns — RegisterSweeperResponse:
  • status: "registered" (the triple was added by this call) or "already_registered" (it was known before).
  • sweeperAddress: string — the deposit address for this triple. Stable, and valid to publish and fund before any contract exists there.
  • initialSweepCheck: boolean — whether the one-time registration balance check has completed. false right after a fresh registration; it runs in the background and flips once every pre-existing balance has been swept.
Idempotent and safe to retry within the current forwarder-based derivation: re-registering an existing triple returns already_registered with the same derived address. A completed initial check is never repeated; if initialSweepCheck is still false, registration schedules it again, while an already-running check is deduplicated.Pre-v1.0.53 registrations need an operator migration. Calling register for one returns and monitors the new forwarder-derived address in the running resolver, but does not replace that row’s stored address; after a resolver restart, warmup reloads only the old value. Pause deposits for that registration and do not display or fund the new address until Monaco confirms its stored row was migrated. Re-registration alone is not restart-durable.Example:
Errors: 400 (a depositTarget that is neither spot nor margin — the SDK rejects it before sending — or a body that still carries the removed clientId or userAddress, which unknown-field rejection turns into a 400 rather than ignoring it; that one only reaches you from an untyped or hand-rolled request, since the typed request has no such fields), 401 (no or invalid session), 403 (the session’s application is no longer active), 429 (the service-wide admission limit on new registrations — they create durable state, so they are bounded across all callers; re-registering an existing address is never limited, and a refused call stores nothing. The body’s retryAfter is the limiter’s own remaining wait in seconds, not a fixed window and not the 24-hour default other limits use — the bucket refills continuously, so it is usually short. Honor the value rather than assuming one), 500 (a server-side failure while registering), and 503 (the sweeper could not complete the registration — several unrelated conditions share this status, from missing configuration to an unreachable chain or store). Treat a 503 as an unknown outcome, not a negative one: the registration is stored before the reply is composed, so a failure reported after that write is indistinguishable from one reported before it, and the status alone never tells you which happened. Retrying is always safe — registration is idempotent, and the retry answers registered or already_registered according to whether the first attempt had landed. Both are success and both carry the same address, so re-register rather than reporting failure, and accept either status.
() => Promise<GetSweeperChainsResponse>
List the chains and assets the sweeper watches. Public — it takes no arguments and needs no session, so a deposit page can show the supported set before the user logs in.Each SweeperChain carries chainId (the EVM chain id, a number — the service refuses ids above Number.MAX_SAFE_INTEGER, so it is always exact), a short name, a hub flag marking the settlement chain that deposits on every other listed chain are bridged into, and its assets. Each SweeperChainAsset carries symbol, the tokenAddress to send on that chain, decimals (0 when the token does not publish them), and minSweepRaw — the minimum sweepable balance in raw base units, as a decimal string because raw amounts can exceed Number.MAX_SAFE_INTEGER. A balance strictly below the minimum stays on the deposit address until a later deposit brings the total up to it; a balance exactly at the minimum sweeps.Example:
The listing is all-or-nothing: rather than publish a partial set, the resolver refuses the whole call whenever any configured chain cannot be advertised. How current it is depends on the chain’s token source — a hub backed by the active-asset registry picks up a listing or delisting on the resolver’s own refresh, with no restart, while a chain configured with an explicit token list changes only when that configuration is rolled out. The gateway also serves the last answer from a short cache. The two intervals stack — at the defaults the resolver refreshes its asset registry every 60 seconds and the gateway caches the resolver’s answer for 60 seconds — so a change can take close to two minutes to appear. Treat the listing as recent, not live.Errors: 500 (a server-side failure) and 503 — both safe to retry. There is no 401: the call is unauthenticated. The 503 covers more than a missing configuration: a configured chain disabled by its own configuration, a chain whose id the resolver has not yet verified against its RPC (an unreachable RPC included — until it verifies, no indexer is watching that chain, so it must not be advertised), a chain whose RPC reports an id outside the JSON-safe range, and a resolver that could not answer at all.
(txHash: string) => Promise<GetSweeperDepositStatusResponse>
Where the caller’s deposits made by one transaction are. Pass the hash of the transaction that sent funds to the caller’s deposit address — on any listed chain — or that burned USDC through CCTP straight to it (the burnTxHash depositFromHyperEvm returns), and poll until each deposit reads DEPOSITED (or FAILED). Requires a session; only deposits credited to the session’s own address are returned, so a transaction that paid several deposit addresses shows the caller just their own. The list is empty until the sweeper has indexed the transaction, so an empty answer right after sending is expected. A malformed hash throws before anything is sent.Each SweeperDeposit moves through:
  • RECEIVED — indexed, not yet swept. A balance below the asset’s minSweepRaw waits here for a later deposit.
  • On the hub chain, straight to DEPOSITED when the sweep lands.
  • On any other chain, the sweep bridges by CCTP: BURNED (swept off the source chain), ATTESTING (waiting for Circle’s attestation), ATTESTED, SETTLING (minted on the hub, being swept into the exchange), then DEPOSITED.
  • A CCTP burn made straight to the deposit address — a Hyperliquid deposit, burned on HyperEVM (sourceChainId 999, 998 on testnet), or an Arc deposit (sourceChainId 5042, 5042002 on testnet) — starts at BURNED (the burn is its own sweepTx) and follows the same CCTP path.
  • FAILED with failureReason: "REFUNDED" — the sweep returned the funds to the depositor’s wallet instead (the exchange was paused, the user is blocked, or the token is not accepted).
sweepTx is the source-chain sweep (the CCTP burn for a remote deposit) and settleTx the hub transaction that deposited the funds; transferType is the CCTP lane the burn requested (FAST or STANDARD). creditedAmountRaw and feeChargedRaw (Circle’s fast-lane fee, "0" on the hub) are set once DEPOSITED. Sweeps move balances, not single transfers, so a sweep that collected several deposits is attributed to each first in, first out — and when one CCTP burn carried several deposits its fee cannot be split, so those two amounts stay null. Deposits bridged by a route other than CCTP stay at BURNED.Example:
Errors: 400 (malformed hash), 401 (no session), 429 (read budget exhausted), 500, and 503 — deposit status is not available in the environment, or the read ran past its deadline. Both are safe to retry.
(nonce: number) => Promise<GetSweeperHypercoreDepositStatusResponse>
Where the caller’s deposit from Hyperliquid (HyperCore) is, by the nonce depositFromHypercore returns. A HyperCore withdrawal has no transaction hash the depositor ever sees — Circle burns it on HyperEVM — so the sweeper reads the Hyperliquid account and nonce from that burn, and again from the attested message its Sei mint carries. The answer holds the same SweeperDeposit entries getDepositStatus returns, each with hypercoreUser / hypercoreNonce set: usually one, the burn (sourceChainId HyperEVM, from BURNED) or, while the burn is not yet indexed but Circle has minted, the Sei mint (sourceChainId Sei, from RECEIVED); poll until an entry reads DEPOSITED (or FAILED). Requires a session; only deposits credited to the session’s own address are returned, whichever Hyperliquid account signed the withdrawal (hypercoreUser names it). The list is empty until the sweeper has indexed the burn or its mint. A nonce that is not a positive safe integer throws before anything is sent.Example:
Errors: 400 (a nonce that is not a positive integer), 401, 429, 500, and 503 (not available, or past the read’s deadline; safe to retry).
(params?: ListSweeperDepositsParams) => Promise<ListSweeperDepositsResponse>
The caller’s deposits, newest first — the same SweeperDeposit entries getDepositStatus returns, so a deposit whose transaction hash was lost can still be found and followed. A swept CCTP deposit is listed once, by its source-chain transfer (its hub mint is the same deposit’s second leg). A CCTP burn made straight to the deposit address (a Hyperliquid deposit) is listed by its burn; its hub mint stops listing once the sweeper links it to the burn, moments after it lands, so the deposit can briefly appear twice (a lookup by the hub mint’s own hash keeps answering). Requires a session; only the session’s own deposits are returned.Walk by cursor: omit pageToken to start, then pass each response’s nextPageToken until it comes back empty — a shorter page is the definitive end. pageSize is 1 to 100 (default 20): each entry is derived from the deposit’s whole sweep path, so pages stay small. An out-of-range pageSize throws before anything is sent.Example:
Errors: 400 (pageSize out of range, or a pageToken that is malformed or minted by another listing), 401, 429, 500, and 503 (not available, or past the read’s deadline; safe to retry).
() => Promise<HypercoreDepositInfo>
What the connected wallet can deposit from Hyperliquid (HyperCore): everything a deposit form needs before the user types. It reads only public data — Hyperliquid’s info endpoint, Circle’s CoreDepositWallet on HyperEVM, and getChains — so it needs a wallet client on Sei but no session.Returns — HypercoreDepositInfo, amounts in raw USDC base units (6 decimals), truncated from Hyperliquid’s own decimals:
  • account: string — the wallet’s address, which is its Hyperliquid account.
  • accountMode: string — the account’s abstraction mode, as Hyperliquid reports it: unifiedAccount and portfolioMargin keep one balance per asset, reported as spot; disabled, default and dexAbstraction keep separate perp and spot balances.
  • perpBalance: bigint — withdrawable USDC in the perp balance; 0n for a unified or portfolio-margin account.
  • spotBalance: bigint — USDC in the spot balance that is not on hold.
  • available: bigint — the most one deposit can draw. A withdrawal draws on a single balance, so this is not the sum of the two, and it leaves room for Hyperliquid’s own withdrawal fee (below).
  • maxFee: bigint — the most Circle keeps to forward a withdrawal to Sei.
  • minimumAmount: bigint — the smallest amount depositFromHypercore accepts: maxFee plus the hub’s minimum for Circle’s USDC (minSweepRaw in getChains).
Errors: InvalidConfigError for a missing wallet client or one not on Sei, InvalidStateError when the hub does not sweep Circle’s USDC, and APIError when a Hyperliquid read fails (safe to retry).
(body: DepositFromHypercoreRequest) => Promise<DepositFromHypercoreResponse>
Deposit USDC the connected wallet holds on Hyperliquid (HyperCore). The wallet signs one Hyperliquid action — Circle’s CCTP withdrawal, sendToEvmWithData, to the caller’s deposit address on Sei — and the SDK posts it to Hyperliquid, which executes it. The user pays no gas and sends no HyperEVM transaction. Circle mints the USDC on Sei, keeping up to its forwarding fee, and the hub sweeps the rest like any deposit made straight to the address. Requires a session and a wallet client whose account holds the Hyperliquid balance. It registers the deposit address itself, so no separate register call is needed.The SDK picks the Hyperliquid balance from the account: spot for a unified or portfolio-margin account (Hyperliquid refuses any other source for one), otherwise the perp balance when it covers amount and the spot balance when only that does.Hyperliquid charges its own fee on each withdrawal, on top of amount: 0.012948 USDC on mainnet and 0.07122 to 0.072664 USDC on testnet when measured. It takes that fee from spot when spot can cover it, otherwise from the balance withdrawn. The SDK keeps 0.1 USDC back for it: a spot draw leaves 0.1 in spot, and a perp draw leaves 0.1 in perp unless spot holds at least that much.Request — DepositFromHypercoreRequest:
  • amount: bigint — USDC to withdraw from Hyperliquid, in raw base units (6 decimals). Circle’s fee comes out of it.
  • depositTarget?: "spot" | "margin" — which deposit address receives it, as in register. Defaults to "spot".
Returns — DepositFromHypercoreResponse, once Hyperliquid accepts the action:
  • sweeperAddress: string — the deposit address on Sei the USDC is minted to.
  • amount: bigint — the USDC withdrawn from Hyperliquid.
  • source: "perp" | "spot" — the Hyperliquid balance it was drawn from.
  • maxFee: bigint — the most Circle keeps to forward the withdrawal to Sei.
  • minimumCredit: bigint — amount - maxFee, the least that is credited.
  • nonce: number — the Hyperliquid action’s nonce: pass it to getHypercoreDepositStatus to follow the deposit.
The deposit then appears in listDeposits too. Before registering or signing, the SDK refuses an amount below minimumAmount or above available (see getHypercoreDepositInfo). Hyperliquid charges a new account a one-time 1 USDC activation fee; an account opened by a Circle deposit that has never sent anything out pays it from its balance on this first outbound action, so it needs that much on top of amount.Example:
Errors: before signing, InvalidConfigError for an invalid amount, a missing wallet client, a wallet that is not on Sei, or an amount below the minimum or above what one balance holds; InvalidStateError when the hub does not sweep Circle’s USDC; and APIError when a Hyperliquid read fails. Errors from register and getChains pass through. Hyperliquid’s refusal throws APIError with Hyperliquid’s reason: a decided outcome, nothing moved. A dropped connection, a 5xx from Hyperliquid, or a reply that is neither accepted nor refused is an unknown outcome, and its APIError carries retryable: false: Hyperliquid may have executed the action, and calling depositFromHypercore again signs a second withdrawal. Look for the deposit in listDeposits or check the Hyperliquid balance before trying again.
Only the wallet that holds the Hyperliquid balance can sign. A user who signs into Hyperliquid by email holds a wallet of Hyperliquid’s own, which your app cannot sign with. That user can move the USDC with Hyperliquid’s own Send to the address of the wallet your app connects (account in getHypercoreDepositInfo), then deposit from there; Hyperliquid charges its activation fee on the first transfer into a new account. Never present the deposit address as a Send target: USDC sent there lands in that address’s Hyperliquid account, which nothing can move.The signature’s domain uses Sei’s chain id, so a wallet on Sei signs without switching networks. The SDK asks a browser or embedded wallet for eth_signTypedData_v4 directly rather than through viem’s signTypedData, which refuses Hyperliquid’s HyperliquidTransaction: type names in some viem versions, so the wallet must accept those names.
() => Promise<HyperEvmDepositInfo>
What the connected wallet can deposit from HyperEVM, Hyperliquid’s EVM chain. It reads only public data — the HyperEVM RPC, Circle’s fee API, and getChains — so it needs a wallet client on Sei but no session.Returns — HyperEvmDepositInfo:
  • account: string — the wallet’s address on HyperEVM.
  • usdcBalance: bigint — the wallet’s Circle USDC on HyperEVM, raw (6 decimals).
  • gasBalance: bigint — the wallet’s HYPE on HyperEVM, in wei. It pays the approval and burn gas.
  • maxFee: bigint — Circle’s forwarding fee to Sei, quoted from Circle’s API: the most Circle keeps.
  • minimumAmount: bigint — the smallest amount depositFromHyperEvm accepts: maxFee plus the hub’s minimum for Circle’s USDC.
Errors: InvalidConfigError for a missing wallet client or one not on Sei, InvalidStateError when the hub does not sweep Circle’s USDC, and APIError when Circle’s fee quote fails (safe to retry).
(body: DepositFromHyperEvmRequest) => Promise<DepositFromHyperEvmResponse>
Deposit Circle USDC the connected wallet holds on HyperEVM. The wallet approves the USDC (when its allowance is short) and burns it through Circle’s TokenMessengerV2 on HyperEVM, naming the caller’s Sei deposit address as the recipient and asking Circle to forward the mint. Circle mints on Sei, keeping up to maxFee, and the hub sweeps the rest like any deposit made straight to the address. The wallet pays HYPE for gas; no Monaco contract is involved on HyperEVM. Requires a session and a wallet client; it registers the deposit address itself.A browser or embedded wallet is switched to HyperEVM (chain 999 on mainnet, 998 on testnet), added first when it does not know the chain, and switched back to Sei afterwards. The wallet must allow HyperEVM: with Privy, list it in supportedChains.Request — DepositFromHyperEvmRequest:
  • amount: bigint — USDC to burn on HyperEVM, in raw base units (6 decimals). Circle’s fee comes out of it.
  • depositTarget?: "spot" | "margin" — which deposit address receives it, as in register. Defaults to "spot".
Returns — DepositFromHyperEvmResponse, once the burn lands on HyperEVM:
  • sweeperAddress: string — the deposit address on Sei the USDC is minted to.
  • amount: bigint — the USDC burned.
  • maxFee: bigint — the most Circle keeps to forward it to Sei.
  • minimumCredit: bigint — amount - maxFee, the least that is credited.
  • approveTxHash?: string — the mined HyperEVM approval, when the allowance had to be raised.
  • burnTxHash: string — the mined HyperEVM burn. When the wallet sped the burn up with a new fee, this is the replacement that was mined.
Follow the deposit with getDepositStatus(burnTxHash); it appears in listDeposits too. Before registering or sending, the SDK refuses an amount below minimumAmount or above usdcBalance (see getHyperEvmDepositInfo), and a wallet with no HYPE.Example:
Errors: before sending, InvalidConfigError for an invalid amount, a missing wallet client, a wallet that is not on Sei, or an amount below the minimum or above the balance; InvalidStateError when the hub does not sweep Circle’s USDC or the wallet has no HYPE; and APIError when Circle’s fee quote fails. A wallet’s refusal (a rejected prompt, say) passes through the wallet’s own error. An approval or burn that reverts, or that the wallet cancels or replaces with a different transaction, throws ContractError: nothing was burned. A burn that may have been broadcast but whose outcome cannot be read — a lost reply to the broadcast, an unexpected wallet failure, or an unreadable receipt — is an unknown outcome: its APIError carries retryable: false and, when the SDK knows it, the burn’s hash in responseBody.burnTxHash, because calling depositFromHyperEvm again burns a second time. Check the wallet’s HyperEVM activity, or look for the deposit in listDeposits, before trying again.
() => Promise<ArcDepositInfo>
What the connected wallet can deposit from Arc, Circle’s chain. It reads only public data — the Arc RPC, Circle’s fee API, and getChains — so it needs a wallet client on Sei but no session.Arc pays gas in USDC. Its native balance (18 decimals) and its USDC token at 0x3600000000000000000000000000000000000000 (6 decimals) are the same balance, so the approval and burn gas comes out of the USDC being deposited. The SDK keeps enough back to pay for them.Returns — ArcDepositInfo, amounts in raw USDC base units (6 decimals):
  • account: string — the wallet’s address on Arc.
  • usdcBalance: bigint — the wallet’s USDC on Arc.
  • gasReserve: bigint — USDC kept back for the approval and burn: 500,000 gas at Arc’s current max fee per gas, rounded up to a whole USDC unit: 0.012 to 0.015 USDC at Arc’s usual 20 to 25 gwei. It stays in the wallet; only the gas actually used is spent, about 0.0045 USDC for an approval and a burn.
  • available: bigint — usdcBalance - gasReserve, never below 0n: the most one deposit can burn.
  • maxFee: bigint — Circle’s forwarding fee to Sei, quoted from Circle’s API: the most Circle keeps.
  • minimumAmount: bigint — the smallest amount depositFromArc accepts: maxFee plus the hub’s minimum for Circle’s USDC.
Errors: InvalidConfigError for a missing wallet client or one not on Sei, InvalidStateError when the hub does not sweep Circle’s USDC, and APIError when an Arc read or Circle’s fee quote fails (safe to retry).
(body: DepositFromArcRequest) => Promise<DepositFromArcResponse>
Deposit USDC the connected wallet holds on Arc. The wallet approves the USDC (when its allowance is short) and burns it through Circle’s TokenMessengerV2 on Arc, naming the caller’s Sei deposit address as the recipient and asking Circle to forward the mint. Circle mints on Sei, keeping up to maxFee, and the hub sweeps the rest like any deposit made straight to the address. Gas is paid in USDC, so the wallet needs no other token; no Monaco contract is involved on Arc. Requires a session and a wallet client; it registers the deposit address itself.A Sei mainnet wallet deposits from Arc mainnet (chain 5042), a Sei testnet wallet from Arc testnet (chain 5042002). A browser or embedded wallet is switched to Arc, added first when it does not know the chain, and switched back to Sei afterwards. The wallet must allow Arc: with Privy, list it in supportedChains.Request — DepositFromArcRequest:
  • amount: bigint — USDC to burn on Arc, in raw base units (6 decimals). Circle’s fee comes out of it. At most available from getArcDepositInfo.
  • depositTarget?: "spot" | "margin" — which deposit address receives it, as in register. Defaults to "spot".
Returns — DepositFromArcResponse, once the burn lands on Arc:
  • sweeperAddress: string — the deposit address on Sei the USDC is minted to.
  • amount: bigint — the USDC burned.
  • maxFee: bigint — the most Circle keeps to forward it to Sei.
  • minimumCredit: bigint — amount - maxFee, the least that is credited.
  • approveTxHash?: string — the mined Arc approval, when the allowance had to be raised.
  • burnTxHash: string — the mined Arc burn. When the wallet sped the burn up with a new fee, this is the replacement that was mined.
Follow the deposit with getDepositStatus(burnTxHash); it appears in listDeposits too. Before registering or sending, the SDK refuses an amount below minimumAmount or above available (see getArcDepositInfo).Example:
Errors: the same as depositFromHyperEvm, with Arc in place of HyperEVM: before sending, InvalidConfigError for an invalid amount, a missing wallet client, a wallet that is not on Sei, or an amount below the minimum or above available; InvalidStateError when the hub does not sweep Circle’s USDC; and APIError when an Arc read or Circle’s fee quote fails. A wallet’s refusal passes through the wallet’s own error. An approval or burn that reverts, or that the wallet cancels or replaces with a different transaction, throws ContractError: nothing was burned. A burn that may have been broadcast but whose outcome cannot be read is an unknown outcome: its APIError carries retryable: false and, when the SDK knows it, the burn’s hash in responseBody.burnTxHash, because calling depositFromArc again burns a second time. Check the wallet’s Arc activity, or look for the deposit in listDeposits, before trying again.
Bind a deposit to the exact (chainId, tokenAddress) pair getChains returns. Never resolve a token by symbol alone: the same symbol can front a different contract on another chain, and a deposit sent to the wrong contract or the wrong chain is not recoverable. Show the user the chain and the token address from the same entry you took the minimum from.
One registration covers every listed chain. The address register returns is identical on, and watched on, every chain getChains lists, so there is nothing chain-specific to pass at registration. Use getChains to tell a depositor where they may send and which token address and minimum applies there — not to choose where to register.
The deposit target is part of the address. "spot" and "margin" derive two different deposit addresses for the same application and user, and each address carries its own target. Sending to the spot address never credits margin, whichever target you registered most recently. Show the user the address returned by the registration whose target you want, and keep the two apart in your UI.
"margin" is a routing request, not a guarantee. A deposit that arrives at a margin address but cannot be routed to collateral — an unsupported collateral asset, or a margin account that fails validation — is credited to the spot wallet instead, exactly as it is for a direct deposit(..., "margin"). Funds are never stranded, but do not treat a margin deposit address as proof the credit landed in collateral: read the balance back (getParentMarginAccountSummary) rather than assuming.
Deriving the address off-chain: @0xmonaco/contracts exports predictSweeperAddress({ factory, forwarder, clientId, user, depositTarget? }), which returns the same EIP-55 checksummed address without an RPC round-trip. The forwarder is the fixed delegation target every clone points at — read it once from factory.forwarder(); the sweeper implementation is deliberately not a derivation input, so logic upgrades never move addresses. It still takes clientId explicitly — off-chain derivation has no session to read it from, and it is your own application’s id — useful to display or fund an address before registering. Pass the same depositTarget you register with, or the prediction points at the other ledger’s address. encodeDepositApplicationData(clientId, target) exposes the underlying encoding, and the SWEEPER_ABI / SWEEPER_FACTORY_ABI exports cover direct contract interaction.
SweepParams binds a sweep to an exact amount. The executor-supplied struct the ABIs encode for sweep(token, params) / sweepWithParams leads with amount, ahead of minSwapOut and minBridgeOut — the exact balance the quote was computed for. The sweep moves exactly that much and leaves any excess, such as a deposit that landed after the quote, for the next re-quoted sweep, so the minimums always cover the full swept amount and a later arrival can never dilute them. A balance that has fallen below the quoted amount reverts with StaleQuote rather than sweeping less against minimums quoted for more: re-quote and retry. amount: 0n binds nothing and sweeps the execution-time balance, and is accepted only on the direct CCTP route, whose burn is exact-amount and carries no minimum that could go stale — every other route (Stargate, swap-then-bridge, and the hub unswap) rejects the sentinel with QuoteNotBound. A sweep against an empty balance reverts with NothingToSweep. Both errors are declared on SWEEPER_ABI only — SWEEPER_FACTORY_ABI carries the new amount field on sweepWithParams but declares neither — so decode factory-route reverts with both ABIs, not the factory’s alone. Address derivation does not read SweepParams, so predictSweeperAddress(...) is unaffected by the struct’s shape.

Token Approvals

(assetId: string, amount: bigint, autoWait?: boolean) => Promise<TransactionResult>
Approve vault to spend tokens. Uses asset ID (UUID).
(assetId: string) => Promise<bigint>
Check current token allowance. Uses asset ID (UUID).
(assetId: string, amount: bigint, options?: DepositOptions) => Promise<boolean>
Check if approval is needed before deposit. Uses asset ID (UUID). Returns false for a wrappedNative asset (e.g. WSEI) without reading the on-chain allowance — its deposit goes through the payable depositNative, which consumes no allowance. Pass { asErc20: true } to check the real allowance for depositing that wrapped-native asset as its ERC20 (matching a deposit with the same option).

Deprecated Methods

vault.getBalance() has been deprecated and no longer functions as of v0.5.5. Use the Profile API instead. The vault.getBalance() method is still exported for compatibility but always throws an APIError with statusCode: 410 client-side, without sending a request. For balance queries, use the Profile API which provides more detailed information:
Why this changed:
  • Profile API provides more detailed balance information (available, locked, total)
  • Consolidates balance queries with other profile data
  • Improves performance with backend optimizations
Get all balances:
See Profile API documentation for more details.

Getting Asset IDs

Asset IDs (UUIDs) are obtained from trading pairs. Each trading pair has baseAssetId and quoteAssetId.