Flow
- Your service opens a persistent WebSocket to the Convallax relay (same host/port as REST).
- When an end-user submits
POST /rfq, the relay broadcasts onerfqmessage to every connected maker. - Before
timeoutMselapses (see env), reply withquotereferencing the samebroadcastId. - The relay merges your quote with Convallax's internal model quote (and every other maker) and selects the winning price for the taker rule below.
- If you win, the taker accepts via
POST /execute; the relay signs an on-chain settlement order and the user's wallet callsfill()on ConvallaxRFQSettlement.
Endpoints
GET http://HOST:PORT/maker/v1/status
JSON: protocol version + count of sockets (for ops).
WS ws://HOST:PORT/maker/v1/ws
Optional: ?apiKey=… when MAKER_API_KEY is set server-side.
Optional: ?apiKey=… when MAKER_API_KEY is set server-side.
POST http://HOST:PORT/rfq
Same envelope the dashboard sends: EIP-712 typed-data signature (signatureEncoding: "eip712"), relay verifies signer = payload.wallet, plus nested payload. Response includes best quote plus meta:
{
"success": true,
"quote": {
"quote_id": "b2c98e2c-…",
"rfq_id": "rfq-…",
"side": "buy",
"price": 0.182,
"size": 100,
"fairValue": 0.175,
"spread_bps": 412,
"greeks": { "delta": 0.62, "gamma": -0.01, "vega": 0.004, "theta": -0.002 },
"expires_in_ms": 2000
},
"meta": {
"broadcastId": "…",
"rfqDeadlineMs": 800,
"makersConnected": 1,
"quotesReceivedExternal": 1,
"winningSource": "external",
"winningMakerId": "mm1-demo"
}
}Relay → maker
First frame after connect: {"type":"connected",…}. Each RFQ:
{
"type": "rfq",
"broadcastId": "6d2c7b3a-…",
"deadlineIso": "2026-05-08T14:03:09.881Z",
"timeoutMs": 800,
"envelope": {
"signature": "0x…",
"signatureEncoding": "eip712",
"payload": {
"version": 2,
"rfqId": "rfq-…",
"wallet": "0x…",
"createdAt": "2026-05-08T14:03:09.081Z",
"market": {
"conditionId": "0x…",
"yesTokenId": "12345…",
"question": "Will …?"
},
"option": {
"optionType": "call",
"strikeBps": 50,
"strike": 0.5,
"expiry": "May 31",
"expiryMs": 1717189200000,
"tauDays": 21.5,
"sigmaL": 2,
"currentYesPrice": 0.48,
"isResolutionExpiry": false
},
"trade": { "side": "buy", "size": 100 }
}
}
}Maker → relay
{
"type": "quote",
"broadcastId": "6d2c7b3a-…",
"makerId": "your-firm-mm1",
"quote": {
"quote_id": "client-generated-uuid",
"rfq_id": "same-as-envelope.payload.rfqId",
"side": "buy",
"price": 0.18,
"size": 100,
"fairValue": 0.175,
"spread_bps": 420,
"greeks": { "delta": 0.61, "gamma": -0.01, "vega": 0.004, "theta": -0.002 },
"expires_in_ms": 5000
}
}quote.rfq_idmust match the RFQ.quote.sidemirrors the wallet's aggressor direction (buy↔ user lifts the offer,sell↔ user hits the bid).quote.sizemust be ≥ half of payloadtrade.size(soft liquidity check until partial fills ship).priceis quoted per one unit of the option notion in 0–1 probability-dollars, consistent with Convallax's RFQ pane.
Winner selection
Among valid quotes matching rfq_id + side: if the user is buy, the lowest price wins; if sell, the highest wins. Quotes outside (0,1) or mis-sized are rejected.
Environment variables (relay)
| Variable | Meaning |
|---|---|
| MAKER_RFQ_TIMEOUT_MS | Wait window for outbound maker quotes after each inbound RFQ (50–30000). |
| MAKER_API_KEY | If non-empty, require ?apiKey= on the WebSocket URL. |
| RFQ_ALLOWED_ORIGINS | CORS allowlist for browser POST routes (comma-separated). |
MM1 starter (Node)
Drop into a standalone repo pointed at localhost or your staging URL; replace the placeholder pricer with your inventory + model stack.
const WebSocket = require("ws");
const url = process.env.CONVALLAX_WS || "ws://localhost:3001/maker/v1/ws";
const qs = process.env.MAKER_API_KEY ? "?apiKey=" + encodeURIComponent(process.env.MAKER_API_KEY) : "";
const ws = new WebSocket(url + qs);
ws.on("open", () => console.log("maker connected"));
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.type !== "rfq") return;
const p = msg.envelope.payload;
const px = Number(p.option?.currentYesPrice ?? 0.48) + 0.005; // placeholder
ws.send(JSON.stringify({
type: "quote",
broadcastId: msg.broadcastId,
makerId: "MM1-demo",
quote: {
quote_id: crypto.randomUUID(),
rfq_id: p.rfqId,
side: p.trade.side === "sell" ? "sell" : "buy",
price: Math.min(0.99, Math.max(0.01, px)),
size: Number(p.trade.size) || 1,
fairValue: px,
spread_bps: 100,
greeks: { delta: 0.5, gamma: 0, vega: 0.01, theta: 0 },
expires_in_ms: 60_000
}
}));
});1. Breaking WS or quote schema changes bump the documented version alongside server hello payloads.