Vaults and sub-accounts
Sub-accounts and vaults are separate addresses controlled by a master
account. Both are traded through
ExchangeClient.WithVault,
and both are funded with integer micro-USDC transfers.
USD and InitialUSD fields are USDC × 10⁶: 25_000_000 is 25 USDC.
Spot amounts (SubAccountSpotTransferAction.Amount) are decimal strings.Sub-accounts
Create and fund
Hyperliquid unlocks sub-accounts after $100,000 of trading volume (see
Sub-accounts).
CreateSubAccount
returns the new address. Move perp USDC in or out with
SubAccountTransfer
and spot tokens with
SubAccountSpotTransfer:
subAccount, err := ex.CreateSubAccount(ctx, hyperliquid.CreateSubAccountAction{Name: "mm-eth"})
if err != nil {
log.Fatal(err)
}
// 250 USDC from the master's perp balance into the sub-account.
err = ex.SubAccountTransfer(ctx, hyperliquid.SubAccountTransferAction{
SubAccountUser: subAccount,
IsDeposit: true,
USD: 250_000_000,
})
if err != nil {
log.Fatal(err)
}
// 10 PURR from the master's spot balance into the sub-account.
err = ex.SubAccountSpotTransfer(ctx, hyperliquid.SubAccountSpotTransferAction{
SubAccountUser: subAccount,
IsDeposit: true,
Token: "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2", // Testnet
Amount: "10",
})
if err != nil {
log.Fatal(err)
}Set IsDeposit: false to move funds back to the master.
SubAccountModify
renames a sub-account. For a sub-account’s own spot/perp moves and
cross-dex transfers, use USDClassTransferAction.SubAccount and
SendAssetAction.FromSubAccount (see Transfers).
Trade as a sub-account
A sub-account has no key of its own. Sign with the master’s key (or an API
wallet the master approved) and pass the sub-account to WithVault:
subEx := ex.WithVault(subAccount)
results, err := subEx.Order(ctx, hyperliquid.OrderAction{Orders: []hyperliquid.Order{order}})
if err != nil {
log.Fatal(err)
}
fmt.Println(results[0])WithVault returns a copy, so ex keeps trading for the master. It applies
to L1 actions (orders, cancels, leverage, …) only; user-signed actions
such as transfers always act for the signer.
Nonces are tracked per signing key, so one API wallet trading several sub-accounts shares one nonce set across them. Hyperliquid recommends a separate API wallet per sub-account (or per trading process).
Read sub-account state
subs, err := info.SubAccounts(ctx, hyperliquid.SubAccountsRequest{User: user})
if err != nil {
log.Fatal(err)
}
for _, s := range subs {
fmt.Println(s.Name, s.SubAccountUser, s.ClearinghouseState.MarginSummary.AccountValue)
}SubAccounts2
returns each sub-account’s state on every perp dex instead of only the main
dex.
Vaults
These actions manage HyperCore vaults, which Hyperliquid now calls legacy vaults: they trade main-dex perps only, without HIP-3 or spot. Newer vaults live on the HyperEVM.
Create and manage a vault
CreateVault
makes the signer the vault’s leader and returns its address. The name is 3
to 50 characters, the description 10 to 250, and the initial deposit at
least 100 USDC.
vault, err := ex.CreateVault(ctx, hyperliquid.CreateVaultAction{
Name: "ETH basis",
Description: "Delta-neutral ETH funding capture.",
InitialUSD: 100_000_000,
})
if err != nil {
log.Fatal(err)
}
// Trade the vault's funds.
vaultEx := ex.WithVault(vault)
_ = vaultEx // vaultEx.Order(...), vaultEx.Cancel(...)
// Stop accepting deposits. Nil fields stay unchanged.
closed := false
err = ex.VaultModify(ctx, hyperliquid.VaultModifyAction{
VaultAddress: vault,
AllowDeposits: &closed,
})
if err != nil {
log.Fatal(err)
}VaultDistribute
pays vault funds out to its followers; USD: 0 closes the vault:
err := ex.VaultDistribute(ctx, hyperliquid.VaultDistributeAction{
VaultAddress: vault,
USD: 1_000_000_000, // 1,000 USDC
})
if err != nil {
log.Fatal(err)
}Deposit and withdraw
Any account can deposit into a vault that allows deposits, and withdraw after the vault’s lock-up:
err := ex.VaultTransfer(ctx, hyperliquid.VaultTransferAction{
VaultAddress: vault,
IsDeposit: true,
USD: 50_000_000, // 50 USDC
})
if err != nil {
log.Fatal(err)
}Read vault state
hlp := hyperliquid.MustParseAddress("0xdfc24b077bc1425ad1dea75bcb6f8158e10df303")
details, err := info.VaultDetails(ctx, hyperliquid.VaultDetailsRequest{
VaultAddress: hlp,
User: &user, // optional: fills FollowerState
})
if err != nil {
log.Fatal(err)
}
if details == nil {
log.Fatal("no such vault")
}
fmt.Println(details.Name, "leader", details.Leader, "APR", details.APR)
if f := details.FollowerState; f != nil {
fmt.Println("my equity", f.VaultEquity, "locked until", time.UnixMilli(f.LockupUntil))
}
equities, err := info.UserVaultEquities(ctx, hyperliquid.UserVaultEquitiesRequest{User: user})
if err != nil {
log.Fatal(err)
}
for _, e := range equities {
fmt.Println(e.VaultAddress, e.Equity)
}VaultDetails
returns nil for an unknown address. APR is a fraction (0.05 is 5%).
LeadingVaults
lists the vaults a user leads, and
VaultSummaries
lists vaults across the exchange.