Place order via RISExUniversalRouter. Client signs the permit (server holds no signer keys), the server builds the byte-packed payload and submits it via the Router.
Two auth flows: with a JWT (Authorization: Bearer) the order is executed against the account's OperatorHub allowance and no per-order signature is needed — omit permit. Without a JWT, sign the permit as described below. Everything from here on applies to the permit flow only.
Prerequisites:
- The account must already be registered on-chain; its first collateral deposit registers it. Until then every order fails
FailedPrecondition: account <addr> is not registered (userId 0). The userId registry is fed by chain events, so for a few seconds after that deposit lands the error can still surface intermittently; retry instead of treating it as a signing problem. GET /v1/auth/eip712-domain— EIP-712 domain (name,version,chainId,verifyingContract= Authorization contract)GET /v1/nonce-state/{account}—nonce_anchorandcurrent_bitmap_index(pass it aspermit.nonce_bitmap_index; if 208 the anchor is full — usenonce_anchor+1with bitmap index 0)GET /v1/system/config—routeraddress (RISExUniversalRouter), used as thetargetfield below
Order data — pack the order fields into a uint88, MSB to LSB:
[87:70] marketId (16 bits)
[69:38] sizeSteps (32 bits)
[37:14] priceTicks (24 bits)
[13:6] orderFlags (8 bits)
[5:1] version (5 bits, must be 1)
[0] reserved (1 bit)
orderFlags bits: 0 side (1=Sell), 1 postOnly, 2 reduceOnly, 4:3 stpMode, 5 orderType (0=Market, 1=Limit), 7:6 timeInForce.
Header flags — a separate byte covering which optional fields are present:
0x01 permit (always set), 0x02 builderId != 0, 0x04 clientOrderId != 0, 0x10 ttlUnits != 0.
Action hash:
actionHash = keccak256(abi.encode(
keccak256("RISE_PERPS_PLACE_ORDER_V1"), // bytes32 action selector
uint8(headerFlags),
uint88(orderData),
uint16(builderId),
uint16(builderFeeBps), // ONLY when builder_fee_bps > 0 — omit this slot otherwise
uint64(clientOrderId),
uint16(ttlUnits)))
Permit digest (EIP-712):
VerifyWitness(address account,address target,bytes32 hash,uint48 nonceAnchor,uint8 nonceBitmap,uint32 deadline)
Sign as standard EIP-712 typed data (e.g. ethers signTypedData) with hash = actionHash, target = RISExUniversalRouter address (NOT the OrdersManager, NOT the account, NOT the domain verifyingContract), nonceBitmap = permit.nonce_bitmap_index. Signing against the wrong target recovers a different address on every attempt and fails with SignerNotAuthorized.
Signature: 64-byte EIP-2098 compact (r + yParityAndS) or 65-byte (r+s+v, auto-converted). permit.signature is a protobuf bytes field — send it base64 over REST, not 0x hex.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||