Skip to main content

Running Tests

The Polymarket CTF Exchange uses Foundry’s testing framework for comprehensive test coverage.

Basic Test Execution

# Run all tests
forge test

Verbosity Levels

Foundry provides different verbosity levels for test output:
-v
flag
Basic test results (pass/fail)
-vv
flag
Shows logs emitted during tests
-vvv
flag
Shows stack traces for failed tests (recommended for debugging)
-vvvv
flag
Shows stack traces for all tests and setup
-vvvvv
flag
Shows full internal EVM execution traces
Use -vvv when debugging failed tests to see detailed stack traces and revert reasons.

Test Structure

Base Test Contract

All exchange tests inherit from BaseExchangeTest.sol, which provides common setup and utilities:
contract BaseExchangeTest is TestHelper, IAuthEE, IFeesEE, IRegistryEE, IPausableEE, ITradingEE, ISignaturesEE {
    USDC public usdc;
    IConditionalTokens public ctf;
    CTFExchange public exchange;

    bytes32 public constant questionID = hex"1234";
    bytes32 public conditionId;
    uint256 public yes;
    uint256 public no;

    address public admin = alice;
    uint256 internal bobPK = 0xB0B;
    uint256 internal carlaPK = 0xCA414;
    address public bob;
    address public carla;

    function setUp() public virtual {
        // Initialize test accounts
        bob = vm.addr(bobPK);
        carla = vm.addr(carlaPK);

        // Deploy mock USDC and CTF
        usdc = new USDC();
        ctf = IConditionalTokens(Deployer.ConditionalTokens());

        // Prepare condition and position IDs
        conditionId = _prepareCondition(admin, questionID);
        yes = _getPositionId(2);
        no = _getPositionId(1);

        // Deploy exchange
        vm.startPrank(admin);
        exchange = new CTFExchange(address(usdc), address(ctf), address(0), address(0));
        exchange.registerToken(yes, no, conditionId);
        exchange.addOperator(bob);
        exchange.addOperator(carla);
        vm.stopPrank();
    }
}

Helper Functions

The base test provides useful helper functions:
// Create and sign an order
function _createAndSignOrder(
    uint256 pk,
    uint256 tokenId,
    uint256 makerAmount,
    uint256 takerAmount,
    Side side
) internal returns (Order memory)

Test Categories

Core Exchange Tests (CTFExchange.t.sol)

Tests for core exchange functionality:
function testAuth() public
function testAuthRemoveAdmin() public
function testAuthNotAdmin() public
function testAuthRenounce() public
Tests admin and operator role management, including adding, removing, and renouncing roles.
function testValidate() public
function testValidateInvalidSig() public
function testValidateInvalidNonce() public
function testValidateInvalidExpiration() public
function testValidateDuplicateOrder() public
Tests order validation logic including signatures, nonces, expirations, and duplicate prevention.
function testFillOrder() public
function testFillOrderPartial() public
function testFillOrderWithFees() public
function testFuzzFillOrderWithFees(uint128 fillAmount, uint16 feeRateBps) public
Tests single order filling with various scenarios including partial fills and fee calculations.
function testCancelOrder(uint256 makerAmount, uint256 takerAmount, uint256 tokenId) public
function testCancelOrders(uint256 makerAmount, uint256 takerAmount, uint256 tokenId) public
function testCancelOrderNotOwner() public
Tests order cancellation functionality and permission checks.
function testRegisterToken(uint256 _token0, uint256 _token1, uint256 _conditionId) public
function testRegisterTokenRevertCases() public
Tests complementary token pair registration.
function testPause() public
Tests emergency pause functionality for halting trading.

Order Matching Tests (MatchOrders.t.sol)

Tests for batch order matching and atomic swaps:
Tests matching BUY and SELL orders for the same token:
function testMatchTypeComplementary() public {
    // YES buy vs YES sell orders
    Order memory buy = _createAndSignOrder(bobPK, yes, 60_000_000, 100_000_000, Side.BUY);
    Order memory sellA = _createAndSignOrder(carlaPK, yes, 50_000_000, 25_000_000, Side.SELL);
    Order memory sellB = _createAndSignOrder(carlaPK, yes, 100_000_000, 50_000_000, Side.SELL);
    
    // Match orders...
}
This tests direct token-to-collateral swaps.

ERC-1271 Signature Tests (ERC1271Signature.t.sol)

Tests for smart contract wallet signature validation using the ERC-1271 standard.

Fuzz Testing

