config.walletAddress);
}
}
**Rationale:** Separating by domain rather than entity prevents authentication leakage and allows independent scaling. Discovery endpoints can remain public and cache aggressively, while execution endpoints enforce strict rate limits and cryptographic validation. Analytics endpoints query historical state without impacting live trading throughput.
### Step 2: Two-Tier Authentication Model
Financial operations require different security ceremonies. Identity establishment should use the strongest available credential, while routine requests should use lightweight session tokens. This prevents private key exposure during high-frequency trading.
```typescript
// auth-layer.ts
import { ethers } from 'ethers';
import { createHmac } from 'crypto';
export class TradingAuthenticator {
// L1: Establish identity via wallet signature
static async deriveTradingCredentials(wallet: ethers.Wallet): Promise<TradingKeys> {
const payload = {
domain: 'trading-platform',
version: '1.0',
chainId: 137,
issuedAt: Date.now()
};
const signature = await wallet.signTypedData(
payload.domain,
payload.version,
payload.chainId,
payload
);
// Server validates signature and returns session credentials
const response = await fetch('/auth/derive-keys', {
method: 'POST',
body: JSON.stringify({ wallet: wallet.address, signature })
});
return response.json(); // { apiKey, apiSecret, passphrase }
}
// L2: Sign routine requests with HMAC
static signExecutionRequest(
payload: string,
credentials: TradingKeys,
timestamp: number
): Record<string, string> {
const message = `${timestamp}${payload}`;
const signature = createHmac('sha256', credentials.apiSecret)
.update(message)
.digest('hex');
return {
'X-WALLET-ADDR': credentials.walletAddress,
'X-REQUEST-SIG': signature,
'X-TIMESTAMP': String(timestamp),
'X-API-KEY': credentials.apiKey,
'X-PASSPHRASE': credentials.passphrase
};
}
}
Rationale: L1 authentication proves wallet ownership through cryptographic signature derivation. This happens infrequently. L2 authentication uses HMAC-SHA256 for every trading request, providing request integrity without exposing the private key. This separation reduces latency, prevents signature replay attacks, and aligns security ceremony with operational risk.
Step 3: Cryptographic Order Submission
Orders in prediction markets are not simple data mutations. They are enforceable financial commitments that must be verifiable off-chain and settleable on-chain. The payload itself carries authorization.
// order-router.ts
import { TypedDataDomain, TypedDataField } from 'ethers';
export interface OrderCommitment {
assetId: string;
price: number;
quantity: number;
side: 'BUY' | 'SELL';
expiry: number;
tickConstraint: string;
negRiskEnabled: boolean;
}
export class OrderRouter {
private readonly signingWallet: ethers.Wallet;
private readonly typedDataSchema: { domain: TypedDataDomain; types: Record<string, TypedDataField[]> };
constructor(wallet: ethers.Wallet) {
this.signingWallet = wallet;
this.typedDataSchema = this.buildEIP712Schema();
}
private buildEIP712Schema(): { domain: TypedDataDomain; types: Record<string, TypedDataField[]> } {
return {
domain: { name: 'PredictionMarket', version: '2.1', chainId: 137 },
types: {
OrderCommitment: [
{ name: 'assetId', type: 'string' },
{ name: 'price', type: 'uint256' },
{ name: 'quantity', type: 'uint256' },
{ name: 'side', type: 'string' },
{ name: 'expiry', type: 'uint64' }
]
}
};
}
async submitCryptographicCommitment(order: OrderCommitment): Promise<ExecutionReceipt> {
// Validate dynamic tick size before signing
const currentTick = await this.fetchMarketTickSize(order.assetId);
if (!this.validateTickPrecision(order.price, currentTick)) {
throw new Error('Invalid tick precision for current market state');
}
// Construct EIP-712 payload
const typedPayload = {
...this.typedDataSchema.domain,
message: {
assetId: order.assetId,
price: Math.floor(order.price * 10000), // Convert to basis points
quantity: Math.floor(order.quantity * 100),
side: order.side,
expiry: order.expiry
}
};
// Cryptographic signature binds the order to the wallet
const signature = await this.signingWallet._signTypedData(
typedPayload.domain,
typedPayload.types,
typedPayload.message
);
// Submit to off-chain matching engine
const response = await fetch('/execution/route', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
commitment: typedPayload.message,
authorization: signature,
marketFlags: { negRisk: order.negRiskEnabled }
})
});
return response.json();
}
}
Rationale: The order payload is structured as EIP-712 typed data, signed client-side, and submitted to an off-chain matching engine. The engine verifies the signature, matches orders, and settles trades on Polygon using the cryptographic proof. This eliminates server-side trust assumptions, provides non-repudiation, and allows the matching engine to operate independently of the settlement layer.
Step 4: Explicit Ontology and State Management
Financial APIs must encode conceptual relationships directly into the data model. Events contain markets, markets carry resolution flags, and parameters like tick size mutate based on price extremes.
// market-ontology.ts
export interface ResolutionEvent {
eventId: string;
title: string;
resolutionDeadline: string;
capitalStructure: 'INDEPENDENT' | 'NEGATIVE_RISK';
outcomes: MarketOutcome[];
}
export interface MarketOutcome {
outcomeId: string;
question: string;
probabilityCurve: number[]; // Parallel array: index matches outcome index
liquidityDepth: number;
}
export class MarketStateTracker {
private tickSizeCache: Map<string, string> = new Map();
private wsConnection: WebSocket;
constructor(endpoint: string) {
this.wsConnection = new WebSocket(endpoint);
this.subscribeToStateChanges();
}
private subscribeToStateChanges(): void {
this.wsConnection.onmessage = (event: MessageEvent) => {
const payload = JSON.parse(event.data);
if (payload.eventType === 'parameter_update') {
this.tickSizeCache.set(payload.assetId, payload.newTickSize);
}
};
}
getActiveTickSize(assetId: string): string {
return this.tickSizeCache.get(assetId) || '0.01';
}
}
Rationale: Explicit ontology prevents silent data corruption. By exposing capitalStructure and parallel probability arrays, clients can reconstruct position equivalences without guessing. Dynamic tick size tracking via WebSocket ensures order construction aligns with current market state, preventing rejections during extreme price movements.
Pitfall Guide
1. Flattening Event-Market Ontology
Explanation: Treating events and markets as identical entities removes the ability to track resolution dependencies and capital relationships. Clients cannot distinguish between independent binary markets and mutually exclusive outcomes.
Fix: Maintain explicit parent-child relationships. Expose resolution flags at the event level and parallel outcome arrays at the market level. Validate ontology consistency during API responses.
2. Hardcoding Tick Size Parameters
Explanation: Assuming tick size is static causes order rejections when markets approach probability extremes. Financial parameters mutate based on price discovery, not configuration files.
Fix: Subscribe to parameter change streams. Validate tick precision dynamically before signing orders. Implement fallback logic that adjusts order size when tick constraints tighten.
3. Collapsing Authentication Layers
Explanation: Using a single auth mechanism for both identity proof and request signing forces clients to expose private keys during high-frequency operations or rely on weak session tokens for critical actions.
Fix: Implement L1/L2 separation. Use cryptographic signatures for credential derivation and HMAC-based tokens for routine requests. Rotate L2 credentials periodically without requiring L1 re-authentication.
4. Ignoring Capital Equivalence Constraints
Explanation: Failing to expose negative risk flags leads to incorrect position modeling. Clients assume independent pricing when outcomes are mathematically linked, causing arbitrage losses and settlement mismatches.
Fix: Surface capitalStructure flags explicitly. Enforce conversion logic at the API boundary. Provide helper methods that calculate equivalent positions across linked outcomes.
5. Treating Orders as Standard POST Requests
Explanation: Submitting orders without cryptographic payloads removes non-repudiation. Servers become the sole source of truth, creating centralization risks and settlement disputes during network partitions.
Fix: Structure orders as self-authorizing instruments. Sign payloads client-side using typed data standards. Verify signatures server-side before matching. Maintain cryptographic audit trails for all executions.
6. Over-Authenticating Read Endpoints
Explanation: Requiring credentials for market data discovery reduces liquidity participation. Analytics pipelines, pricing aggregators, and retail interfaces face unnecessary friction, degrading overall market efficiency.
Fix: Decouple read and write access. Keep discovery endpoints public and cache aggressively. Apply authentication only at execution boundaries. Implement rate limiting based on IP or lightweight tokens rather than full OAuth flows.
7. Missing WebSocket State Propagation
Explanation: Clients that only listen to price ticks miss critical state changes like tick size adjustments, resolution updates, or liquidity threshold shifts. This causes stale order construction and failed submissions.
Fix: Stream parameter changes alongside price updates. Implement reconnection logic with state reconciliation. Validate market conditions before every order submission, not just during initial connection.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Retail UI Display | Public discovery gateway, cached responses, WebSocket price feeds | Minimizes latency, reduces auth overhead, improves UX | Low infrastructure cost, high CDN utilization |
| Algorithmic Trading Bot | Execution gateway, L2 HMAC auth, dynamic tick validation, signed order routing | Ensures non-repudiation, prevents rejections, maintains settlement integrity | Higher compute cost for signature verification, lower dispute resolution cost |
| Analytics & Reporting Pipeline | Analytics gateway, wallet-addressed queries, historical trade streams | Isolates heavy read queries from live trading, prevents resource contention | Moderate storage cost, reduced execution gateway load |
| Cross-Platform Aggregator | Public read endpoints, standardized ontology mapping, parameter stream subscription | Enables consistent data modeling across multiple markets, simplifies integration | Low auth cost, requires robust state reconciliation logic |
Configuration Template
// api-client-config.ts
export interface FinancialAPIClientConfig {
discovery: {
baseUrl: string;
cacheTTL: number;
maxRetries: number;
};
execution: {
baseUrl: string;
auth: {
l1Provider: string;
l2CredentialStore: string;
hmacAlgorithm: 'sha256';
};
orderValidation: {
dynamicTickCheck: boolean;
negRiskEnforcement: boolean;
expiryBuffer: number;
};
};
analytics: {
baseUrl: string;
walletAddress: string;
streamEndpoints: string[];
};
websocket: {
reconnectDelay: number;
maxReconnectAttempts: number;
stateSyncInterval: number;
};
}
export const defaultConfig: FinancialAPIClientConfig = {
discovery: {
baseUrl: 'https://discovery-gateway.example.com',
cacheTTL: 30000,
maxRetries: 3
},
execution: {
baseUrl: 'https://execution-engine.example.com',
auth: {
l1Provider: 'ethers',
l2CredentialStore: 'secure-vault',
hmacAlgorithm: 'sha256'
},
orderValidation: {
dynamicTickCheck: true,
negRiskEnforcement: true,
expiryBuffer: 5000
}
},
analytics: {
baseUrl: 'https://analytics-stream.example.com',
walletAddress: '',
streamEndpoints: ['/positions', '/trades', '/leaderboard']
},
websocket: {
reconnectDelay: 1000,
maxReconnectAttempts: 10,
stateSyncInterval: 15000
}
};
Quick Start Guide
- Initialize Domain Gateways: Configure separate client instances for discovery, execution, and analytics using the provided template. Set cache TTLs and retry policies based on consumer profile.
- Derive L2 Credentials: Use L1 wallet signature to authenticate once and retrieve HMAC session keys. Store credentials securely and implement automatic rotation before expiration.
- Subscribe to State Streams: Connect to WebSocket endpoints for price ticks and parameter updates. Implement reconnection logic with state reconciliation to maintain accurate market conditions.
- Construct Signed Orders: Validate dynamic tick size and capital constraints. Build EIP-712 typed payloads, sign client-side, and route to the execution gateway. Verify cryptographic receipts before updating local state.
- Monitor and Reconcile: Track execution receipts, position updates, and parameter changes. Implement idempotency keys for order submissions and maintain an audit trail for settlement verification.