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
| Field | Type | Description |
|---|---|---|
id | string | Snowflake ID |
chain | string | Blockchain network (currently only "HYPERLIQUID") |
address | string | Wallet address on the chain |
Supported Chains
| Chain | Value | Description |
|---|---|---|
| HyperLiquid | "HYPERLIQUID" | Auto-created on first wallet fetch |
Error Responses
| Status | Error | Cause |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid access token |
| 500 | INTERNAL_SERVER_ERROR | Failed 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
chainto"HYPERLIQUID"if omitted. - Log every signature to
glyde.sign_logfor 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
| Field | Type | Description |
|---|---|---|
action | object | The HyperLiquid action object — sent verbatim to /exchange after signing. |
nonce | int64 | Unix-ms nonce. Must be within (T - 2d, T + 1d) where T is current time. |
vaultAddress | string | null | Optional vault address. Omit for personal account. |
expiresAfter | int64 | null | Optional action expiry nonce. |
broadcast | bool | If true, the backend POSTs the signed payload to HyperLiquid and returns the venue response. |
userSigned | bool | Switch to user-signed EIP-712 domain (HyperliquidSignTransaction). Required for withdraws/transfers/etc. |
primaryType | string | EIP-712 primary type. Required when userSigned is true. |
payloadTypes | array | EIP-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)
| Status | Error | Cause |
|---|---|---|
| 400 | INVALID_REQUEST_BODY | Body is not valid JSON or missing required fields |
| 400 | INVALID_MESSAGE | Empty message or invalid hex when encoding=hex |
| 400 | INVALID_TYPED_DATA | Missing primaryType or types |
| 400 | INVALID_ACTION | Empty action, missing/invalid nonce, or malformed JSON |
| 401 | UNAUTHORIZED | Missing or invalid access token |
| 404 | WALLET_NOT_FOUND | User has no wallet for the requested chain |
| 429 | TOO_MANY_REQUESTS | Exceeded the per-user 5/s sign rate |
| 500 | SIGN_FAILED | Internal signing error (encryption / key issue) |
| 502 | BROADCAST_FAILED | Could 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.