Authentication & Signing
Authentication & Signing
Every write to the Perps API is authorized by an EIP-712 signature carried in the request body. There are two signer roles:
A trading key is a separate keypair you generate locally. Your main wallet authorizes it once; after that, the agent key signs all trading actions. If it leaks, the attacker can trade on your account but cannot withdraw — and you can revoke it instantly.
The EIP-712 domain
All typed data shares one domain — note there is no verifyingContract and no salt:
- For trading actions (Order, Cancel, Modify, UpdateIsolatedMargin),
chainIdis pinned per environment — 999 for mainnet, 998 for testnet — regardless of your connected wallet’s chain. For wallet-signed actions (AuthorizeAgent, RevokeAgent, Withdraw) the server is chain-agnostic: sign with any chainId > 0 and send the same value assignature_chain_idin the body. Using 999 everywhere on mainnet is simplest. hyperflowEnvironmentfields carry the literal string"Mainnet"or"Testnet"as a cross-environment replay guard.
Encoding rules
These rules must match exactly or signature recovery fails:
- Prices, sizes, and amounts are signed as
string(hashed as UTF-8), not integers. The server hashes the body strings verbatim — so the string you sign must be byte-identical to the string you send. Use canonical decimals (no trailing zeros, no exponent:"0.5", never"0.50"or"5e-1") to avoid mismatches; the app normalizes withBigNumber.toFixed(). nonceis a Unix-millisecond timestamp (uint256), single-use per wallet. Monotonically increase it when sending bursts. Its JSON carriage type varies per endpoint (a string in order/cancel/modify/withdraw bodies, a number in register/revoke/margin) — follow each endpoint’s example.accountisbytes32(the 32-byte account id) inOrder,Modify, andUpdateIsolatedMargin— butaddress(the 20-byte wallet) inCancel,AuthorizeAgent,RevokeAgent, andWithdraw.- Signatures are 65-byte
r || s || vhex (v= 27/28), sent either as onesignaturestring or split{r, s, v}depending on the endpoint — each endpoint’s schema states which.
Creating a trading key
Sign AuthorizeAgent with your main wallet
Typed data — AuthorizeAgent(address user,address agent,string hyperflowEnvironment,uint256 expiresAt,uint256 nonce):
Register it
expires_at is required (absent or 0 is rejected) and must byte-match the signed expiresAt. Optional fields: name (a label, ≤64 chars) and key_hint (≤32 chars).
Returns data.expires_at (Unix ms). Responses: 400 bad body/env/chain/signature or missing expires_at; 403 the wallet is not on the alpha whitelist; 409 replayed nonce or the agent address is already in use.
Managing trading keys
The app mints trading keys with a 7-day expiry; the API accepts a signature-bound expiry of up to 180 days. Re-registering the same agent refreshes its expiry.
Next: Orders