Auction Relayer Reference
Canonical WebSocket channels, payload shapes, validations, acknowledgments, broadcasts, and signature semantics for the Auction Relayer.
For a quick start and a minimal working bot, see Guides → Build a Auction Bot.
Endpoint
- Production:
wss://api.sapience.xyz/auction
(confirm base URL) - Local dev (monorepo):
ws://localhost:3001/auction
Message flow
auction.start
→ relayer repliesauction.ack
and broadcastsauction.started
to all clientsauction.subscribe
→ client subscribes to a specificauctionId
to receiveauction.bids
bid.submit
→ relayer repliesbid.ack
and broadcastsauction.bids
to subscribers of thatauctionId
Auctions are short-lived (~60s TTL). The socket that sends auction.start
is auto-subscribed to that auction channel.
Schemas
auction.start
Client → Relayer
{
type: 'auction.start',
payload: {
maker: string, // 0x... EOA
wager: string, // wei string (>= 1)
resolver: string, // 0x... resolver contract
predictedOutcomes: string[] // bytes strings (non-empty)
}
}
Acknowledgment
{ type: 'auction.ack', payload: { auctionId: string } }
Broadcast to all clients
{ type: 'auction.started', payload: {
auctionId: string,
maker: string,
wager: string,
predictedOutcomes: string[],
resolver: string
}}
auction.subscribe
Client → Relayer
{
type: 'auction.subscribe',
payload: { auctionId: string }
}
If accepted, the relayer will immediately stream current bids, if any:
{ type: 'auction.bids', payload: { auctionId: string, bids: ValidatedBid[] } }
bid.submit
Taker → Relayer
{
type: 'bid.submit',
payload: {
auctionId: string,
taker: string, // 0x... EOA
takerWager: string, // wei string (>0)
takerDeadline: number, // unix seconds (future)
takerSignature: string // 0x... hex (typed signature)
}
}
Acknowledgment to submitting socket
{ type: 'bid.ack', payload: { error?: string } }
Broadcast to auction subscribers of this auctionId
{ type: 'auction.bids', payload: {
auctionId: string,
bids: Array<{
auctionId: string,
takerSignature: string,
taker: string,
takerWager: string,
takerDeadline: number,
}>
}}
Notes
auctionId
is generated by the relayer (crypto.randomUUID()
), with ~60s TTL.- The relayer buffers all valid bids; selection is client-side (e.g., highest
takerWager
among non-expired).
Validation
Server-side checks (summarized from packages/api/src/auction/sim.ts
, helpers.ts
, registry.ts
, and ws.ts
).
Auction payload
wager
must parse to BigInt and be > 0predictedOutcomes
must be an array with ≥1 element, each a non-empty bytes stringresolver
must be provided (0x address expected by downstream)maker
must be a valid0x
address (40 hex)
Bid payload
auctionId
must be a non-empty string of an active auctiontaker
must be a valid0x
address (40 hex)takerWager
must parse to BigInt and be > 0takerDeadline
must be a finite number strictly greater thannow
(unix seconds)takerSignature
must be a hex string starting with0x
and a sensible length
If any check fails, bid.ack
includes an error
string. Common reasons:
invalid_payload
invalid_auction_id
invalid_taker
invalid_taker_wager
invalid_wager_values
quote_expired
invalid_taker_bid_signature_format
auction_not_found_or_expired
Signature semantics (strict mode)
Basic format checks are always applied. When chain and contract addresses are configured, the relayer performs optional EIP-712 verification (see verifyTakerBidStrict
). This check is best-effort: failures are logged but do not reject the bid if basic validation passed.
Typed inner message hash (Solidity-compatible):
// encodeAbiParameters([
// { type: 'bytes' }, // encodedPredictedOutcomes (first entry)
// { type: 'uint256' }, // takerWager
// { type: 'uint256' }, // maker wager
// { type: 'address' }, // resolver
// { type: 'address' }, // maker
// { type: 'uint256' }, // takerDeadline
// ], [ ... ]) → keccak256(inner) → messageHash
EIP-712 domain:
{ name: 'SignatureProcessor', version: '1', chainId, verifyingContract }
Types and primary type:
Approve: [
{ name: 'messageHash', type: 'bytes32' },
{ name: 'owner', type: 'address' },
]
// message: { messageHash, owner: taker }
The taker signs this Approve
typed data; the server verifies with verifyTypedData
from viem
.
Rate limits and sizes
- 100 messages per 10s window; on exceed → close with code
1008
, reasonrate_limited
- Message size > 64,000 bytes → close with code
1009
, reasonmessage_too_large
Expiration and subscriptions
- Auctions expire after ~60 seconds (internal TTL); expired auctions are removed.
- The socket that sends
auction.start
is auto-subscribed to thatauctionId
. - Other clients may call
auction.subscribe
to receiveauction.bids
for thatauctionId
. auction.bids
broadcasts only to subscribers of thatauctionId
.
On-chain flow context
Relayer focuses on off-chain matching and signature validation. On-chain minting and settlement are handled by PredictionMarket.sol
(and related contracts). Key checks performed on-chain at mint
include:
- Maker must be the caller (
MakerIsNotCaller
) takerDeadline
must be in the future (TakerDeadlineExpired
)- Non-zero collaterals and above minimum (
MakerCollateralMustBeGreaterThanZero
,TakerCollateralMustBeGreaterThanZero
,CollateralBelowMinimum
) - Non-empty
encodedPredictedOutcomes
(InvalidEncodedPredictedOutcomes
) - Taker signature validity (EOA or ERC-1271) over the same preimage described above (
InvalidTakerSignature
) - Market validation via resolver (
InvalidMarketsAccordingToResolver
)
Both parties must set ERC-20 approvals to allow the contract to pull their collateral.
Example messages
auction.start
{
type: 'auction.start',
payload: {
maker: '0xMaker123...',
wager: '1000000000000000000',
resolver: '0xResolver456...',
predictedOutcomes: ['0xabc123']
}
}
bid.submit
{
type: 'bid.submit',
payload: {
auctionId: 'c6b2d5bb-...-1f25',
taker: '0xTaker789...',
takerWager: '500000000000000000',
takerDeadline: Math.floor(Date.now()/1000) + 60,
takerSignature: '0x' + '11'.repeat(32) + '22'.repeat(32)
}
}
Implementation pointers
- See
packages/api/src/auction/botExample.ts
for a working taker reference - Message and type definitions live in
packages/api/src/auction/types.ts
- Server behavior:
ws.ts
,registry.ts
,sim.ts
,helpers.ts