Auction Relayer API
The Auction Relayer is a WebSocket service that facilitates real-time matching between requesters and responders for prediction market positions.
- Requester: Creates an auction by specifying a wager and predictions (including one or many picks). Referred to as
takerin payloads. - Responder: Submits bids offering to take the opposite side. Referred to as
makerin payloads.
The relayer validates messages, broadcasts auctions to all connected clients, and streams bids to subscribed requesters.
Endpoint
wss://api.sapience.xyz/auctionThis is the standard relayer deployment. You can configure a different relayer in the app at sapience.xyz/settings.
Message Flow
Requester Relayer Responder
│ │ │
│─── auction.start ────────▶│ │
│◀── auction.ack ───────────│ │
│ │─── auction.started ───────▶│
│ │ │
│ │◀── bid.submit ─────────────│
│ │─── bid.ack ───────────────▶│
│◀── auction.bids ──────────│ │
│ │ │- Requester sends
auction.startwith wager and picks - Relayer responds with
auction.ack(includesauctionIdand optionalidif provided) and broadcastsauction.startedto all clients - Responders send
bid.submitwith their wager offer and signature - Relayer replies
bid.ackand streamsauction.bidsto the requester
Requesters are auto-subscribed to their auction's bid stream.
Quick Start
Requester: Start an Auction
import WebSocket from 'ws';
const ws = new WebSocket('wss://api.sapience.xyz/auction');
// Initialize an auction
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'auction.start',
payload: {
taker: '0xRequesterAddress...',
wager: '1000000000000000000', // 1 USDe in wei
resolver: '0xResolverContract...',
predictedOutcomes: ['0xEncodedPick1', '0xEncodedPick2'],
takerNonce: 1,
chainId: 42161
}
}));
});
// Listen for bids
ws.on('message', (data) => {
const msg = JSON.parse(String(data));
if (msg.type === 'auction.ack') {
console.log('Auction started:', msg.payload.auctionId);
}
if (msg.type === 'auction.bids') {
const bids = msg.payload.bids;
// Select best bid by highest makerWager among non-expired
const validBids = bids.filter(b => b.makerDeadline > Date.now() / 1000);
validBids.sort((a, b) => BigInt(b.makerWager) > BigInt(a.makerWager) ? 1 : -1);
console.log('Best bid:', validBids[0]);
}
});Responder: Submit a Bid
const ws = new WebSocket('wss://api.sapience.xyz/auction');
// Listen for bids
ws.onmessage = (ev) => {
const msg = JSON.parse(String(ev.data));
if (msg.type === 'auction.started') {
const auction = msg.payload;
const now = Math.floor(Date.now() / 1000);
ws.send(JSON.stringify({
type: 'bid.submit',
payload: {
auctionId: auction.auctionId,
maker: '0xResponderAddress...',
makerWager: (BigInt(auction.wager) / 2n).toString(),
makerDeadline: now + 60,
makerSignature: '0x...', // EIP-712 typed signature
makerNonce: 1
}
}));
}
};Payloads
auction.start
{
type: 'auction.start',
id?: string, // Optional request ID for correlation
payload: {
taker: string, // Requester's address (0x...)
wager: string, // Wager amount in wei
resolver: string, // Resolver contract address
predictedOutcomes: string[], // Encoded picks (bytes strings)
takerNonce: number,
chainId: number
}
}If id is provided, it will be echoed back in the auction.ack response for client-side correlation.
bid.submit
{
type: 'bid.submit',
payload: {
auctionId: string,
maker: string, // Responder's address
makerWager: string, // Responder's wager in wei
makerDeadline: number, // Unix timestamp (must be future)
makerSignature: string, // EIP-712 signature
makerNonce: number
}
}auction.bids
Streamed to auction subscribers:
{
type: 'auction.bids',
payload: {
auctionId: string,
bids: Array<{
auctionId: string,
maker: string,
makerWager: string,
makerDeadline: number,
makerSignature: string,
makerNonce: number
}>
}
}Validation
Auction (auction.start):
predictedOutcomeshas ≥1 non-empty bytes stringtakeris a valid addressresolveris provided
Bid (bid.submit):
auctionIdmatches an active auctionmakerWager> 0makerDeadlineis in the futuremakerSignatureis valid hex format
Additional Messages
auction.subscribe
Subscribe to an existing auction's bid stream without starting a new auction:
{
type: 'auction.subscribe',
payload: {
auctionId: string
}
}This is useful when a secondary client needs to monitor bids for an auction started by another connection.
Connection Limits
- Rate limit: 100 messages per 10 seconds. Exceeding closes connection with code
1008. - Message size: Max 64KB per message. Exceeding closes connection with code
1009.
Error Codes
Returned in bid.ack.payload.error:
invalid_payload– Malformed message structureauction_not_found_or_expired– Unknown or expired auctionquote_expired–makerDeadlinehas passedinvalid_maker_wager– Wager is zero or invalidinvalid_maker– Maker address is invalidinvalid_maker_bid_signature_format– Signature format invalid
Bid Acceptance
After selecting a bid, the requester constructs a MintParlayRequestData struct and calls mint() on the PredictionMarket contract. Both parties must have ERC-20 approvals set for the contract to pull their collateral (USDe).
Reference implementation: packages/api/src/auction/botExample.ts