Skip to main content

Wallets

Endpoints for managing user wallets. All endpoints require authentication.

GET /v1/wallet/all

Returns all wallets belonging to the authenticated user. If the user doesn't have a HyperLiquid wallet yet, one is automatically created on first request.

Headers

Authorization: Bearer <access_token>

Response

{
"status": 200,
"data": [
{
"id": "1234567890123456789",
"chain": "HYPERLIQUID",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD38"
}
]
}

Wallet Object

FieldTypeDescription
idstringSnowflake ID
chainstringBlockchain network (currently only "HYPERLIQUID")
addressstringWallet address on the chain

Supported Chains

ChainValueDescription
HyperLiquid"HYPERLIQUID"Auto-created on first wallet fetch

Error Responses

StatusErrorCause
401UNAUTHORIZEDMissing or invalid access token
500INTERNAL_SERVER_ERRORFailed to create or fetch wallets

Notes

  • The private key is never exposed in the API response. Generated on first GET request if not available.
  • The wallet list may grow as more chains are supported.

Signing endpoints

The frontend never holds the private key. To produce a signature, send the message/action to one of the endpoints below. The backend looks up the user's wallet, signs server-side, and (optionally) broadcasts to HyperLiquid.

All sign endpoints:

  • Require Authorization: Bearer <access_token>.
  • Are rate-limited to 5 req/s per user (in addition to the global 1200/min limit). Configurable via SIGN_RATE_LIMIT_PER_SEC.
  • Default chain to "HYPERLIQUID" if omitted.
  • Log every signature to glyde.sign_log for audit (only the payload hash is stored, not the payload itself).

POST /v1/wallet/sign/message — EIP-191 personal_sign

Signs the EIP-191 prefixed digest: keccak256("\x19Ethereum Signed Message:\n" + len(message) + message).

Request

{
"chain": "HYPERLIQUID",
"message": "Login nonce: abc123",
"encoding": "utf8"
}

encoding is "utf8" (default) or "hex". When "hex", message must be a 0x-prefixed hex string and the raw bytes are signed.

Response

{
"status": 200,
"data": {
"address": "0x...",
"signature": "0x<r||s||v, 65 bytes>",
"hash": "0x<keccak256 of the prefixed message>"
}
}

POST /v1/wallet/sign/typed-data — generic EIP-712

Signs an arbitrary EIP-712 payload. The frontend builds the full domain/types/message; the backend signs as-is.

Request

{
"chain": "HYPERLIQUID",
"typedData": {
"domain": { "name": "MyApp", "version": "1", "chainId": 1 },
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" }
],
"Mail": [
{ "name": "from", "type": "string" },
{ "name": "contents", "type": "string" }
]
},
"primaryType": "Mail",
"message": { "from": "alice", "contents": "hi" }
}
}

Response

{
"status": 200,
"data": {
"address": "0x...",
"signature": "0x<65 bytes>",
"hash": "0x<EIP-712 digest>"
}
}

POST /v1/wallet/sign/hl-action — HyperLiquid action

Signs a HyperLiquid action and (optionally) broadcasts it to /exchange. Handles both L1 actions (orders, cancels, etc.) and user-signed actions (withdraws, transfers, agent approvals).

Request — L1 action (default)

{
"action": {
"type": "order",
"orders": [{ "a": 0, "b": true, "p": "30000", "s": "0.01", "r": false, "t": { "limit": { "tif": "Gtc" } } }],
"grouping": "na"
},
"nonce": 1735689600000,
"vaultAddress": null,
"expiresAfter": null,
"broadcast": true
}

Request — user-signed action (withdraw, usdSend, approveAgent, …)

{
"action": {
"type": "withdraw3",
"hyperliquidChain": "Mainnet",
"signatureChainId": "0xa4b1",
"destination": "0x...",
"amount": "10",
"time": 1735689600000
},
"nonce": 1735689600000,
"userSigned": true,
"primaryType": "HyperliquidTransaction:Withdraw",
"payloadTypes": [
{ "name": "hyperliquidChain", "type": "string" },
{ "name": "destination", "type": "string" },
{ "name": "amount", "type": "string" },
{ "name": "time", "type": "uint64" }
],
"broadcast": true
}

Body fields

FieldTypeDescription
actionobjectThe HyperLiquid action object — sent verbatim to /exchange after signing.
nonceint64Unix-ms nonce. Must be within (T - 2d, T + 1d) where T is current time.
vaultAddressstring | nullOptional vault address. Omit for personal account.
expiresAfterint64 | nullOptional action expiry nonce.
broadcastboolIf true, the backend POSTs the signed payload to HyperLiquid and returns the venue response.
userSignedboolSwitch to user-signed EIP-712 domain (HyperliquidSignTransaction). Required for withdraws/transfers/etc.
primaryTypestringEIP-712 primary type. Required when userSigned is true.
payloadTypesarrayEIP-712 fields for primaryType. Required when userSigned is true.

Response

{
"status": 200,
"data": {
"address": "0x...",
"signature": { "r": "0x...", "s": "0x...", "v": 27 },
"broadcasted": true,
"exchangeStatus": 200,
"exchangeResponse": { "status": "ok", "response": { "type": "order", "data": { "...": "..." } } }
}
}

When broadcast is false (or omitted), broadcasted is false and exchangeStatus/exchangeResponse are absent — the frontend can submit the returned {r,s,v} itself.

Error Responses (sign endpoints)

StatusErrorCause
400INVALID_REQUEST_BODYBody is not valid JSON or missing required fields
400INVALID_MESSAGEEmpty message or invalid hex when encoding=hex
400INVALID_TYPED_DATAMissing primaryType or types
400INVALID_ACTIONEmpty action, missing/invalid nonce, or malformed JSON
401UNAUTHORIZEDMissing or invalid access token
404WALLET_NOT_FOUNDUser has no wallet for the requested chain
429TOO_MANY_REQUESTSExceeded the per-user 5/s sign rate
500SIGN_FAILEDInternal signing error (encryption / key issue)
502BROADCAST_FAILEDCould not reach HyperLiquid; the signature was produced but not submitted

Note: a non-2xx response from HyperLiquid is not an error from this API — the venue's status and body are surfaced as exchangeStatus / exchangeResponse so the frontend can handle "rejected order" vs. "transport failure" distinctly.