Order Lifecycle¶
Network & Compatibility
| Resource | Value |
|---|---|
| API base URL | https://api.testnet.sera.cx/api/v1 |
| Chain | Sepolia (chainId 11155111) |
| Sera contract | 0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e |
| Vault contract | 0x3c7945840bAE0d7e7f3824Ebccef1962629250F0 |
| SOR contract | 0x83c1368110B640A729f3810De5FBe94b99aa5668 |
Signing primitives. Every trading mutation is an EIP-712 typed-data signature against the Sera domain. Deposits that take the permit path use the ERC-2612 Permit extension — supported by USDC, EURC, EURT, and most modern stablecoins, not all ERC-20s; call GET /permit/metadata to check support before signing. API-key management uses an EIP-712 ManageApiKey payload.
Tested clients. Python eth_account >= 0.10 + requests; TypeScript ethers v6 (signer.signTypedData). Browser wallets confirmed working with EIP-712 typed data: MetaMask, Rabby, Frame, Coinbase Wallet, Trust, Rainbow. Safe multisigs work via EIP-1271 (the message is verified on-chain rather than via ecrecover).
Address casing. Read endpoints (/balances, /orders, /fills) treat owner_address as case-sensitive — pass the lowercase form. EIP-712 signed payloads accept EIP-55 checksum addresses.
This tutorial walks through the complete lifecycle of a limit order on Sera — from placing the order to claiming proceeds.
Overview¶
sequenceDiagram
participant User
participant API as Sera API
participant Chain as Ethereum
User->>API: POST /orders (signed order)
API-->>User: order_id
Note over API: Order is matched<br/>when counterparty found
API->>Chain: Settlement on-chain
User->>API: GET /orders/{id}
API-->>User: Status: settled
Note over User: Proceeds available<br/>in vault balance Step 1: Check Server Time¶
Before creating signatures, sync with the server clock to avoid expiration issues:
Every signed order requires expiration, and the API enforces now < expiration <= now + 365 days - 300 seconds. Use server time, not only the browser clock, when you derive that field.
Step 2: Query Available Tokens¶
Get the list of supported tokens to find the contract addresses you need:
Step 3: Place a Limit Order¶
Use the Market Maker Guide setup and single-order helper, including its market selection. BASE and QUOTE come from the returned market. The helper previews, signs the returned normalized fields, saves the exact request before sending and stops on errors. Here the price 1 is an example in quote per base; select a price appropriate to that market.
Step 4: Monitor Order Status¶
Use your API key to poll the order status:
status = requests.get(
f"https://api.testnet.sera.cx/api/v1/orders/{order_id}",
headers={"Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET"},
timeout=10,
).json()
print("Status:", status["status"])
# External statuses: "pending", "matched", "settled", "cancelled", "failed"
print("Filled:", status["filled_amount"])
print("Signed uuid_int:", status["uuid_int"])
const statusRes = await fetch(
`https://api.testnet.sera.cx/api/v1/orders/${orderId}`,
{ headers: { "Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET" } },
);
const status = await statusRes.json();
console.log("Status:", status.status);
// External statuses: "pending", "matched", "settled", "cancelled", "failed"
console.log("Filled:", status.filled_amount);
console.log("Signed uuid_int:", status.uuid_int);
Step 5: Check Your Balances¶
Once the order settles, proceeds appear in your vault balance:
balances = requests.get(
"https://api.testnet.sera.cx/api/v1/balances",
params={"owner_address": wallet_address},
headers={"Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET"},
timeout=10,
).json()["balances"]
for bal in balances:
print(f"{bal['symbol']}: vault_raw={bal['vault_available']}, "
f"frozen_raw={bal['vault_frozen']}, decimals={bal['decimals']}")
const balanceRes = await fetch(
`https://api.testnet.sera.cx/api/v1/balances?owner_address=${walletAddress}`,
{ headers: { "Authorization": "Bearer YOUR_API_KEY:YOUR_API_SECRET" } },
);
const { balances } = await balanceRes.json();
for (const bal of balances) {
console.log(`${bal.symbol}: vault_raw=${bal.vault_available}, ` +
`frozen_raw=${bal.vault_frozen}, decimals=${bal.decimals}`);
}
GET /balances now returns raw uint256 decimal strings. Convert them with each row's decimals field before displaying human-readable balances.
Step 6: Cancel an Order (Optional)¶
If you want to cancel an unfilled or partially filled order:
Note
Orders have a configurable cancel cooldown (default 300 seconds). A cooldown rejection returns 429; the current implementation has no high-volume account exemption. Check the response because rate limits also return 429.
Step 7: Withdraw (Optional)¶
To move funds from your vault back to your wallet, use the instant-withdraw flow:
POST /withdrawto obtain the executor co-signature.POST /withdraw/buildto get the unsignedexecuteInstantWithdrawDualSigtransaction.- Sign that transaction locally and broadcast it with
POST /withdraw/send.
If the API is unavailable, use the on-chain emergency withdrawal flow, subject to its request, delay, execution-window and balance rules.
Virtual Liquidity Orders¶
VL orders follow the same lifecycle as standard limit orders, but with shared budget management. When a VL sibling fills, the matching engine automatically amends or cancels the remaining siblings to stay within budget.
Key differences:
- Placement — Use
POST /orders/vl/batchinstead ofPOST /orders - Cancellation — Use
POST /orders/vl/cancelto cancel the entire batch, orPOST /orders/cancelfor individual siblings - Budget tracking — The system tracks a shared frozen balance across all siblings; fills on one sibling reduce the budget available to others
See Virtual Liquidity for the full guide.
Order States Summary¶
| State | Meaning | Can Cancel? |
|---|---|---|
pending | Submitted, resting on the book, or partially filled | Yes |
matched | All legs crossed in the matching engine; on-chain settlement is in flight | No |
settled | Fully filled and chain-confirmed | No |
cancelled | Cancelled before full fill | No |
failed | Rejected or settlement reverted | Usually no; inspect error and error_code |
pending can include a partially filled resting order. For progress displays, do not rely on status alone: read filled_base_amount, filled_quote_amount, remaining_amount, settlement_summary, and /fills. Use settlement_economics.gross_debits and gross_credits for owner balance-movement displays. The public API does not expose protocol spread capture or counterparty transfer splits.