Zero to HYPE

T3 · Foundations

Signing and sending

Where most bots break. Two signing schemes, and a handful of encoding traps that make the exchange recover the wrong address.

LessonChecked against the live API on 22 Oct 2026

Read first

The official docs for this track.

Then

Tutorials and write-ups worth your time.

Build

Hands-on work for this track.

Two signing schemes

Every POST /exchange carries an action, a nonce and a signature. How you sign depends on the action.

The request itself looks like this:

{
  "action": { "type": "order", "orders": [ ... ], "grouping": "na" },
  "nonce": 1792600000000,
  "signature": { "r": "0x...", "s": "0x...", "v": 27 },
  "vaultAddress": null,
  "expiresAfter": null
}

A wrong signature doesn’t say what’s wrong

The exchange recovers a signer from your signature and payload. If anything differs from what it hashes, it recovers some other address and answers with an error such as:

User or API Wallet 0x7e3a… does not exist.
Must deposit before performing actions. User: 0x7e3a…

The address in the message isn’t yours, and it changes every time you change the input. Nothing tells you which byte was wrong. These are the causes we’ve seen most, from the docs and from fixing them in hypersdk:

Mistake Why it breaks the hash
Using the wrong scheme An L1 action signed as user-signed data, or the reverse
Fields in a different order msgpack keeps field order, so {a, b} and {b, a} hash differently
null instead of leaving a field out A null is encoded; a missing field isn’t
Trailing zeros, such as "1.50" Send "1.5"; the SDKs strip trailing zeros before signing
Upper-case addresses Lower-case every address before signing
Addresses as 20 raw bytes The exchange hashes them as lower-case hex strings
HIP-3 or HIP-4 lists not sorted Deployer actions expect lists sorted by their first element, before signing
Trusting a local recover Recovering your own address locally proves nothing, because you rebuilt the payload yourself

Use the error as a test

That unhelpful error is also the best test you have. Generate a brand-new key, sign an action with it, and send it to testnet. The new key has no account, so the action is always rejected, but the error names the address the exchange recovered:

No funds are involved and nothing can take effect. hyperkeeper runs this check every time it starts, and the Sign without money lab will let you try it in the browser.

Errors that aren’t about signing

When the signature is fine, an order can still be rejected. Batched orders and cancels return one status per item. These are the errors you’ll meet first:

Error Meaning
Tick The price has too much precision (see T2)
MinTradeNtl Order value under $10
PerpMargin Not enough margin to open it
BadAloPx A post-only order would have matched immediately
IocCancel An immediate-or-cancel order found nothing to match
Oracle Price too far from the oracle
MissingOrder Cancelling an order that was never placed, already filled or already cancelled

Some errors depend only on the payload, such as an empty batch. Those come back once for the whole batch, so a batch of 20 orders can return a single error.

New accounts pay to activate

The first transfer into a brand-new HyperCore account costs 1 unit of the quote token (1 USDC, for example), whatever asset is sent. An account that has never been activated can’t send CoreWriter actions from the HyperEVM, which catches contract developers out.

Order types, briefly

Limit orders take a time-in-force:

A client order ID (cloid) is an optional 128-bit hex string you choose, so you can cancel or look up an order without waiting for its oid. T4 covers triggers, TWAPs and the rest.