Skip to main content

Signature Format

Most /exchange actions use a top-level nonce plus a top-level raw ECDSA signature:
{
  "action": {
    /* action details */
  },
  "nonce": 1234567890,
  "expiresAfter": 1234569999,
  "signature": {
    "r": "0x1234...",
    "s": "0x5678...",
    "v": 27
  }
}
  • reportDeposit is unsigned. - acceptTerms uses a top-level hex signature string and does not use a nonce. - approveAgent, trading actions, referral actions such as getReferralCode and createReferralCode, and TWAP actions use the {(r, s, v)} + nonce wrapper shown above. - Withdrawals also use the {(r, s, v)} + nonce wrapper, but the signed payload is the withdrawal action fields. - Withdrawals must be signed by the user account, not by an approved agent wallet.

Nonce

The nonce is an integer used to prevent replay attacks. In practice clients typically use a millisecond timestamp:
const nonce = Date.now();
Each nonce can only be used once. Requests with duplicate nonces will be rejected.

Signature Components

The signature consists of three components (standard ECDSA):
  • r - First 32 bytes of signature (hex string)
  • s - Second 32 bytes of signature (hex string)
  • v - Recovery ID. Most trading actions accept 0-255; withdrawals require 27 or 28.

Creating Signatures

Trading Actions

Orders, cancels, modifies, and leverage updates are verified from the raw action payload plus nonce. Use the Notional SDK or the exact client implementation for canonical action normalization and signing.
const action = {
  type: "order",
  orders: [
    {
      a: "00020000",
      b: true,
      p: "50000.0",
      s: "0.1",
      t: { limit: { tif: "Gtc" } },
    },
  ],
  grouping: "na",
};

const request = {
  action,
  nonce: Date.now(),
  signature: {
    r: "0x...",
    s: "0x...",
    v: 27,
  },
};
Use the Notional SDK for exact typed-data definitions and action normalization.

Terms Acceptance

acceptTerms is the exception to the nonce-based pattern. It sends the typed-data signature as a top-level hex string:
{
  "action": {
    "type": "acceptTerms",
    "termsVersion": "2026-03-01",
    "signedAt": 1701234567890,
    "chainId": 42161
  },
  "signature": "0x..."
}

Withdrawals

Withdrawals still use the outer nonce + { r, s, v } wrapper, but the signed fields live inside action:
{
  "action": {
    "type": "withdraw",
    "notionalChain": "Mainnet",
    "signatureChainId": "0xa4b1",
    "destination": "0x...",
    "token": "00012710",
    "amount": "1000.0",
    "source": "balance",
    "time": 1701234567890
  },
  "nonce": 1701234567890,
  "signature": {
    "r": "0x...",
    "s": "0x...",
    "v": 27
  }
}
signatureChainId selects the EIP-712 domain for signature recovery. The signed withdrawal payload is:
  • notionalChain
  • destination
  • token
  • amount
  • source
  • time

Agent Approval

Instead of signing each request with your main wallet, you can authorize an API wallet (agent) to sign on your behalf.

Benefits

  • Security: Keep your main wallet offline
  • Convenience: No manual signature approval for each trade
  • Automation: Enable trading bots and automated strategies

Approving an Agent

Send an approveAgent action signed by your main wallet:
{
  "action": {
    "type": "approveAgent",
    "hyperliquidChain": "Mainnet",
    "signatureChainId": "0xa4b1",
    "agentAddress": "0x...",
    "agentName": "My Trading Bot",
    "nonce": 1234567890
  },
  "nonce": 1234567890,
  "signature": {
    "r": "0x...",
    "s": "0x...",
    "v": 27
  }
}
Request to:
POST /exchange
Response:
{
  "success": true
}

Using an Approved Agent

Once approved, the agent can sign trading requests on behalf of the user:
  1. Agent creates the action payload
  2. Agent signs the action with its own private key
  3. Backend validates:
    • Signature is valid for agent address
    • Agent is approved for the user
    • Action is authorized
The userAddress is recovered from the approval record, not the signature. Agent wallets can place and manage trades, but they cannot initiate withdrawals.

Validation Process

When a nonce-based signed request arrives, the backend:
  1. Validates signature structure - Checks r, s, v format
  2. Recovers signer address - Uses ECDSA recovery
  3. Checks authorization:
    • If signer = user → Direct mode (approved)
    • If signer = agent → Agent mode (checks approval table)
  4. Verifies nonce - Ensures uniqueness
  5. Validates action - Checks order parameters, margin, etc.

Error Responses

Invalid Signature

{
  "error": "Invalid signature"
}
HTTP Status: 401 Unauthorized

Agent Not Approved

{
  "error": "Agent not approved for user"
}
HTTP Status: 401 Unauthorized

Duplicate Nonce

{
  "error": "Nonce already used"
}
HTTP Status: 400 Bad Request

Security Best Practices

  • Never share your private key - Use hardware wallets for main account - Limit agent permissions - Only approve agents you trust - Monitor agent activity - Review orders placed by agents - Rotate agents - Revoke and re-approve agents periodically - Use unique nonces
  • Ensure timestamp-based nonces are unique