The exchange uses extensive fuzz testing to discover edge cases:

Fuzz Test Examples

// Fuzz test with multiple parameters
function testFuzzFillOrderWithFees(
    uint128 fillAmount,
    uint16 feeRateBps
) public {
    uint256 makerAmount = 50_000_000;
    uint256 takerAmount = 100_000_000;

    // Constrain inputs to valid ranges
    vm.assume(
        fillAmount <= makerAmount && 
        feeRateBps < exchange.getMaxFeeRate()
    );

    // Test order filling with random parameters
    Order memory order = _createAndSignOrderWithFee(
        bobPK, yes, makerAmount, takerAmount, feeRateBps, Side.BUY
    );
    
    // Verify behavior across parameter space
}

Fuzz Configuration

Configure fuzz test runs in foundry.toml:
[profile.default.fuzz]
runs = 256
Run intensive fuzz tests:
FOUNDRY_PROFILE=intense forge test

Gas Snapshots

The exchange includes gas snapshot tests to monitor gas efficiency:

Running Gas Snapshot Tests

forge test --match-contract GasSnapshots_Test --gas-report

Gas Snapshot Test Patterns

The GasSnapshots.t.sol file includes tests for various matching scenarios:
function test_complementary_1maker() public {
    vm.prank(bob);
    vm.startSnapshotGas("complementary_1maker");
    exchange.matchOrders(takerOrder, makerOrders, takerFillAmount, fillAmounts);
    vm.stopSnapshotGas();
}

function test_complementary_5makers() public { /* ... */ }
function test_complementary_10makers() public { /* ... */ }
function test_complementary_20makers() public { /* ... */ }

Viewing Gas Reports

Generate a detailed gas report:
forge test --gas-report
This outputs a table showing gas usage for each function:
| Contract       | Function              | min  | avg    | max    |
|----------------|-----------------------|------|--------|--------|
| CTFExchange    | fillOrder             | 2505 | 151203 | 186820 |
| CTFExchange    | matchOrders           | 5320 | 231052 | 425316 |
| CTFExchange    | cancelOrder           | 1234 | 12453  | 23672  |

Saving Gas Snapshots

Create a baseline snapshot:
forge snapshot --match-contract GasSnapshots_Test
This creates a .gas-snapshot file with gas measurements:
GasSnapshots_Test:test_complementary_1maker() (gas: 145283)
GasSnapshots_Test:test_complementary_5makers() (gas: 523441)
GasSnapshots_Test:test_mint_1maker() (gas: 198234)

Comparing Snapshots

After making changes, compare against the baseline:
forge snapshot --match-contract GasSnapshots_Test --diff
Output shows gas changes:
GasSnapshots_Test:test_complementary_1maker() (gas: -1245 (-0.86%))
GasSnapshots_Test:test_mint_1maker() (gas: +523 (+0.26%))

Coverage Reports

Generate a test coverage report:
forge coverage
For detailed coverage with line-by-line breakdown:
forge coverage --report lcov
genhtml lcov.info -o coverage
open coverage/index.html

Common Test Patterns

Expecting Reverts

Test that functions revert with specific errors:
function testAuthNotAdmin() public {
    vm.expectRevert(NotAdmin.selector);
    exchange.addAdmin(address(1));
}

Expecting Events

Verify that events are emitted correctly:
function testAuth() public {
    vm.expectEmit(true, true, true, true);
    emit NewAdmin(henry, admin);
    
    vm.startPrank(admin);
    exchange.addAdmin(henry);
    vm.stopPrank();
}

Checkpoint Pattern

Test balance changes using checkpoints:
function testFillOrder() public {
    // Checkpoint balances before
    checkpointCollateral(carla);
    checkpointCTF(bob, yes);
    
    // Perform operation
    vm.prank(carla);
    exchange.fillOrder(order, 25_000_000);
    
    // Assert changes from checkpoint
    assertCollateralBalance(carla, 25_000_000);
    assertCTFBalance(bob, yes, 50_000_000);
}

Best Practices

Use Descriptive Names

Name test functions clearly: testFillOrderWithFees not test1

Test Edge Cases

Use fuzz testing to discover edge cases automatically

Check Events

Always verify that events are emitted with correct parameters

Monitor Gas

Run gas snapshots before and after optimization changes
The CTF Exchange has extensive test coverage including unit tests, integration tests, fuzz tests, and gas benchmarks. Always run the full test suite before deployment.

Build docs developers (and LLMs) love