### NatSpec for Struct Fields Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Example of NatSpec documentation for struct fields, explaining the purpose of each field. ```Solidity struct Order { /// @notice Unique salt to ensure entropy uint256 salt; /// @notice Maker of the order, i.e the source of funds address maker; } ``` -------------------------------- ### NatSpec for Events Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Example of NatSpec documentation for Solidity events, detailing the event's purpose and its parameters. ```Solidity /// @notice Emitted when the fee receiver is updated. /// @param receiver The new fee receiver address. event FeeReceiverUpdated(address receiver); ``` -------------------------------- ### Build Project (Forge) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Command to compile the project using Foundry's build system. Ensure you have Foundry installed and updated. ```bash forge build ``` -------------------------------- ### Constructing a BUY Order for CTF Exchange V2 Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Example of how to construct an `Order` struct for a BUY transaction. Ensure `makerAmount` represents collateral and `takerAmount` represents the minimum outcome tokens. The `tokenId` should be the asset to buy. ```Solidity // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.34; import { Order, Side, SignatureType } from "src/exchange/libraries/Structs.sol"; // Example: construct a BUY order for 100 USDC worth of YES tokens at 0.60 Order memory buyOrder = Order({ salt: 12345678, // unique entropy maker: 0xUserAddress, // source of funds signer: 0xUserAddress, // EOA: must equal maker tokenId: 0xYES_TOKEN_ID, // CTF ERC1155 token to buy makerAmount: 100_000_000, // 100 USDC (6 decimals) — collateral to spend takerAmount: 60_000_000, // 60 outcome tokens minimum — implied price ~0.60 side: Side.BUY, signatureType: SignatureType.EOA, timestamp: 1_700_000_000_000, // milliseconds metadata: keccak256("arbitraryMetadata"), builder: bytes32(0), signature: hex"..." // EIP-712 ECDSA signature over the order hash }); ``` -------------------------------- ### NatSpec for Errors Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Example of NatSpec documentation for Solidity errors, including a descriptive notice. ```Solidity /// @notice Thrown when the caller is not authorized. error Unauthorized(); ``` -------------------------------- ### Constructing a SELL Order for CTF Exchange V2 Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Example of how to construct an `Order` struct for a SELL transaction. For SELL orders, `tokenId` represents the asset being sold, `makerAmount` is the quantity of that asset, and `takerAmount` is the minimum collateral to receive. ```Solidity // Example: construct a SELL order for the complementary NO token at 0.40 Order memory sellOrder = Order({ salt: 87654321, maker: 0xAnotherUser, signer: 0xAnotherUser, tokenId: 0xNO_TOKEN_ID, // SELL: tokenId = asset being sold makerAmount: 60_000_000, // 60 NO tokens to sell takerAmount: 40_000_000, // 40 USDC minimum to receive side: Side.SELL, signatureType: SignatureType.EOA, timestamp: 1_700_000_001_000, metadata: bytes32(0), builder: bytes32(0), signature: hex"..." }); ``` -------------------------------- ### Get Order Status - CTFExchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Query the fill state of an order using its hash. Returns the OrderStatus struct indicating if the order is filled and the remaining amount to fill. ```Solidity import { CTFExchange } from "src/exchange/CTFExchange.sol"; import { OrderStatus } from "src/exchange/libraries/Structs.sol"; CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); bytes32 orderHash = exchange.hashOrder(myOrder); OrderStatus memory status = exchange.getOrderStatus(orderHash); // status.filled — true if the order has been fully filled or is completely consumed // status.remaining — amount still available to fill (0 when order is new, set on first partial fill) if (status.filled) { // order is done — do not resubmit } else if (status.remaining > 0) { uint256 canFill = status.remaining; // partial fill: only `canFill` maker-amount units remain } ``` -------------------------------- ### Solidity Import Conventions Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Illustrates the standard import paths for external libraries, cross-domain source files, and same-domain source files. Remappings are managed via `foundry.toml`. ```Solidity import {Type} from "@solady/src/path/File.sol"; ``` ```Solidity import {Type} from "@forge-std/src/File.sol"; ``` ```Solidity import {Type} from "@ctf-exchange-v2/src/domain/File.sol"; ``` ```Solidity import {Type} from "./File.sol"; ``` ```Solidity import {Type} from "../File.sol"; ``` -------------------------------- ### Foundry Development Workflow Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Commands for building, testing, and generating gas snapshots using Foundry. Includes prerequisites, running tests with various filters, and configuration options. ```bash # Prerequisites: install Foundry curl -L https://foundry.paradigm.xyz | bash foundryup # Build all contracts forge build # Run the full test suite forge test # Run tests matching a function name pattern forge test -m testMatchOrders # Run tests in contracts matching a name pattern (verbose stack trace) forge test --mc CTFExchange -vvv # Run with the intense fuzz profile (10,000 fuzz runs) forge test --profile intense # Generate gas snapshots (output to snapshots/ directory) forge snapshot # Configuration (foundry.toml): # solc: 0.8.30 # optimizer_runs: 1_000_000 ``` -------------------------------- ### Deploy CTF Exchange with Init Params Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Use this to deploy a standard CTF exchange instance. Ensure all addresses for collateral, CTF, and other factories are correctly set. ```solidity import { ExchangeInitParams } from "src/exchange/libraries/Structs.sol"; import { CTFExchange } from "src/exchange/CTFExchange.sol"; // Deploy a standard CTF exchange instance ExchangeInitParams memory params = ExchangeInitParams({ admin: 0xAdminAddress, collateral: 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB, // pUSD (CollateralToken proxy) ctf: 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045, // Gnosis CTF contract ctfCollateral: 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, // USDCe (used for position IDs) outcomeTokenFactory: 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045, // CTF or NegRiskAdapter proxyFactory: 0xaB45c5A4B0c941a2F231C04C3f49182e1A254052, safeFactory: 0xd497BeA5D3b5B4B3E5C7F9E7A8B6D5C4F3E2A1B0, feeReceiver: 0xFeeReceiverAddress }); CTFExchange exchange = new CTFExchange(params); // exchange is now deployed and the admin is also the initial operator ``` -------------------------------- ### Build & Verification Commands Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Standard commands for building, testing, formatting, and snapshotting gas usage for the project. Ensure all commands pass before completing a task. ```bash forge build # Compile forge test # Run all tests forge fmt # Format all files forge snapshot # Regenerate .gas-snapshot ``` -------------------------------- ### Balance and Deal Helper Functions Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Use provided helpers for asserting balances and managing token deals and approvals. ```solidity assertCollateralBalance() ``` ```solidity assertCTFBalance() ``` ```solidity getCTFBalance() ``` ```solidity dealUsdcAndApprove() ``` ```solidity dealOutcomeTokensAndApprove() ``` -------------------------------- ### Branchless Match Type and Asset ID Calculation (Solidity) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Illustrates how to derive match types and compute asset IDs using arithmetic operations instead of conditional statements, leading to more efficient execution. ```solidity // Match type without branching matchType := mul(add(takerOrderSide, 1), eq(takerOrderSide, makerOrderSide)) // Asset IDs without branching makerAssetId := mul(side, tokenId) takerAssetId := sub(tokenId, makerAssetId) ``` -------------------------------- ### Assertion Helpers Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Utilize standard assertion functions like assertEq, assertTrue, and assertFalse for verifying conditions. ```solidity assertEq() ``` ```solidity assertTrue() ``` ```solidity assertFalse() ``` -------------------------------- ### Generate Gas Snapshots (Forge) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Creates gas snapshots for the project, useful for analyzing gas consumption of contract functions. ```bash forge snapshot ``` -------------------------------- ### CTF Exchange V2 Order Lifecycle Steps Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Illustrates the sequence of events from order creation to settlement in CTF Exchange V2, including signature validation and match types. ```text 1. Users sign EIP-712 typed orders off-chain specifying token, amounts, and side (BUY/SELL) 2. The operator calls `matchOrders()` with a taker order and array of maker orders 3. The exchange validates signatures, checks prices cross, and determines the match type 4. Settlement executes based on match type: - **COMPLEMENTARY** (BUY vs SELL) — Direct peer-to-peer transfers, no CTF operations - **MINT** (both BUY) — Collateral split into outcome tokens via CTF - **MERGE** (both SELL) — Outcome tokens merged back into collateral via CTF ``` -------------------------------- ### Run All Tests (Forge) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Executes all tests within the project. Use `-vvv` flag to display a stack trace for failed tests. ```bash forge test ``` -------------------------------- ### Storage-Packed Order Status Update (Solidity) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Demonstrates how to read and write packed order status using single SLOAD and SSTORE operations with bitwise operations. This method optimizes storage usage and access. ```solidity let packed := sload(status.slot) filled := and(packed, 0xff) remaining := shr(8, packed) sstore(status.slot, or(shl(8, remaining), iszero(remaining))) ``` -------------------------------- ### Caller Impersonation Pattern Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Use vm.prank, vm.startPrank, or vm.stopPrank for impersonating callers in tests. ```solidity vm.prank(address) ``` ```solidity vm.startPrank ``` ```solidity vm.stopPrank ``` -------------------------------- ### Event Emission Expectation Pattern Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Use vm.expectEmit with specific parameters before the emitting call to assert event emissions. ```solidity vm.expectEmit(true, true, true, true) ``` -------------------------------- ### EIP-712 Hashing with mcopy (Assembly) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Shows how to use the `mcopy` opcode for efficient struct hashing in assembly, copying order fields in a single operation to reduce overhead compared to field-by-field `mstore` calls. ```assembly mstore(ptr, ORDER_TYPEHASH) mcopy(add(ptr, 0x20), order, 0x160) // Copy 352 bytes in one instruction result := keccak256(ptr, 0x180) ``` -------------------------------- ### NatSpec for Contracts and Interfaces Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Standard NatSpec tags for documenting contracts, interfaces, and libraries. Includes title, notice, and author information. ```Solidity /// @title ContractName /// @notice Brief one-line description. /// @author Polymarket ``` -------------------------------- ### Cross-Multiplication Price Validation (Solidity) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Compares V1's division-based price comparison with V2's cross-multiplication method. V2 avoids division, preventing precision loss and potential floating-point issues. ```solidity // V1: division-based price comparison priceA = makerAmount_A * 1e18 / takerAmount_A priceB = makerAmount_B * 1e18 / takerAmount_B // V2: cross-multiplication (no division, no precision loss) makerAmount_A * makerAmount_B >= takerAmount_A * takerAmount_B ``` -------------------------------- ### NatSpec for Functions Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Standard NatSpec tags for documenting Solidity functions, covering their purpose, implementation details, parameters, and return values. ```Solidity /// @notice What it does (one line). /// @dev Implementation detail or access restriction (only if non-obvious). /// @param _name Description. /// @return name Description. ``` -------------------------------- ### CTF Exchange V2 Collateral System Overview Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Explains the collateral system in CTF Exchange V2, focusing on the PMCT token and its associated onramp, offramp, and adapter contracts. ```text The exchange trades in **PMCT** (PolyMarket Collateral Token), an ERC20 wrapper around USDC/USDCe: - **CollateralOnramp**: Wraps supported assets into PMCT - **CollateralOfframp**: Unwraps PMCT back to supported assets - **CtfCollateralAdapter**: Bridges PMCT ↔ CTF operations (split/merge/redeem) ``` -------------------------------- ### Calculate Taker Amount for Partial Fills Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Internal library function to compute the amount of 'taker-side' tokens to transfer based on a partial 'maker' fill. Ensure correct amounts are used for makingAmount, makerAmount, and takerAmount to achieve accurate pro-rata calculations. ```solidity import { CalculatorHelper } from "src/exchange/libraries/CalculatorHelper.sol"; // takingAmount = (makingAmount * takerAmount) / makerAmount // Example: order has makerAmount=100, takerAmount=60 (price = 0.60) // Filling 40 maker units: uint256 taking = CalculatorHelper.calculateTakingAmount( 40_000_000, // makingAmount (partial fill) 100_000_000, // order.makerAmount 60_000_000 // order.takerAmount ); // taking = (40_000_000 * 60_000_000) / 100_000_000 = 24_000_000 (24 USDC) ``` -------------------------------- ### wrap / unwrap Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Allows addresses with the WRAPPER_ROLE to mint pUSD by wrapping underlying assets (like USDCe) and burn pUSD by unwrapping them back to the underlying asset. End users interact with this functionality indirectly through ramp contracts. ```APIDOC ## wrap / unwrap (CollateralToken) ### Description Mint and burn pUSD. Only addresses with `WRAPPER_ROLE` can call these functions directly. End users interact via the ramp contracts. ### Method - `wrap(_asset, _to, _amount, _callbackReceiver, _data)`: Mints pUSD. - `unwrap(_asset, _to, _amount, _callbackReceiver, _data)`: Burns pUSD. ### Parameters - **_asset** (address) - The address of the underlying asset (e.g., USDCe). - **_to** (address) - The recipient address. - **_amount** (uint256) - The amount to wrap or unwrap (in the underlying asset's decimals). - **_callbackReceiver** (address) - Optional callback receiver address. - **_data** (bytes) - Optional data for the callback. ### Behavior - `wrap` emits `Wrapped(caller, asset, recipient, amount)`. - `unwrap` emits `Unwrapped(caller, asset, recipient, amount)`. - `wrap`: Recipient receives pUSD; underlying asset sent to VAULT. - `unwrap`: Underlying asset transferred from VAULT to recipient; pUSD burned. ``` -------------------------------- ### Run Tests Matching Pattern (Forge) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Filters and runs test functions whose names match the provided regular expression pattern. ```bash forge test -m PATTERN ``` -------------------------------- ### Configure Fee Parameters - CTFExchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Set the fee receiver address and the maximum fee rate. The maximum fee rate is set in basis points and has a ceiling. ```Solidity // Called by: onlyAdmin CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // Update the fee collection address exchange.setFeeReceiver(0xNewTreasury); // Emits: FeeReceiverUpdated(newReceiver) // Set the protocol max to 2% (200 basis points); default is 500 (5%) exchange.setMaxFeeRate(200); // Emits: MaxFeeRateUpdated(200) // Reverts with MaxFeeRateExceedsCeiling() if >= 10000 // Read current settings address receiver = exchange.getFeeReceiver(); // → 0xNewTreasury uint256 maxFeeBps = exchange.getMaxFeeRate(); // → 200 ``` -------------------------------- ### Solidity License Identifiers Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Specifies the SPDX license identifiers to be used for different types of Solidity files within the project. Production contracts require BUSL-1.1, while tests and dev helpers use MIT. ```Solidity // SPDX-License-Identifier: BUSL-1.1 ``` ```Solidity // SPDX-License-Identifier: MIT ``` ```Solidity // SPDX-License-Identifier: LGPL-3.0-or-later ``` -------------------------------- ### preapproveOrder Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Allows an operator to pre-register an order, bypassing future signature checks for gas savings on subsequent `matchOrders` calls. ```APIDOC ## preapproveOrder — operator pre-registers an order, bypassing future signature checks ### Description Verifies the order signature once and stores the hash in the `preapproved` mapping. On subsequent `matchOrders` calls the operator can pass `signature = ""` to skip ECDSA recovery, saving calldata gas. ### Usage ```solidity // Called by: onlyOperator CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // The order must carry a valid signature the first time exchange.preapproveOrder(myOrder); // Emits: OrderPreapproved(orderHash) // Now submit with empty signature — skips ECDSA recovery myOrder.signature = ""; exchange.matchOrders(conditionId, myOrder, makerOrders, takerFill, makerFills, takerFee, makerFees); ``` ### Parameters - `myOrder` (Order) - The order object to preapprove, including a valid signature. ``` -------------------------------- ### wrap — permissionless wrap into pUSD Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Allows any address to wrap USDC or USDCe into pUSD through the onramp without requiring an approval signature. ```APIDOC ## wrap — permissionless wrap into pUSD Any address can wrap USDC or USDCe into pUSD through the onramp without an approval signature. ```solidity import { CollateralOnramp } from "src/collateral/CollateralOnramp.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; CollateralOnramp onramp = CollateralOnramp(0x93070a847efEf7F70739046A929D47a521F5B8ee); address USDCE = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; // Step 1: approve the onramp to pull USDCe from the caller IERC20(USDCE).approve(address(onramp), 200_000_000); // 200 USDC // Step 2: wrap — user receives pUSD at 1:1 onramp.wrap({ _asset: USDCE, _to: msg.sender, // recipient of pUSD _amount: 200_000_000 }); // Emits: Wrapped(onramp, USDCE, msg.sender, 200_000_000) on the CollateralToken ``` ``` -------------------------------- ### EIP-712 Witness-Gated Wrap/Unwrap Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt A permissioned variant of the collateral ramp requiring an EIP-712 signature from a WITNESS_ROLE holder. Includes nonce and deadline for replay protection and time-based validity. Ensure correct signature, nonce, and deadline are provided. ```solidity import { PermissionedRamp } from "src/collateral/PermissionedRamp.sol"; PermissionedRamp ramp = PermissionedRamp(0xebC2459Ec962869ca4c0bd1E06368272732BCb08); // Off-chain: witness signs over: // keccak256("Wrap(address sender,address asset,address to,uint256 amount,uint256 nonce,uint256 deadline)") // with domain { name: "PermissionedRamp", version: "1", chainId: 137, verifyingContract: rampAddress } uint256 currentNonce = ramp.nonces(msg.sender); // must match exactly uint256 deadline = block.timestamp + 3600; // 1-hour validity window // Approve ramp to pull USDCe IERC20(USDCE).approve(address(ramp), 50_000_000); ramp.wrap({ _asset: USDCE, _to: msg.sender, _amount: 50_000_000, _nonce: currentNonce, _deadline: deadline, _signature: witnessSignature // from a WITNESS_ROLE holder }); // Reverts: ExpiredDeadline(), InvalidNonce(), InvalidSignature() // Permissioned unwrap (pUSD → USDCe) IERC20(COLLATERAL_TOKEN).approve(address(ramp), 50_000_000); ramp.unwrap({ _asset: USDCE, _to: msg.sender, _amount: 50_000_000, _nonce: ramp.nonces(msg.sender), _deadline: block.timestamp + 600, _signature: witnessUnwrapSig }); ``` -------------------------------- ### Solidity Formatting Enforcement Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Configuration for code formatting enforced by `forge fmt`. This includes line length, tab width, bracket spacing, comment wrapping, and single-line statement blocks. ```Solidity /*-------------------------------------------------------------- SECTION NAME --------------------------------------------------------------*/ ``` -------------------------------- ### Revert Expectation Pattern Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Use vm.expectRevert with the specific error selector before the call that is expected to revert. ```solidity vm.expectRevert(ErrorName.selector) ``` -------------------------------- ### Solidity Test Contract Structure Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Tests extend BaseExchangeTest, which provides order and deal helpers. Ensure new tests are added to appropriate contract files and not base contracts. ```solidity contract MatchOrdersTest is BaseExchangeTest { function test_MatchOrders_Complementary() public { ... } function test_MatchOrders_Mint() public { ... } function test_MatchOrders_revert_NotCrossing() public { ... } } ``` -------------------------------- ### User Self-Pause Mechanism Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Any user can initiate a self-pause for their account, which becomes effective after a configurable block delay. During this delay, the user can cancel the pause. Once active, orders from a paused maker will be rejected. Check the current pause state using isUserPaused and userPausedBlockAt. ```solidity // Called by: any user (no role required) // Deployed exchange address CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // Initiate self-pause — effective after userPauseBlockInterval blocks exchange.pauseUser(); // Emits: UserPaused(msg.sender, block.number + interval) // Cancel before the pause activates, or re-enable after being paused exchange.unpauseUser(); // Emits: UserUnpaused(msg.sender) // Check current pause state bool paused = exchange.isUserPaused(0xUserAddress); uint256 blockAt = exchange.userPausedBlockAt(0xUserAddress); // 0 = not paused uint256 interval = exchange.userPauseBlockInterval(); // default: 100 ``` -------------------------------- ### CTF Exchange V2 Source Code Directory Structure Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Details the file organization for the CTF Exchange V2 smart contracts, including core exchange logic, interfaces, libraries, and mixins. ```text src/ ├── exchange/ │ ├── CTFExchange.sol — Main entry point │ ├── interfaces/ — Interface definitions │ ├── libraries/ │ │ ├── Structs.sol — Order, OrderStatus, enums │ │ ├── CalculatorHelper.sol — Price math (assembly-optimized) │ │ ├── TransferHelper.sol — Unified ERC20/ERC1155 transfers │ │ ├── Create2Lib.sol — CREATE2 address computation │ │ ├── PolyProxyLib.sol — Proxy wallet address derivation │ │ └── PolySafeLib.sol — Gnosis Safe address derivation │ └── mixins/ — Modular functionality ├── adapters/ │ ├── CtfCollateralAdapter.sol — CTF ↔ PMCT adapter │ └── NegRiskCtfCollateralAdapter.sol — Negative Risk variant └── collateral/ ├── CollateralToken.sol — PMCT (PolyMarket Collateral Token) ├── CollateralOnramp.sol — Wrap USDC/USDCe → PMCT └── CollateralOfframp.sol — Unwrap PMCT → USDC/USDCe ``` -------------------------------- ### Convert NO Tokens to YES Tokens (Negative-Risk Markets) Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Convert NO positions into YES positions in multi-outcome negative-risk markets. This function pulls NO tokens, converts them via the NegRiskAdapter (potentially applying a fee), and returns YES tokens for outcomes not in the indexSet. Excess USDCe is wrapped to pUSD and sent to the caller. ```solidity import { NegRiskCtfCollateralAdapter } from "src/adapters/NegRiskCtfCollateralAdapter.sol"; NegRiskCtfCollateralAdapter nrAdapter = NegRiskCtfCollateralAdapter(0xAdA200001000ef00D07553cEE7006808F895c6F1); bytes32 marketId = 0xMARKET_ID; uint256 indexSet = 0x3; // bitmask: bits 0 and 1 set → questions 0 and 1 uint256 amount = 10_000_000; // 10 tokens of each selected NO position // Approve CTF ERC1155 transfer IERC1155(ctf).setApprovalForAll(address(nrAdapter), true); nrAdapter.convertPositions(marketId, indexSet, amount); ``` -------------------------------- ### Run Tests Matching Contract Pattern (Forge) Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Filters and runs tests within contracts whose names match the provided regular expression pattern. ```bash forge test --mc PATTERN ``` -------------------------------- ### Solidity Pragma Directives Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/CLAUDE.md Defines the pragma solidity version requirements for various contract types. Production contracts use an exact version, while libraries and tests use version ranges. ```Solidity pragma solidity 0.8.34; ``` ```Solidity pragma solidity <0.9.0; ``` ```Solidity pragma solidity >=0.5.1; ``` -------------------------------- ### pauseUser / unpauseUser Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Allows any user to initiate a self-pause of their account, which becomes effective after a configurable block delay. Users can cancel the pause before it activates or re-enable their account after being paused. ```APIDOC ## pauseUser / unpauseUser ### Description Any user (maker) can pause their own account. The pause activates after a configurable block delay. During the delay window, the user can cancel by calling `unpauseUser`. Once active, `matchOrders` will revert for any order where the maker is paused. ### Method - `pauseUser()`: Initiates self-pause. - `unpauseUser()`: Cancels the self-pause or re-enables a paused account. ### Behavior - `pauseUser()` emits `UserPaused(msg.sender, block.number + interval)`. - `unpauseUser()` emits `UserUnpaused(msg.sender)`. - `matchOrders` reverts if the maker's account is paused. ``` -------------------------------- ### hashOrder Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Computes the EIP-712 order digest, which is used both off-chain for signing and on-chain for order status lookup. ```APIDOC ## hashOrder — compute the EIP-712 order digest ### Description Returns the domain-separated hash of an order. Used off-chain to produce the bytes that must be ECDSA-signed, and on-chain to look up order status. ### Usage ```solidity import { CTFExchange } from "src/exchange/CTFExchange.sol"; import { Order } from "src/exchange/libraries/Structs.sol"; CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); Order memory order = ...; // populate all fields except signature bytes32 digest = exchange.hashOrder(order); // Off-chain equivalent (ethers.js v6): // const domain = { name: "Polymarket CTF Exchange", version: "2", chainId: 137, verifyingContract: exchangeAddress }; // const types = { Order: [...fields] }; // const digest = TypedDataEncoder.hash(domain, types, order); // // Sign with EOA: // const sig = await signer.signTypedData(domain, types, order); // order.signature = sig; ``` ### Parameters - `order` (Order) - The order object to hash (signature field should be empty). ### Returns - `bytes32` - The EIP-712 digest of the order. ``` -------------------------------- ### setFeeReceiver / setMaxFeeRate Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Configures the protocol's fee parameters, including the address where fees are collected and the maximum allowable fee rate. ```APIDOC ## setFeeReceiver / setMaxFeeRate — configure fee parameters ### Description Allows admins to configure fee collection and limits. ### Usage ```solidity // Called by: onlyAdmin CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // Update the fee collection address exchange.setFeeReceiver(0xNewTreasury); // Emits: FeeReceiverUpdated(newReceiver) // Set the protocol max to 2% (200 basis points); default is 500 (5%) exchange.setMaxFeeRate(200); // Emits: MaxFeeRateUpdated(200) // Reverts with MaxFeeRateExceedsCeiling() if >= 10000 // Read current settings address receiver = exchange.getFeeReceiver(); // → 0xNewTreasury uint256 maxFeeBps = exchange.getMaxFeeRate(); // → 200 ``` ### Parameters - `newReceiver` (address) - The new address to receive collected fees. - `maxFeeRate` (uint256) - The maximum fee rate allowed, in basis points (1/100th of a percent). ### Returns - `getFeeReceiver()` - Returns the current fee receiver address. - `getMaxFeeRate()` - Returns the current maximum fee rate in basis points. ``` -------------------------------- ### Permissionless Wrap USDC/USDCe into pUSD Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Allows any address to wrap USDC or USDCe into pUSD without an approval signature. Ensure the onramp contract address and collateral token addresses are correct. ```solidity import { CollateralOnramp } from "src/collateral/CollateralOnramp.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; CollateralOnramp onramp = CollateralOnramp(0x93070a847efEf7F70739046A929D47a521F5B8ee); address USDCE = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; // Step 1: approve the onramp to pull USDCe from the caller IERC20(USDCE).approve(address(onramp), 200_000_000); // 200 USDC // Step 2: wrap — user receives pUSD at 1:1 onramp.wrap({ _asset: USDCE, _to: msg.sender, // recipient of pUSD _amount: 200_000_000 }); // Emits: Wrapped(onramp, USDCE, msg.sender, 200_000_000) on the CollateralToken ``` -------------------------------- ### Mint Outcome Tokens from pUSD using CtfCollateralAdapter Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Accepts pUSD, unwraps it to USDCe, calls `splitPosition` on the legacy CTF, and returns ERC1155 YES/NO outcome tokens. Requires approval for pUSD transfer to the adapter. Note the unused parameters for interface compatibility. ```solidity import { CtfCollateralAdapter } from "src/adapters/CtfCollateralAdapter.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; CtfCollateralAdapter adapter = CtfCollateralAdapter(0xADa100874d00e3331D00F2007a9c336a65009718); address COLLATERAL_TOKEN = 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB; bytes32 conditionId = 0xABC...; // market condition identifier // Approve pUSD transfer to the adapter IERC20(COLLATERAL_TOKEN).approve(address(adapter), 100_000_000); // Split 100 pUSD into 100 YES + 100 NO tokens // (unused address/bytes32/uint256[] params retained for IConditionalTokens interface compatibility) adapter.splitPosition(address(0), bytes32(0), conditionId, new uint256[](0), 100_000_000); // Result: caller receives 100 YES tokens + 100 NO tokens (ERC1155) ``` -------------------------------- ### convertPositions Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Converts a set of NO positions into the corresponding YES positions (minus a fee) via the NegRiskAdapter. This is used in negative-risk markets where multiple outcomes compete. ```APIDOC ## convertPositions ### Description Convert NO tokens into YES tokens in multi-outcome markets. Used in negative-risk markets where multiple outcomes compete, this function converts a set of NO positions into the corresponding YES positions (minus a fee) via the NegRiskAdapter. ### Method `convertPositions` ### Parameters - `marketId` (bytes32): The ID of the market. - `indexSet` (uint256): A bitmask representing the NO positions to convert. Bits 0 and 1 set correspond to questions 0 and 1. - `amount` (uint256): The amount of tokens to convert for each selected NO position. ### Request Example ```solidity import { NegRiskCtfCollateralAdapter } from "src/adapters/NegRiskCtfCollateralAdapter.sol"; NegRiskCtfCollateralAdapter nrAdapter = NegRiskCtfCollateralAdapter(0xAdA200001000ef00D07553cEE7006808F895c6F1); bytes32 marketId = 0xMARKET_ID; uint256 indexSet = 0x3; // bitmask: bits 0 and 1 set → questions 0 and 1 uint256 amount = 10_000_000; // 10 tokens of each selected NO position // Approve CTF ERC1155 transfer IERC1155(ctf).setApprovalForAll(address(nrAdapter), true); nrAdapter.convertPositions(marketId, indexSet, amount); // Pulls NO tokens for questions in indexSet // Converts via NegRiskAdapter (may apply feeBips) // Returns YES tokens for questions NOT in indexSet to caller // Any excess USDCe received is wrapped to pUSD and sent to caller ``` ### Response - YES tokens for questions NOT in indexSet are returned to the caller. - Any excess USDCe received is wrapped to pUSD and sent to the caller. ``` -------------------------------- ### CTF Exchange V2 Contract Structure Source: https://github.com/polymarket/ctf-exchange-v2/blob/main/README.md Overview of the main contracts and their modular components within the CTF Exchange V2 architecture. ```text CTFExchange ├── Auth — Admin/operator role management ├── Trading — Order matching and settlement │ ├── Hashing — EIP-712 typed data hashing │ ├── AssetOperations — ERC20/ERC1155 transfers, CTF mint/merge │ ├── Events — Assembly-optimized event emission │ ├── Fees — Fee validation and collection │ ├── UserPausable — Per-user pause with block delay │ └── Signatures — EOA, Proxy, Safe, EIP-1271 verification ├── Pausable — Global trading pause └── ERC1155TokenReceiver ``` -------------------------------- ### Manage Admins - CTFExchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Add or remove admin roles. Admins can pause trading, update fees, and manage operators and other admins. At least one admin must remain. ```Solidity // Called by: onlyAdmin CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); exchange.addAdmin(0xNewAdmin); // Emits: NewAdmin(newAdmin, caller) exchange.removeAdmin(0xOldAdmin); // Emits: RemovedAdmin(oldAdmin, caller) // Reverts with LastAdmin() if trying to remove the last admin ``` -------------------------------- ### Match Orders in CTF Exchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt The operator calls this function to match taker and maker orders. It validates signatures, checks prices, executes settlement, and emits relevant events. Requires `onlyOperator` and `notPaused` modifiers. ```solidity // Called by: onlyOperator // Modifiers: notPaused import { CTFExchange } from "src/exchange/CTFExchange.sol"; import { Order, Side, SignatureType } from "src/exchange/libraries/Structs.sol"; CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); bytes32 conditionId = 0xABC123...; // market condition ID // Taker: BUY 50 YES tokens at 0.60 Order memory taker = Order({ salt: 1001, maker: 0xTakerAddress, signer: 0xTakerAddress, tokenId: YES_TOKEN_ID, makerAmount: 50_000_000, // 50 USDC takerAmount: 30_000_000, // 30 YES tokens minimum side: Side.BUY, signatureType: SignatureType.EOA, timestamp: block.timestamp * 1000, metadata: bytes32(0), builder: bytes32(0), signature: takerSig }); // Maker: SELL 30 YES tokens at 0.60 (price crosses) Order[] memory makers = new Order[](1); makers[0] = Order({ salt: 2002, maker: 0xMakerAddress, signer: 0xMakerAddress, tokenId: YES_TOKEN_ID, // same token, opposite side → COMPLEMENTARY makerAmount: 30_000_000, // 30 YES tokens to sell takerAmount: 18_000_000, // 18 USDC minimum side: Side.SELL, signatureType: SignatureType.EOA, timestamp: block.timestamp * 1000, metadata: bytes32(0), builder: bytes32(0), signature: makerSig }); uint256 takerFillAmount = 30_000_000; // fill 30 USDC worth (maker amount units) uint256[] memory makerFills = new uint256[](1); makerFills[0] = 30_000_000; // fill all 30 YES tokens from maker uint256 takerFee = 150_000; // 0.15 USDC taker fee (≤ 5% default max) uint256[] memory makerFees = new uint256[](1); makerFees[0] = 90_000; // 0.09 USDC maker fee // Requires: msg.sender is an approved operator exchange.matchOrders( conditionId, taker, makers, takerFillAmount, makerFills, takerFee, makerFees ); // Emits: OrderFilled (per maker), OrderFilled (taker), OrdersMatched, FeeCharged (per fee) ``` -------------------------------- ### Manage Operators - CTFExchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Add or remove operators, who are authorized to call matchOrders, preapproveOrder, and invalidatePreapprovedOrder. Operators can also self-renounce. ```Solidity // Called by: onlyAdmin exchange.addOperator(0xOperatorBot); // Emits: NewOperator exchange.removeOperator(0xRetiredBot); // Emits: RemovedOperator // Operators can self-remove (e.g., in an emergency) // Called by: onlyOperator exchange.renounceOperatorRole(); // Emits: RemovedOperator(msg.sender, msg.sender) // Read-only checks bool isOp = exchange.isOperator(0xSomeAddress); bool isAdmin = exchange.isAdmin(0xSomeAddress); ``` -------------------------------- ### addAdmin / removeAdmin Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Manages the set of admin addresses. Admins have privileges to pause trading, update fees, and manage operators and other admins. ```APIDOC ## addAdmin / removeAdmin — manage the admin set ### Description The admin role can pause/unpause trading globally, update fee parameters, add/remove operators, and manage other admins. At least one admin must remain. ### Usage ```solidity // Called by: onlyAdmin CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); exchange.addAdmin(0xNewAdmin); // Emits: NewAdmin(newAdmin, caller) exchange.removeAdmin(0xOldAdmin); // Emits: RemovedAdmin(oldAdmin, caller) // Reverts with LastAdmin() if trying to remove the last admin ``` ### Parameters - `newAdmin` (address) - The address to add as an admin. - `oldAdmin` (address) - The address to remove as an admin. ``` -------------------------------- ### Validate Order Signature and Status Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt This read-only function validates an order's signature, checks if it's already filled, verifies the maker is not paused, and ensures `makerAmount` is positive. It reverts with custom errors if invalid. ```solidity import { CTFExchange } from "src/exchange/CTFExchange.sol"; import { Order } from "src/exchange/libraries/Structs.sol"; CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // Will revert with a descriptive custom error if invalid; returns normally if valid. try exchange.validateOrder(myOrder) { // Order is valid and ready to be submitted to matchOrders } catch Error(string memory reason) { // Legacy string revert (unlikely — exchange uses custom errors) } catch (bytes memory lowLevelData) { // Custom errors: InvalidSignature, OrderAlreadyFilled, ZeroMakerAmount, UserIsPaused } ``` -------------------------------- ### calculateTakingAmount Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt An internal library function used by the exchange to compute how many 'taker-side' tokens to transfer given a partial maker fill. ```APIDOC ## calculateTakingAmount ### Description Internal library function used by the exchange to compute how many 'taker-side' tokens to transfer given a partial maker fill. ### Method `calculateTakingAmount` ### Parameters - `makingAmount` (uint256): The amount of maker tokens being filled (can be a partial fill). - `makerAmount` (uint256): The total amount of maker tokens in the order. - `takerAmount` (uint256): The total amount of taker tokens in the order. ### Calculation Formula `takingAmount = (makingAmount * takerAmount) / makerAmount` ### Request Example ```solidity import { CalculatorHelper } from "src/exchange/libraries/CalculatorHelper.sol"; // Example: order has makerAmount=100, takerAmount=60 (price = 0.60) // Filling 40 maker units: uint256 taking = CalculatorHelper.calculateTakingAmount( 40_000_000, // makingAmount (partial fill) 100_000_000, // order.makerAmount 60_000_000 // order.takerAmount ); // taking = (40_000_000 * 60_000_000) / 100_000_000 = 24_000_000 (24 USDC) ``` ### Response - `taking` (uint256): The calculated amount of taker-side tokens to transfer. ``` -------------------------------- ### Global Trading Halt Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Admins can immediately halt all order matching operations across the entire exchange by calling pauseTrading. Trading can be resumed by calling unpauseTrading. ```solidity // Called by: onlyAdmin exchange.pauseTrading(); // All matchOrders calls revert until unpaused exchange.unpauseTrading(); ``` -------------------------------- ### Preapprove Order - CTFExchange Source: https://context7.com/polymarket/ctf-exchange-v2/llms.txt Operator pre-registers an order to bypass future signature checks. The order must carry a valid signature the first time it's preapproved. ```Solidity // Called by: onlyOperator CTFExchange exchange = CTFExchange(0xE111180000d2663C0091e4f400237545B87B996B); // The order must carry a valid signature the first time exchange.preapproveOrder(myOrder); // Emits: OrderPreapproved(orderHash) // Now submit with empty signature — skips ECDSA recovery myOrder.signature = ""; exchange.matchOrders(conditionId, myOrder, makerOrders, takerFill, makerFills, takerFee, makerFees); ```