Quote: Get Started
A desk wired to the API. By the end you will have logged in to read your inbox, pulled a directed RFQ, priced it, signed the Terms, and watched the relayer anchor your quote on-chain.
Operating a desk (running it, risk-managing it, monetizing it) lives in Get Started. This page is the wire.
The whole path, end to end:
Follow every step in order. Nothing branches.
NoteReads need a login; the quote itself is authenticated by your signature. You log in to read your inbox. The bind authority is the Terms signature you produce in step 4.
What you need first
- A wallet approved as a
MAKERon the venue. See API → Get Started (~4 min). - Test USDC deposited into your general balance, and gas. See Fund Your Account (~3 min).
- The relayer base URL and the deployed core address.
Log in to read your inbox
The inbox is a private read. Get a challenge, sign it with personal_sign, exchange it for a one-hour token.
const { message } = await fetch(`${RELAYER}/auth/challenge`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: maker }),
}).then((r) => r.json());
const signature = await wallet.signMessage({ message });
const { token } = await fetch(`${RELAYER}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: maker, signature }),
}).then((r) => r.json());Poll the inbox
GET /rfq/inbox?maker=<addr> with your token. Each item is an RFQ CRX has routed to your desk, on a short expiry timer.
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;WarningEach item expires in 30 seconds. Price it inside the window or it falls off the inbox.
TipTo decline, do nothing. There is no decline endpoint. An unpriced item drops off the inbox when its window passes, and nothing is owed.
Price the request
POST /rfq/:id/quote with your rate and margin schedule. This is 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,
taker: item.taker, // echo the RFQ's identity fields from the inbox
maker,
aca_number: item.aca_number,
instrument: item.instrument, // NDF = "0"
pair: item.pair,
locked_rate: rate, // quote-ccy per USD, scaled
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);Sign the Terms and confirm
Sign the returned Terms (EIP-712), then POST /rfq/:id/confirm with the signature. On confirm the relayer anchors the quote on-chain.
const makerSig = await wallet.signTypedData({
domain: { name: "CRX", version: "1", chainId: 84532, 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: makerSig }),
});TipQuote-lock holds while the quote is live. You stand behind the rate you signed until itsvalid_untilpasses. If the market moves past your price, let it expire. Do not re-price under a live quote.
Allocate margin once bound
When CRX binds, post your side's initial margin into the agreement's SCA.
await crx.write.allocate([acaId, USDC, imMaker]);To withdraw free collateral, deallocation is two-phase.
await crx.write.computeDeallocation([acaId, tokens, amounts]);
await crx.write.deallocate([acaId, token, amount]);WarningDeallocation is exact and time-boxed. Snapshot the free margin, then execute within the five-minute window or it reverts as stale.
What you have now, and the branch
One of two outcomes after you confirm:
- It binds. CRX fetched the bundle, signed the same Terms, and submitted the bind inside
sigDeadline: Leg A, you facing CRX. You allocate; the position is live and marks for you and against you continuously. - It expires. CRX did not bind before the window closed. Your quote dies. Nothing is owed, nothing is posted. The next poll shows a fresh inbox.
From here:
- The inbox model in full: Answer an RFQ (~6 min).
- How your collateral is posted and held: Manage your book (~5 min).
Desk operations
Beyond quoting, a desk owner calls these directly.
Setting up a desk
Onboarding is off-chain signing plus one polling call. Verify the whitelist with a public read, then poll your inbox.
GET /makers
→ [ { "address": "0x…your address…" }, … ]
GET /rfq/inbox?maker=0x…your address…
Authorization: Bearer <jwt>
→ [] // empty until CRX routes an RFQ to you
Assigning roles
Set the signer that acts for the desk by authenticating its wallet; rotate by authenticating a new wallet and discarding the old JWT.
POST /auth/challenge { "address": "0x…signer…" }
POST /auth/login { "address": "0x…signer…", "signature": "0x…" }
→ { "token": "eyJ…", "expires_at": … }
Acting as a sentinel
Poll your firm state to read live exposure before stepping in on a short counterparty.
GET /firm/0x…your address…
→ { "balance": "…", "open_acas": … }