Running Tests
The Polymarket CTF Exchange uses Foundry’s testing framework for comprehensive test coverage.
Basic Test Execution
All Tests
Verbose Output
Specific Pattern
Specific Contract
# Run all tests
forge test
Verbosity Levels
Foundry provides different verbosity levels for test output:
Basic test results (pass/fail)
Shows logs emitted during tests
Shows stack traces for failed tests (recommended for debugging)
Shows stack traces for all tests and setup
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:
Order Creation
Token Minting
Balance Assertions
Balance Checkpoints
// 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:
Complementary Matching
Mint Matching
Merge Matching
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. Tests matching BUY orders for complementary tokens (mints new positions): function testMatchTypeMint () public {
// YES buy vs NO buy - requires minting
Order memory buy = _createAndSignOrder (bobPK, yes, 60_000_000 , 100_000_000 , Side.BUY);
Order memory noBuy = _createAndSignOrder (carlaPK, no, 16_000_000 , 40_000_000 , Side.BUY);
// Exchange mints YES and NO tokens from collateral
}
This tests the exchange’s ability to mint outcome tokens from collateral. Tests matching SELL orders for complementary tokens (merges to collateral): function testMatchTypeMerge () public {
// YES sell vs NO sell - merges to collateral
Order memory yesSell = _createAndSignOrder (bobPK, yes, 100_000_000 , 60_000_000 , Side.SELL);
Order memory noSell = _createAndSignOrder (carlaPK, no, 75_000_000 , 30_000_000 , Side.SELL);
// Exchange merges YES and NO tokens back to collateral
}
This tests the exchange’s ability to merge outcome tokens back to collateral.
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:
Default (256 runs)
Intense (10,000 runs)
[ 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:
Complementary Matches
Mint Matches
Merge Matches
Combo Matches
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 { /* ... */ }
function test_mint_1maker () public {
vm. prank (bob);
vm. startSnapshotGas ( "mint_1maker" );
exchange. matchOrders (takerOrder, makerOrders, takerFillAmount, fillAmounts);
vm. stopSnapshotGas ();
}
function test_mint_5makers () public { /* ... */ }
function test_mint_10makers () public { /* ... */ }
function test_mint_20makers () public { /* ... */ }
function test_merge_1maker () public {
vm. prank (bob);
vm. startSnapshotGas ( "merge_1maker" );
exchange. matchOrders (takerOrder, makerOrders, takerFillAmount, fillAmounts);
vm. stopSnapshotGas ();
}
function test_merge_5makers () public { /* ... */ }
function test_merge_10makers () public { /* ... */ }
function test_merge_20makers () public { /* ... */ }
// Combo: complementary + mint
function test_combo_complementary_mint_10makers () public { /* ... */ }
function test_combo_complementary_mint_20makers () public { /* ... */ }
// Combo: complementary + merge
function test_combo_complementary_merge_10makers () public { /* ... */ }
function test_combo_complementary_merge_20makers () public { /* ... */ }
Viewing Gas Reports
Generate a detailed 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:
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.