Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Signing

Orders, cancels and withdrawal requests are EIP-712 typed data, signed by your account's key or by a key you registered on chain. The gateway checks the signature before anything is sequenced, and for an order the settlement contract checks it again before the fill pays out.

Domain

{ "name": "Hypervolic", "version": "1", "chainId": <the chain's id>, "verifyingContract": "<the settlement contract's address>" }
There is no salt.

Types

Order(address maker,uint64 underlyingKey,uint64 optionSpecifier,uint8 side,uint64 size,uint64 limitPrice,uint64 nonce,uint64 expiresAt,uint8 maxFeeBps) Cancel(address maker,uint64 nonce,uint64 orderNonce) WithdrawalRequest(address user,uint128 amount,uint64 nonce)
The field meanings are on Orders and Account. The signer is not a field: the gateway and the contract recover it.
TypeMay be signed by
OrderThe account, or a key it registered
CancelThe account, or a key it registered
WithdrawalRequestThe account only

Signature format

65 bytes, r‖s‖v, 0x-prefixed hex, with v of 27 or 28. This is what eth_signTypedData_v4, viem and eth-account produce. A signature with a high s is refused.

Nonces

Every order, cancel and withdrawal request carries a nonce chosen by you. For an order it is also the order's identifier: you cancel it by its nonce, and GET /v1/orders/{maker}/{nonce} looks it up.
  • It must be in [1, 2^62), with bits 54–61 zero.
  • Orders, cancels and withdrawal requests share one space per account. Each nonce is used once.
  • The exchange remembers the 4096 nonces below the highest you have used. A nonce further below that is refused as NONCE_BELOW_WINDOW.
So keep nonces increasing. A counter you persist works, and so does a millisecond timestamp with a few low bits for a per-millisecond counter, as long as everything that signs for one account draws from the same sequence.

Signing keys

A trading bot need not hold the key that owns your money. Register a separate key on the settlement contract:
setSigningKey(address key, uint64 expiry) // expiry in unix seconds; 0 revokes
That key may sign orders and cancels and open sessions until expiry. It can never withdraw. An order it signs must expire before the key does. An account holds up to six registered keys.

Example

from eth_account import Account DOMAIN = {"name": "Hypervolic", "version": "1", "chainId": CHAIN_ID, "verifyingContract": CONTRACT} ORDER = {"Order": [ {"name": "maker", "type": "address"}, {"name": "underlyingKey", "type": "uint64"}, {"name": "optionSpecifier", "type": "uint64"}, {"name": "side", "type": "uint8"}, {"name": "size", "type": "uint64"}, {"name": "limitPrice", "type": "uint64"}, {"name": "nonce", "type": "uint64"}, {"name": "expiresAt", "type": "uint64"}, {"name": "maxFeeBps", "type": "uint8"}, ]} def sign(private_key, types, message) -> str: signed = Account.sign_typed_data(private_key, DOMAIN, types, message) return "0x" + bytes(signed.signature).hex() order = { "maker": MAKER, "underlyingKey": 7, "optionSpecifier": 859008923130267914, "side": 0, "size": 25, "limitPrice": 20000, "nonce": 42, "expiresAt": 200003600000, "maxFeeBps": 100, } body = {"order": order, "signature": sign(PRIVATE_KEY, ORDER, order)}
A cancel and a withdrawal request are signed the same way, with their own types and primaryType.

Test vector

To check an implementation, this order, under chain id 31337 and contract 0x000000000000000000000000000000000000cafE:
{ "maker": "0x1111111111111111111111111111111111111111", "underlyingKey": 7, "optionSpecifier": 859008923130267914, "side": 0, "size": 25, "limitPrice": 20000, "nonce": 42, "expiresAt": 200000100000, "maxFeeBps": 100 }
has the EIP-712 digest 0xb1e704016d5ff8c94ef43ef6921ccd796dc79b1de433992f5d092d7e1317bc75.