Skip to content

Transfers

Moving funds between addresses, between the perp and spot balances, across perp dexes and off Hyperliquid. Every method here returns only an error; check the result with InfoClient.ClearinghouseState, SpotClearinghouseState or UserNonFundingLedgerUpdates.

Which key signs

Transfers are user-signed actions: they must be signed by the account’s own key, not an API (agent) wallet. An agent wallet can trade but cannot move funds; the exchange rejects a transfer it signs. See Signing and keys.

owner, err := hyperliquid.NewPrivateKeySigner(os.Getenv("HL_ACCOUNT_KEY"))
if err != nil {
	log.Fatal(err)
}
ex := hyperliquid.NewExchangeClient(hyperliquid.Testnet, owner)

ExchangeClient.WithVault does not apply to user-signed actions; they always act for the signer. To move a sub-account’s funds, use the sub-account fields shown below.

Amounts

The transfers on this page take amounts as Decimal strings in token units: "25" or "0.5". Some other actions take integer fields instead, documented on each field:

FieldUnitUsed by
Amount DecimalToken units, e.g. "25.5" USDCUSDSend, SpotSend, Withdraw, USDClassTransfer, SendAsset, SubAccountSpotTransfer
USD uint64USDC × 10⁶ (25_500_000 = 25.5 USDC)SubAccountTransfer, VaultTransfer, VaultDistribute, CreateVault (InitialUSD)
Wei uint64HYPE × 10⁸CDeposit, CWithdraw, TokenDelegate

Send USDC

USDSend sends USDC from your perp balance to another address on Hyperliquid:

err := ex.USDSend(ctx, hyperliquid.USDSendAction{
	Destination: hyperliquid.MustParseAddress("0x0000000000000000000000000000000000000001"),
	Amount:      "10",
})
if err != nil {
	log.Fatal(err)
}

Send a spot token

SpotSend names the token as "NAME:0x<tokenId>". Build it from SpotMeta:

spot, err := info.SpotMeta(ctx)
if err != nil {
	log.Fatal(err)
}
var purr string
for _, t := range spot.Tokens {
	if t.Name == "PURR" {
		purr = t.Name + ":" + t.TokenID
	}
}

err = ex.SpotSend(ctx, hyperliquid.SpotSendAction{
	Destination: destination,
	Token:       purr,
	Amount:      "100",
})
if err != nil {
	log.Fatal(err)
}

Withdraw to Arbitrum

Withdraw sends USDC from the perp balance through the bridge to an address on Arbitrum. A fixed fee is deducted from the amount.

err := ex.Withdraw(ctx, hyperliquid.WithdrawAction{
	Destination: owner.Address(),
	Amount:      "50",
})
if err != nil {
	log.Fatal(err)
}

Move USDC between perp and spot

USDClassTransfer moves USDC between your spot and perp balances. ToPerp picks the direction:

// Spot to perp.
err := ex.USDClassTransfer(ctx, hyperliquid.USDClassTransferAction{Amount: "20", ToPerp: true})
if err != nil {
	log.Fatal(err)
}

// Perp to spot, on one of your sub-accounts.
err = ex.USDClassTransfer(ctx, hyperliquid.USDClassTransferAction{
	Amount:     "20",
	ToPerp:     false,
	SubAccount: &subAccount,
})
if err != nil {
	log.Fatal(err)
}

Move tokens across dexes, spot and accounts

SendAsset is the general transfer: any token, any source and destination balance, to yourself or another address. SourceDex and DestinationDex name the balance:

  • "": the main perp dex
  • "spot": the spot balance
  • any other name: a builder-deployed (HIP-3) perp dex, such as "xyz"

When a perp dex is involved, the token must be that dex’s collateral token.

usdc := "USDC:0xeb62eee3685fc4c43992febcd9e75443" // Testnet; see SpotMeta

// Fund the Testnet "test" dex (USDC-collateralized) from your main perp balance.
err := ex.SendAsset(ctx, hyperliquid.SendAssetAction{
	Destination:    owner.Address(),
	SourceDex:      "",
	DestinationDex: "test",
	Token:          usdc,
	Amount:         "100",
})
if err != nil {
	log.Fatal(err)
}

// Send spot USDC from a sub-account to another address.
err = ex.SendAsset(ctx, hyperliquid.SendAssetAction{
	Destination:    destination,
	SourceDex:      "spot",
	DestinationDex: "spot",
	Token:          usdc,
	Amount:         "5",
	FromSubAccount: &subAccount,
})
if err != nil {
	log.Fatal(err)
}

AgentSendAsset takes the same fields but is an L1 action that an API wallet may sign.

Sub-account USDC and spot transfers have their own actions; see Vaults and sub-accounts.

Move spot tokens to the HyperEVM

A spot token linked to an ERC-20 contract moves to the HyperEVM when you send it to the token’s system address: 0x20 followed by zeros and the token index in big-endian (HYPE uses 0x2222…2222). The tokens arrive at the same address on the EVM side.

var system hyperliquid.Address
system[0] = 0x20
binary.BigEndian.PutUint64(system[12:], uint64(tokenIndex)) // SpotTokenMeta.Index

err := ex.SendAsset(ctx, hyperliquid.SendAssetAction{
	Destination:    system,
	SourceDex:      "spot",
	DestinationDex: "spot",
	Token:          token, // "NAME:0x<tokenId>"
	Amount:         "1",
})
if err != nil {
	log.Fatal(err)
}

See HyperCore ↔ HyperEVM transfers for linking and the caveats.

Send to a contract with data

SendToEVMWithData sends a token from HyperCore to a contract that implements ICoreReceiveWithData, passing it a payload. Token is the plain token name here, and DestinationRecipient is encoded per AddressEncoding:

err := ex.SendToEVMWithData(ctx, hyperliquid.SendToEVMWithDataAction{
	Token:                "USDC",
	Amount:               "1",
	SourceDex:            "",
	DestinationRecipient: "0x5e9ee1089755c3435139848e47e6635505d5a13a",
	AddressEncoding:      hyperliquid.AddressEncodingHex,
	DestinationChainID:   42161,
	GasLimit:             200000,
	Data:                 []byte{0xde, 0xad, 0xbe, 0xef},
})
if err != nil {
	log.Fatal(err)
}
Last updated on