Documentation Index Fetch the complete documentation index at: https://mintlify.com/Polymarket/ctf-exchange/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide provides security best practices for developers integrating with the Polymarket CTF Exchange and operators running exchange infrastructure. Following these guidelines will help ensure the security of your integration and protect your users.
For Integrators
Signature Validation
The CTF Exchange supports multiple signature types. Ensure you’re using the correct type for your use case.
EOA Signatures (SignatureType.EOA)
Standard ECDSA signatures for externally owned accounts. Requirements (Signatures.sol:67-73):
Signer and maker must be the same address
Valid ECDSA signature using EIP-191 or EIP-712
Use when :
Users are signing with standard Ethereum wallets (MetaMask, etc.)
No proxy or smart contract wallet is involved
Example :const order = {
signer: userAddress ,
maker: userAddress ,
signatureType: SignatureType . EOA ,
signature: await wallet . signMessage ( orderHash ),
// ... other fields
};
Proxy Wallet Signatures (SignatureType.POLY_PROXY)
For Polymarket proxy wallets. Requirements (Signatures.sol:96-102):
Valid ECDSA signature from the wallet owner
Maker address must match the deterministic proxy address
Proxy address is computed from signer, factory, and implementation
Use when :
Users are trading through Polymarket proxy wallets
You need deterministic wallet addresses
Validation :const proxyAddress = await exchange . getPolyProxyWalletAddress ( signer );
// Ensure order.maker === proxyAddress
Gnosis Safe Signatures (SignatureType.POLY_GNOSIS_SAFE)
For multisig wallets using Gnosis Safe. Requirements (Signatures.sol:112-118):
Valid ECDSA signature from a Safe owner
Maker address must match the deterministic Safe address
Use when :
Implementing institutional trading with multisig
Users require multiple signers for security
Example :const safeAddress = await exchange . getSafeAddress ( signer );
// Ensure order.maker === safeAddress
Smart Contract Signatures (SignatureType.POLY_1271)
For smart contracts implementing EIP-1271. Requirements (Signatures.sol:125-132):
Signer and maker must be the same address
Contract must have code deployed
Must implement EIP-1271 isValidSignature interface
Use when :
Integrating with smart contract wallets
Building automated trading strategies
Implementing custom signature logic
Important : Ensure the contract’s isValidSignature function cannot be exploited.
Order Construction
Always validate order parameters before signing to prevent users from signing malicious orders.
Essential Validations
Token Validation
// Verify token is registered
const conditionId = await exchange . getConditionId ( tokenId );
if ( conditionId === ethers . constants . HashZero ) {
throw new Error ( "Token not registered" );
}
// Verify complement matches
const complement = await exchange . getComplement ( tokenId );
if ( complement !== expectedComplement ) {
throw new Error ( "Invalid complement" );
}
Nonce Validation
// Use current nonce for the order
const currentNonce = await exchange . nonces ( makerAddress );
order . nonce = currentNonce ;
Amount Validation
// Ensure amounts are reasonable
if ( order . makerAmount <= 0 || order . takerAmount <= 0 ) {
throw new Error ( "Invalid amounts" );
}
// Check for precision issues
if ( ! Number . isInteger ( order . makerAmount ) || ! Number . isInteger ( order . takerAmount )) {
throw new Error ( "Amounts must be integers" );
}
Side Validation
// Ensure side is valid (BUY or SELL)
if ( order . side !== Side . BUY && order . side !== Side . SELL ) {
throw new Error ( "Invalid side" );
}
Nonce Management
The exchange uses nonces to prevent replay attacks and allow order cancellation.
Each address has a single nonce value. Incrementing it invalidates all outstanding orders from that address.
Emergency Order Cancellation
// Cancel all outstanding orders by incrementing nonce
await exchange . incrementNonce ();
Location : NonceManager.sol:9
Use cases :
User wants to cancel all orders at once
Suspected account compromise
Market conditions change drastically
Incrementing the nonce invalidates ALL outstanding orders. Users cannot selectively cancel individual orders this way.
Error Handling
Implement comprehensive error handling for all exchange interactions.
try {
await exchange . fillOrder ( order , fillAmount );
} catch ( error ) {
if ( error . message . includes ( "NotOperator" )) {
// Only operators can fill orders
console . error ( "Caller is not an authorized operator" );
} else if ( error . message . includes ( "Paused" )) {
// Trading is paused
console . error ( "Exchange trading is currently paused" );
} else if ( error . message . includes ( "InvalidSignature" )) {
// Signature validation failed
console . error ( "Order signature is invalid" );
} else if ( error . message . includes ( "InvalidTokenId" )) {
// Token not registered
console . error ( "Token is not registered for trading" );
} else {
// Handle other errors
console . error ( "Order fill failed:" , error );
}
}
Reentrancy Protection
All state-changing functions in the exchange are protected by OpenZeppelin’s ReentrancyGuard.
While the exchange itself is protected, your integration should also follow best practices:
// ✅ Good: Check state before external calls
const balance = await token . balanceOf ( user );
if ( balance >= amount ) {
await exchange . fillOrder ( order , amount );
}
// ❌ Bad: Making assumptions after external calls
await exchange . fillOrder ( order , amount );
const balance = await token . balanceOf ( user ); // State may have changed
Gas Optimization
Batch Operations
Use batch functions when filling multiple orders:
// ✅ Efficient: Single transaction for multiple orders
await exchange . fillOrders ( orders , fillAmounts );
// ❌ Inefficient: Multiple transactions
for ( const order of orders ) {
await exchange . fillOrder ( order , fillAmount );
}
Benefits :
Lower gas costs per order
Atomic execution (all or nothing)
Better UX for users
Frontend Security
Display Order Details Clearly
Always show users exactly what they’re signing: const orderSummary = {
market: marketName ,
outcome: outcomeName ,
side: order . side === Side . BUY ? "Buy" : "Sell" ,
amount: formatAmount ( order . makerAmount ),
price: calculatePrice ( order . makerAmount , order . takerAmount ),
expiration: new Date ( order . expiration * 1000 ),
};
// Display to user before requesting signature
Verify contract addresses match expected values
Display contract addresses to users
Implement address whitelisting for production
Use ENS names where appropriate
const EXPECTED_EXCHANGE = "0x..." ; // From config
if ( exchange . address . toLowerCase () !== EXPECTED_EXCHANGE . toLowerCase ()) {
throw new Error ( "Exchange address mismatch - possible phishing attempt" );
}
Monitor for Suspicious Activity
Track and alert on unusual patterns: // Example: Detect rapid order creation
const recentOrders = userOrders . filter (
( o ) => o . timestamp > Date . now () - 60000 // Last minute
);
if ( recentOrders . length > 10 ) {
showWarning ( "Unusual activity detected. Please verify your account security." );
}
For Operators
Access Control
Operators can execute trades on behalf of users. Secure your operator keys carefully.
Compromised operator keys can be used to execute unauthorized trades. Implement defense in depth.
Key Management
Use Hardware Security Modules (HSMs)
Store operator keys in HSMs or secure enclaves
Never store keys in plain text
Rotate keys periodically
Implement Rate Limiting
// Example: Limit orders per second
const MAX_ORDERS_PER_SECOND = 10 ;
const orderTimestamps = [];
function checkRateLimit () {
const now = Date . now ();
const recent = orderTimestamps . filter (( t ) => now - t < 1000 );
if ( recent . length >= MAX_ORDERS_PER_SECOND ) {
throw new Error ( "Rate limit exceeded" );
}
orderTimestamps . push ( now );
}
Monitor Operator Activity
Log all operator transactions
Alert on unusual patterns
Implement automated circuit breakers
Use Multiple Operators
Distribute load across multiple operator addresses
Limit exposure if one key is compromised
Implement operator rotation
Order Validation
Operators should validate orders before submission:
function validateOrderBeforeFill ( order : Order ) : void {
// 1. Verify signature
const isValid = await exchange . validateOrderSignature (
orderHash ,
order
);
if ( ! isValid ) throw new Error ( "Invalid signature" );
// 2. Check nonce
const currentNonce = await exchange . nonces ( order . maker );
if ( order . nonce !== currentNonce ) {
throw new Error ( "Invalid nonce - order may be cancelled" );
}
// 3. Verify expiration
if ( order . expiration < Math . floor ( Date . now () / 1000 )) {
throw new Error ( "Order expired" );
}
// 4. Check token registration
await exchange . validateTokenId ( order . tokenId );
// 5. Verify maker has sufficient balance
const balance = await ctf . balanceOf ( order . maker , order . tokenId );
if ( balance < order . makerAmount ) {
throw new Error ( "Insufficient maker balance" );
}
}
Monitoring and Alerting
Implement comprehensive monitoring for exchange operations:
exchange . on ( "TradingPaused" , ( pauser ) => {
console . error ( `ALERT: Trading paused by ${ pauser } ` );
// Stop accepting new orders
// Notify operations team
});
exchange . on ( "TradingUnpaused" , ( unpauser ) => {
console . info ( `Trading resumed by ${ unpauser } ` );
// Resume normal operations
});
exchange . on ( "RemovedOperator" , ( operator , admin ) => {
if ( operator === operatorAddress ) {
console . error ( `ALERT: Operator privileges revoked by ${ admin } ` );
// Stop operations immediately
}
});
exchange . on ( "NewOperator" , ( operator , admin ) => {
console . info ( `New operator added: ${ operator } by ${ admin } ` );
});
exchange . on ( "ProxyFactoryUpdated" , ( oldFactory , newFactory ) => {
console . warn ( `Proxy factory updated: ${ oldFactory } -> ${ newFactory } ` );
// May need to update signature verification logic
});
exchange . on ( "SafeFactoryUpdated" , ( oldFactory , newFactory ) => {
console . warn ( `Safe factory updated: ${ oldFactory } -> ${ newFactory } ` );
// May need to update signature verification logic
});
Track Transaction Failures
const failureThreshold = 10 ;
let recentFailures = 0 ;
async function executeTrade ( order : Order , amount : number ) {
try {
await exchange . fillOrder ( order , amount );
recentFailures = 0 ; // Reset on success
} catch ( error ) {
recentFailures ++ ;
if ( recentFailures >= failureThreshold ) {
console . error ( "ALERT: High failure rate detected" );
// Trigger circuit breaker
}
throw error ;
}
}
Infrastructure Security
Network Security
Use private RPC endpoints
Implement request authentication
Rate limit RPC calls
Monitor for unusual network activity
Database Security
Encrypt sensitive data at rest
Use parameterized queries
Implement access controls
Regular security audits
API Security
Require authentication for all endpoints
Implement request signing
Use HTTPS exclusively
Rate limit all endpoints
Deployment Security
Use infrastructure as code
Implement least privilege access
Regular security patches
Audit logging for all access
Emergency Procedures
For Admins
In case of a security incident:
Pause Trading
Immediately call pauseTrading() to halt all operations: await exchange . pauseTrading ();
Assess the Situation
Identify the nature of the incident
Determine scope of impact
Review recent transactions
Check if any operators are compromised
Revoke Compromised Access
Remove any compromised operators or admins: await exchange . removeOperator ( compromisedOperator );
Implement Mitigation
Deploy fixes if necessary
Update configurations
Rotate keys
Notify affected users
Resume Operations
Once the issue is resolved: await exchange . unpauseTrading ();
Post-Incident Review
Document the incident
Identify root cause
Implement preventive measures
Update security procedures
For Users
If you suspect your account is compromised:
Cancel All Orders
Increment your nonce to invalidate all outstanding orders: await exchange . incrementNonce ();
Revoke Approvals
Remove token approvals if possible: await ctf . setApprovalForAll ( exchange . address , false );
Transfer Assets
Move assets to a secure address if possible.
Contact Support
Report the incident through official channels.
Security Checklist
Integration Checklist
Pre-Launch Security Review
Operator Checklist
Operational Security Review
Additional Resources
Security Audit Review the ChainSecurity audit report
Admin Controls Learn about administrative functions
Order Structure Understand how orders work
Signature Types Deep dive into signature validation
Security is an ongoing process. Stay informed about updates to the protocol and regularly review your security practices.