Answer an RFQ
Goal
Turn one inbox item into a bound position. You will pull a directed RFQ, price it, sign the Terms, confirm, and post your margin once the taker binds.
Testnet only. Sonic Testnet, chain 14601.
Reads need a token; the bind needs your signature. Log in to read the inbox. The quote is authorized by the Terms signature, not the token.
Before you start
- Your wallet is approved as a
MAKER, with USDC deposited into your general balance. - You hold a one-hour token from
/auth/challenge→/auth/login. - The relayer base URL and the deployed core address (API reference, ~4 min).
Step 1 — Pull an inbox item
GET /rfq/inbox?maker=<addr> with your token. Pick an item and read its terms-to-be: pair, notional, direction.
const inbox = await fetch(`${RELAYER}/rfq/inbox?maker=${maker}`, {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());
const item = inbox[0];
const rfqId = item.rfq_id ?? item.rfqId;
Each item expires in 30 seconds. Price it inside the window or it falls off.
Step 2 — Compute your price
Set the rate and the margin schedule. The rate is your firm forward quote; the bps are the IM each side posts. This is your edge — size the margin to the pair's gap risk.
Step 3 — Post the quote
POST /rfq/:id/quote. Unsigned — the relayer returns canonical Terms for you to sign.
const priced = await fetch(`${RELAYER}/rfq/${rfqId}/quote`, {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({
quote: {
rfq_id: rfqId,
locked_rate: rate,
im_taker_bps: 200,
im_maker_bps: 100,
threshold_bps: 0,
mta_bps: 10,
tol_bps: 50,
valid_until: nowSecs + 600,
quote_nonce: String(nowSecs),
},
}),
}).then((r) => r.json());
const terms = termsFromWire(priced.terms);
Step 4 — Sign and confirm
Sign the returned Terms (EIP-712), then POST /rfq/:id/confirm. On confirm the relayer anchors your quote on-chain.
const sig = await wallet.signTypedData({
domain: { name: "CRX", version: "1", chainId: 14601, verifyingContract: CRX },
types: TERMS_TYPES,
primaryType: "Terms",
message: terms,
});
await fetch(`${RELAYER}/rfq/${rfqId}/confirm`, {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({ signature: sig }),
});
Step 5 — Allocate once the taker binds
When the taker submits the bind, post your side's IM into the agreement's SCA.
await crx.write.allocate([maId, USDC, imMaker]);
What happens — and the branch
- It binds. The taker fetched the bundle, signed the same Terms, and submitted the bind inside
sigDeadline. You allocate; the position is live and marks against you and for you continuously. - It expires. The taker did not bind before the window closed. Your quote dies. Nothing is owed, nothing is posted. The next poll shows a fresh inbox.
Quote-lock holds while the quote is live: you stand behind it until its validity passes. If the market moves past your price, let it expire — do not re-price under a live quote.
Next: Get Data (~4 min) — read the book you just added to.