Place order
Changed onPlace 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_anchor and current_bitmap_index (pass it as permit.nonce_bitmap_index; if 208 the anchor is full — use nonce_anchor+1 with bitmap index 0)
- GET /v1/system/config — router address (RISExUniversalRouter), used as the target field 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.
Request
- Base URLhttps://api.testnet.rise.trade
- Authnone required
- TypeScriptA function with typed inputs. Its response is checked against the schema.
Request body
Example request
{
"client_order_id": "0",
"permit": {
"account": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"signer": "0x1234567890123456789012345678901234567890",
"nonce_anchor": "0"
}
}Response
A successful response.
Example response
{
"order_id": "12345-100-0"
}Changes
- info
added the new optional request property
- info