Transfers and withdrawals
Every HyperCore transfer is a signed action: sends and withdrawals need the account's own key, and an API wallet's transfer actions keep value inside the account.
In standard mode your account holds separate balances, and nothing moves between them by itself. There’s spot, one per perp dex (the validator-operated USDC dex and each HIP-3 dex it uses), staking and vault positions, plus sub-accounts, which are accounts in their own right.
Say 100 USDC sits in spot and you want it as margin on the USDC perp dex. You sign one sendAsset from "spot" to "" with your own key.
The action fixes the key. Every send to someone else or off the chain is user-signed (EIP-712, readable in a wallet) by the account’s own key, as are a few internal moves such as usdClassTransfer and staking deposits. The documented L1 transfers, which an API wallet may sign, stay inside the account: a leaked trading key can’t withdraw.
Two kinds of transfer action
Section titled “Two kinds of transfer action”On the wire (bytes on signing):
| User-signed | L1 action | |
|---|---|---|
hyperliquidChain ("Mainnet"), signatureChainId |
inside the action | absent |
| Nonce inside the action | time for usdSend, spotSend, withdraw3; nonce for the rest; equal to the outer nonce |
absent |
vaultAddress, expiresAfter |
not accepted | accepted, as on an order |
| Action | Moves | Signed by | Notes |
|---|---|---|---|
usdSend |
USDC to another address | own key | disabled on unified and portfolio margin accounts |
spotSend |
a spot token to another address | own key | token as "NAME:0x<32 hex>"; disabled on unified and portfolio margin |
usdClassTransfer |
USDC between spot and perp, same account | own key | toPerp; disabled on unified and portfolio margin |
sendAsset |
any token between perp dexes, spot, users and sub-accounts | own key | the general form; works in every mode |
agentSendAsset |
as sendAsset, but destination must equal the source |
API wallet | |
withdraw3 |
USDC out to Arbitrum | own key | $1 fee |
vaultTransfer |
USDC into or out of a vault | API wallet or own key | usd integer, 1e6 per USDC |
subAccountTransfer |
USDC between master and sub-account | API wallet or own key | not in the official docs |
subAccountSpotTransfer |
a spot token between master and sub-account | API wallet or own key | not in the official docs |
cDeposit, cWithdraw |
HYPE between spot and staking | own key | cWithdraw enters a 7-day queue |
hip3LiquidatorTransfer |
quote tokens to or from a HIP-3 dex’s backstop liquidator | API wallet or own key | only principal is withdrawable |
sendToEvmWithData |
a token to a linked HyperEVM contract, with a payload | own key | see HyperEVM transfers |
A user-signed transfer can’t carry expiresAfter, so once signed but unsubmitted it stays valid while its nonce is within 2 days before to 1 day after block time and above the lowest of the signer’s 100 stored. Hand one out only if you accept its execution.
sendAsset and the dex strings
Section titled “sendAsset and the dex strings”sendAsset generalises the older sends. Balances are strings: "" is the validator-operated USDC perp dex, "spot" the spot balance, anything else a HIP-3 dex. Only a dex’s collateral token can enter or leave it.
The move above:
{ "type": "sendAsset", "hyperliquidChain": "Mainnet", "signatureChainId": "0xa4b1", "destination": "0x0000000000000000000000000000000000000000", "sourceDex": "spot", "destinationDex": "", "token": "USDC:0x6d1e7cde53ba9467b783cb7c530ce054", "amount": "100", "fromSubAccount": "", "nonce": 1716531066415}destination is your own address for internal moves, or another to pay; lowercase it, because its case is hashed. fromSubAccount sends from a sub-account. amount is a decimal string, not wei.
Sub-accounts and vaults sign through the master
Section titled “Sub-accounts and vaults sign through the master”Sub-accounts and vaults have no keys. L1 actions are signed by the master (or its API wallet) with vaultAddress set to the sub-account or vault; user-signed actions name the sub-account inside, as fromSubAccount on sendAsset or an amount of "1 subaccount:0x..." on usdClassTransfer. One API wallet signing for several sub-accounts shares one nonce set. Vault lock-ups are under The numbers (vaults).
| Action | Fields | Notes |
|---|---|---|
vaultTransfer |
{vaultAddress, isDeposit, usd} |
the docs type usd only as a number; SDKs send an integer in millionths, so 10 USDC is 10000000 |
subAccountTransfer |
{subAccountUser, isDeposit, usd} |
L1 action in SDK code, not in the official docs |
subAccountSpotTransfer |
{subAccountUser, isDeposit, token, amount} |
L1 action in SDK code, not in the official docs |
Withdrawing to Arbitrum
Section titled “Withdrawing to Arbitrum”withdraw3 carries {amount, time, destination} plus the two chain fields, and signs as HyperliquidTransaction:Withdraw per the docs’ Rust type (one SDK used Withdraw3). Validators submit it to the Arbitrum bridge contract; you send no Arbitrum transaction.
The docs file it under the Arbitrum bridge, now legacy and deprecated, and prefer CCTP for native USDC.
Crossing to the HyperEVM
Section titled “Crossing to the HyperEVM”Send with spotSend or sendAsset to the token’s system address, 0x20 followed by the token index (HYPE is 0x2222...2222).
- With
sendAssetthe dex must be"spot";""returnsInvalid token. - The crossing costs 200k gas at the next EVM block’s base fee, taken from the HyperCore balance, so a balance never bridges in full.
- USDC not in whole HyperEVM units is rejected with
Invalid number of decimals(5 of 5 cases measured). - A HyperEVM transfer that fails on HyperCore does not revert: the EVM transaction succeeds and the money stays put (footguns).
The activation fee
Section titled “The activation fee”The first transaction to a new HyperCore account costs 1 quote token (1 USDC, 1 USDT), whatever is sent. Measured: two sends to one fresh testnet destination recorded fee 1.0 then fee 0.0; mainnet sends to fresh contracts each carried "fee": "1.0" in the sender’s ledger. Unactivated accounts cannot send CoreWriter actions (activation fee).
The numbers
Section titled “The numbers”| Item | Value |
|---|---|
| Activation fee | 1 quote token, once, on the first transaction to a new account |
withdraw3 fee |
$1, “at the time of this writing” |
withdraw3 time |
about 5 minutes (exchange docs); 3 to 4 minutes (bridge docs) |
| Legacy bridge deposit | credited in under 1 minute; minimum 5 USDC, smaller deposits are lost |
vaultTransfer.usd, subAccountTransfer.usd |
integer, 1e6 per USDC |
hip3LiquidatorTransfer.ntl |
integer in 1e-6 quote tokens; a multiple of 1000 quote tokens |
| HLP deposit lock-up | 4 days; user vaults 1 day |
| Unstaking queue | 7 days |
| Unified and portfolio margin accounts | 50k user actions per day |
| HyperCore to HyperEVM gas | 200k gas x next EVM block base fee |
| USDC decimals | 8 on HyperCore, 6 on the HyperEVM |
Where your USDC can margin
Section titled “Where your USDC can margin”In standard mode, USDC margins only the balance it sits on: spot, the USDC perp dex, or one HIP-3 dex. Unified and portfolio margin accounts have one balance per asset, so these moves disappear, but the older sends stop working.
Sending transfers from code
Section titled “Sending transfers from code”- Default to
sendAsset, which works in every mode.usdSend,spotSendandusdClassTransferfail on unified and portfolio margin accounts withAction disabled when unified account is active. - On those accounts, all balances and holds are in
spotClearinghouseState; per-dex perp states are not meaningful. - Price the activation fee into payouts to fresh addresses: at small sizes 1 USDC can exceed months of yield.
- Reconcile from the ledger, not your request log (data):
| Entry | Source |
|---|---|
internalTransfer (a usdSend), spotTransfer (with its fee), accountClassTransfer, subAccountTransfer, withdraw, vault entries |
userNonFundingLedgerUpdates |
AccountActivationGas, PerpDexClassTransfer |
node data |
For system addresses, decimals and the crossing fee, see HyperCore and HyperEVM transfers. For what sub-accounts are, see Sub-accounts.