Standalone off-chain trading engine for RetroPick prediction markets (V3). Implements LS-LMSR pricing, session state, checkpoint building, and finalizer integration. No Nitrolite or Yellow Network required.
| Component | Role |
|---|---|
| Trading | LS-LMSR pricing; BuyShares, SwapShares; in-memory session state |
| Checkpoint | Build signed payloads (operator + user EIP-712); serve CRE workflow via GET/POST /cre/checkpoints/:sessionId |
| Chain Client | latestNonce sync, finalizeCheckpoint, cancelPendingCheckpoint via ChannelSettlement ABI |
The relayer does not send checkpoint submit transactions. It prepares signed data; the CRE workflow delivers it via Chainlink Forwarder → CREReceiver → ChannelSettlement. The relayer can optionally submit finalizeCheckpoint after the 30 min challenge window via POST /cre/finalize/:sessionId.
- LS-LMSR Pricing: Cost function, prices, BuyShares, SwapShares
- Session State: q, balances, positions, nonce, lastTradeAt, prevStateHash
- Trading API:
POST /api/trade/buy,POST /api/trade/swapwith maxCost, minShares, maxOddsImpact - CRE Endpoints:
GET /cre/sessions,GET /cre/sessions/:id,GET /cre/checkpoints,GET /cre/checkpoints/:sessionId,POST /cre/checkpoints/:sessionId,POST /cre/finalize/:sessionId - Checkpointing: Chain nonce sync, EIP-712 signatures, lastTradeAt tracking
- Finalizer: Optional
POST /cre/finalize/:sessionIdto submit finalizeCheckpoint tx (permissionless; relayer convenience)
npm installCopy .env.example to .env:
| Variable | Purpose |
|---|---|
CHANNEL_SETTLEMENT_ADDRESS |
Required — ChannelSettlement contract (e.g. Fuji: 0xFA5D0e64B0B21374690345d4A88a9748C7E22182) |
OPERATOR_PRIVATE_KEY |
Required for checkpoint signing (must match contract OPERATOR) |
FINALIZER_PRIVATE_KEY |
Optional — for POST /cre/finalize; defaults to OPERATOR_PRIVATE_KEY |
RPC_URL |
Required — for nonce sync and finalizer |
CHAIN_ID |
Chain ID (Fuji: 43113) |
npm run devPOST /api/session/create– Create session (sessionId, marketId, vaultId, numOutcomes, b, resolveTime?, riskCaps?, b0?, alpha?)POST /api/session/credit– Credit user balance (for testing)GET /api/session/:sessionId– Session metadata (q, bParams, nonce, riskCaps, etc.)GET /api/session/:sessionId/account/:address– User balance, positions, initialBalanceGET /api/session/:sessionId/quote– Pre-trade quote:?type=buy&outcomeIndex=0&delta=10or?type=swap&fromOutcome=0&toOutcome=1&delta=10GET /api/session/:sessionId/prices– Current marginal price vector
POST /api/trade/buy– BuyShares (sessionId, outcomeIndex, delta, maxCost?, minShares?, maxOddsImpactBps?, userAddress)POST /api/trade/swap– SwapShares (sessionId, fromOutcome, toOutcome, delta, maxCost?, minReceive?, userAddress)POST /api/trade/sell– SellShares (sessionId, outcomeIndex, delta, minReceive?, maxOddsImpactBps?, userAddress)
Pass riskCaps in POST /api/session/create:
maxOI– Max open interest Σ max(0, q_i)maxPosPerUser– Max position per outcome per user (in outcome units)maxOddsImpactBps– Session default for maxOddsImpact
GET /cre/sessions– Sessions ready for finalization (resolveTime <= now)GET /cre/markets– Session-to-marketId mapping for frontend alignmentGET /cre/sessions/:sessionId– Session payload for legacy SessionFinalizer pathGET /cre/checkpoints– Checkpoint metadata list (sessionId, marketId, nonce, hasDeltas)GET /cre/checkpoints/:sessionId– Checkpoint spec for ChannelSettlement (digest, users, deltas); syncs nonce from chainPOST /cre/checkpoints/:sessionId– Build full payload; body{ userSigs: { [address]: "0x..." } }; returns0x03-prefixed payload for CREPOST /cre/finalize/:sessionId– SubmitfinalizeCheckpointtx (permissionless; requires RPC_URL and FINALIZER_PRIVATE_KEY or OPERATOR_PRIVATE_KEY)
- Balance and positions use 1e6 scaling (6 decimals). One unit = 1,000,000 internal units.
- Delta conversion: Checkpoint deltas use
int128for sharesDelta and cashDelta. The relayer passes values in 1e6 scale; the contract may expect the same (verify OutcomeToken1155 / vault decimals). - Overflow:
int128max ≈ 2^127; 1e6 × typical amounts is safe for most use cases.
The relayer uses ChannelSettlement.json from packages/contracts/docs/abi/. After contract changes, run:
npm run sync-abiTest the deployed relayer API with a simple create → credit → buy flow:
npm run test:payload
# Or with custom URL:
npx tsx scripts/testPayload.ts https://your-relayer.up.railway.appnpm run test
npm run test:coverage
npm run test:integration # Integration tests only (requires RPC + deployed contract)Unit tests cover LMSR, session store, checkpoint building, and API routes. Gasless trading tests run without RPC:
test/integration/anvilGaslessTrading.test.ts– Create session, credit, buy/sell/swap (no chain; proves gasless)
Full integration tests require Anvil + DeployAnvilRelayerTest (creates market, funds users):
- Start Anvil:
anvil - Deploy:
cd packages/contracts && source .env.anvil.example && forge script script/DeployAnvilRelayerTest.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - Configure relayer: Create
apps/relayer/.envfrom.env.anvil.example. Set from deploy output:CHANNEL_SETTLEMENT_ADDRESSMARKET_ID(0)OUTCOME_TOKEN_ADDRESS(optional; for on-chain balance assertions)
- Run:
cd apps/relayer && npm run test:integration
Integration tests (skip when env not set):
contractIntegration.test.ts–readLatestNonce,finalizeCheckpoint(NoPending revert, success after submit+warp)anvilTradingFlow.test.ts– Full E2E: create → trade → submit → warp → finalize → verify OutcomeTokenanvilResolution.test.ts– Past resolveTime, nonce incrementsanvilChallengeWindow.test.ts– Finalize reverts before 30 min warp, succeeds afteranvilMultiUser.test.ts– Two users, single checkpointanvilSwapAndSell.test.ts– Buy, swap, sell pathsanvilE2E.test.ts– Legacy flow (checkpoint spec → build payload)
Alternative deploy: DeployBetaTestnet.s.sol for full beta stack (no market created; use MARKET_ID=0 if you seed a market separately).
| Doc | Description |
|---|---|
| docs/DEPLOY_PAAS.md | PaaS deployment (Railway, Render) — 24/7 hosting |
| packages/contracts/docs/abi/docs/relayer/RelayerArchitecture.md | Architecture and lifecycle |
| packages/contracts/docs/abi/docs/cre/CREWorkflowCheckpoints.md | How CRE workflow fetches and delivers checkpoints |