Overview
The CTFExchange contract is the core binary limit order exchange for Polymarket. It implements comprehensive trading logic for Conditional Token Framework (CTF) assets through a modular architecture of inherited mixins.
Contract: CTFExchange
Inherits: BaseExchange, Auth, Assets, Fees, Pausable, AssetOperations, Hashing, NonceManager, Registry, Signatures, Trading
Constructor
constructor(
address _collateral,
address _ctf,
address _proxyFactory,
address _safeFactory
)
Initializes the CTFExchange contract with required addresses for collateral, conditional tokens, and wallet factories.
The ERC20 collateral token address (USDC)
The ERC1155 Conditional Token Framework contract address
The Polymarket proxy wallet factory address
The Gnosis Safe factory contract address
Trading Functions
fillOrder
function fillOrder(Order memory order, uint256 fillAmount) external
Fills an order with the caller as the taker.
The amount to be filled, always in terms of the maker amount
Modifiers: nonReentrant, onlyOperator, notPaused
Requirements:
- Caller must be an authorized operator
- Trading must not be paused
- Order must be valid and not expired
- Order signature must be valid
- Order must not be filled or cancelled
fillOrders
function fillOrders(
Order[] memory orders,
uint256[] memory fillAmounts
) external
Fills multiple orders in a single transaction.
Array of orders to be filled
Array of amounts to be filled for each order, in terms of maker amounts
Modifiers: nonReentrant, onlyOperator, notPaused
Requirements:
- Caller must be an authorized operator
- Trading must not be paused
- All orders must be valid
matchOrders
function matchOrders(
Order memory takerOrder,
Order[] memory makerOrders,
uint256 takerFillAmount,
uint256[] memory makerFillAmounts
) external
Matches a taker order against multiple maker orders.
The active taker order to be matched
Array of passive maker orders to match against
Amount to fill on the taker order, in terms of maker amount
Array of amounts to fill on each maker order, in terms of maker amounts
Modifiers: nonReentrant, onlyOperator, notPaused
Requirements:
- Caller must be an authorized operator
- Trading must not be paused
- Orders must be crossing (price overlap)
- Token IDs must match or be complements
Order Management Functions
validateOrder
function validateOrder(Order memory order) public view
Validates an order without executing it.
Validation checks:
- Order has not expired
- Signature is valid
- Fee rate is within allowed maximum
- Token ID is registered
- Order is not already filled or cancelled
- Nonce is valid
getOrderStatus
function getOrderStatus(bytes32 orderHash) public view returns (OrderStatus memory)
Returns the current status of an order.
The hash of the order to query
Returns: OrderStatus struct containing:
isFilledOrCancelled (bool): Whether the order is filled or cancelled
remaining (uint256): Remaining amount available to fill
cancelOrder
function cancelOrder(Order memory order) external
Cancels a single order. Can only be called by the order maker.
Requirements:
- Caller must be the order maker
- Order must not already be filled or cancelled
cancelOrders
function cancelOrders(Order[] memory orders) external
Cancels multiple orders in a single transaction.
Array of orders to cancel
Requirements:
- Caller must be the maker of all orders
- Orders must not already be filled or cancelled
Access Control Functions
addAdmin
function addAdmin(address admin_) external
Adds a new admin to the exchange.
Address to grant admin privileges
Modifiers: onlyAdmin
addOperator
function addOperator(address operator_) external
Adds a new operator to the exchange.
Address to grant operator privileges
Modifiers: onlyAdmin
removeAdmin
function removeAdmin(address admin) external
Removes an admin from the exchange.
Address to revoke admin privileges from
Modifiers: onlyAdmin
removeOperator
function removeOperator(address operator) external
Removes an operator from the exchange.
Address to revoke operator privileges from
Modifiers: onlyAdmin
renounceAdminRole
function renounceAdminRole() external
Allows caller to renounce their own admin role.
Modifiers: onlyAdmin
renounceOperatorRole
function renounceOperatorRole() external
Allows caller to renounce their own operator role.
Modifiers: onlyOperator
isAdmin
function isAdmin(address usr) external view returns (bool)
Checks if an address has admin privileges.
Returns: true if the address is an admin
isOperator
function isOperator(address usr) external view returns (bool)
Checks if an address has operator privileges.
Returns: true if the address is an operator
Configuration Functions
pauseTrading
function pauseTrading() external
Pauses all trading operations on the exchange.
Modifiers: onlyAdmin
unpauseTrading
function unpauseTrading() external
Resumes trading operations on the exchange.
Modifiers: onlyAdmin
setProxyFactory
function setProxyFactory(address _newProxyFactory) external
Updates the Polymarket proxy wallet factory address.
New proxy wallet factory address
Modifiers: onlyAdmin
setSafeFactory
function setSafeFactory(address _newSafeFactory) external
Updates the Gnosis Safe factory address.
Modifiers: onlyAdmin
registerToken
function registerToken(
uint256 token,
uint256 complement,
bytes32 conditionId
) external
Registers a token pair (binary outcome) for trading on the exchange.
The ERC1155 token ID to register
The complementary token ID (the other outcome)
The CTF condition ID for this market
Modifiers: onlyAdmin
Requirements:
- Token and complement must be different
- Neither token can be zero
- Tokens must not already be registered
View Functions
getCollateral
function getCollateral() public view returns (address)
Returns the collateral token address (USDC).
Returns: Address of the ERC20 collateral token
getCtf
function getCtf() public view returns (address)
Returns the Conditional Token Framework contract address.
Returns: Address of the ERC1155 CTF contract
getMaxFeeRate
function getMaxFeeRate() public pure returns (uint256)
Returns the maximum allowed fee rate in basis points.
Returns: Maximum fee rate (1000 bips = 10%)
getConditionId
function getConditionId(uint256 token) public view returns (bytes32)
Returns the condition ID for a registered token.
Returns: The bytes32 condition ID
getComplement
function getComplement(uint256 token) public view returns (uint256)
Returns the complement token ID for a given token.
Returns: The complement token ID
Requirements:
validateComplement
function validateComplement(uint256 token, uint256 complement) public view
Validates that two tokens are complements of each other.
Second token ID to validate as complement
Reverts: If tokens are not complements
validateTokenId
function validateTokenId(uint256 tokenId) public view
Validates that a token is registered for trading.
Reverts: If token is not registered
getProxyFactory
function getProxyFactory() public view returns (address)
Returns the Polymarket proxy wallet factory address.
Returns: Proxy factory address
getSafeFactory
function getSafeFactory() public view returns (address)
Returns the Gnosis Safe factory address.
Returns: Safe factory address
getPolyProxyFactoryImplementation
function getPolyProxyFactoryImplementation() public view returns (address)
Returns the implementation address used by the proxy factory.
Returns: Proxy factory implementation address
getSafeFactoryImplementation
function getSafeFactoryImplementation() public view returns (address)
Returns the master copy address used by the Safe factory.
Returns: Safe factory master copy address
getPolyProxyWalletAddress
function getPolyProxyWalletAddress(address _addr) public view returns (address)
Computes the Polymarket proxy wallet address for a given owner.
Address of the wallet owner
Returns: Computed proxy wallet address
getSafeAddress
function getSafeAddress(address _addr) public view returns (address)
Computes the Gnosis Safe address for a given owner.
Address of the safe owner
Returns: Computed safe address
isValidNonce
function isValidNonce(address usr, uint256 nonce) public view returns (bool)
Checks if a nonce is valid for a user.
Returns: true if the nonce matches the user’s current nonce
incrementNonce
function incrementNonce() external
Increments the caller’s nonce by 1, invalidating all orders with the previous nonce.
Events
OrderFilled
event OrderFilled(
bytes32 indexed orderHash,
address indexed maker,
address indexed taker,
uint256 makerAssetId,
uint256 takerAssetId,
uint256 makerAmountFilled,
uint256 takerAmountFilled,
uint256 fee
)
Emitted when an order is filled.
OrdersMatched
event OrdersMatched(
bytes32 indexed takerOrderHash,
address indexed takerOrderMaker,
uint256 makerAssetId,
uint256 takerAssetId,
uint256 makerAmountFilled,
uint256 takerAmountFilled
)
Emitted when orders are matched against each other.
OrderCancelled
event OrderCancelled(bytes32 indexed orderHash)
Emitted when an order is cancelled.
FeeCharged
event FeeCharged(
address indexed receiver,
uint256 tokenId,
uint256 amount
)
Emitted when a fee is charged.
NewAdmin
event NewAdmin(
address indexed newAdminAddress,
address indexed admin
)
Emitted when a new admin is added.
NewOperator
event NewOperator(
address indexed newOperatorAddress,
address indexed admin
)
Emitted when a new operator is added.
RemovedAdmin
event RemovedAdmin(
address indexed removedAdmin,
address indexed admin
)
Emitted when an admin is removed.
RemovedOperator
event RemovedOperator(
address indexed removedOperator,
address indexed admin
)
Emitted when an operator is removed.
TradingPaused
event TradingPaused(address indexed pauser)
Emitted when trading is paused.
TradingUnpaused
event TradingUnpaused(address indexed pauser)
Emitted when trading is unpaused.
TokenRegistered
event TokenRegistered(
uint256 indexed token0,
uint256 indexed token1,
bytes32 indexed conditionId
)
Emitted when a token pair is registered for trading.
ProxyFactoryUpdated
event ProxyFactoryUpdated(
address indexed oldProxyFactory,
address indexed newProxyFactory
)
Emitted when the proxy factory address is updated.
SafeFactoryUpdated
event SafeFactoryUpdated(
address indexed oldSafeFactory,
address indexed newSafeFactory
)
Emitted when the safe factory address is updated.
Errors
NotAdmin
Thrown when a non-admin tries to call an admin-only function.
NotOperator
Thrown when a non-operator tries to call an operator-only function.
NotOwner
Thrown when trying to cancel an order not owned by the caller.
NotTaker
Thrown when the caller is not the designated taker of an order.
OrderFilledOrCancelled
error OrderFilledOrCancelled()
Thrown when trying to fill or cancel an order that is already filled or cancelled.
OrderExpired
Thrown when trying to fill an expired order.
InvalidNonce
Thrown when an order has an invalid nonce.
MakingGtRemaining
error MakingGtRemaining()
Thrown when trying to fill more than the remaining amount on an order.
NotCrossing
Thrown when orders don’t have price overlap and cannot be matched.
error TooLittleTokensReceived()
Thrown when a match operation doesn’t generate enough tokens.
MismatchedTokenIds
error MismatchedTokenIds()
Thrown when trying to match orders with incompatible token IDs.
FeeTooHigh
Thrown when an order specifies a fee higher than the maximum allowed.
InvalidSignature
Thrown when an order signature is invalid.
InvalidComplement
error InvalidComplement()
Thrown when validating tokens that are not complements.
InvalidTokenId
Thrown when a token ID is not registered or invalid.
AlreadyRegistered
error AlreadyRegistered()
Thrown when trying to register a token that is already registered.
Paused
Thrown when trying to trade while the exchange is paused.
Data Structures
Order
struct Order {
uint256 salt;
address maker;
address signer;
address taker;
uint256 tokenId;
uint256 makerAmount;
uint256 takerAmount;
uint256 expiration;
uint256 nonce;
uint256 feeRateBps;
Side side;
SignatureType signatureType;
bytes signature;
}
Unique salt to ensure order uniqueness
Address of the order maker (source of funds)
Address that signed the order
Designated taker address (zero address for public orders)
Token ID being bought (BUY) or sold (SELL)
Maximum amount of tokens to be sold by maker
Minimum amount of tokens to be received by maker
Timestamp after which the order expires (0 = no expiration)
Nonce for onchain cancellations
Fee rate in basis points charged to maker on proceeds
Order side: BUY (0) or SELL (1)
Type of signature: EOA (0), POLY_PROXY (1), POLY_GNOSIS_SAFE (2), or POLY_1271 (3)
OrderStatus
struct OrderStatus {
bool isFilledOrCancelled;
uint256 remaining;
}
Whether the order is completely filled or has been cancelled
Remaining maker amount available to fill
Side
enum Side {
BUY, // 0
SELL // 1
}
Order side enumeration.
SignatureType
enum SignatureType {
EOA, // 0: ECDSA signatures from EOAs
POLY_PROXY, // 1: Signatures from Polymarket Proxy wallets
POLY_GNOSIS_SAFE, // 2: Signatures from Polymarket Gnosis Safes
POLY_1271 // 3: EIP-1271 signatures from smart contracts
}
Supported signature types.
MatchType
enum MatchType {
COMPLEMENTARY, // 0: Buy vs Sell (direct trade)
MINT, // 1: Both buys (mint new tokens from collateral)
MERGE // 2: Both sells (merge tokens into collateral)
}
Types of order matching operations.