Skip to main content
This page provides detailed reference documentation for all backtesting configuration options, API methods, and data structures.
New to backtesting? Start with the Backtesting Guide to understand the basics before diving into this reference.

Configuration Parameters

targetContract

The address of the contract (Assertion Adopter) to test assertions against. The backtesting tool will find all transactions that interact with this contract (either directly or internally, depending on useTraceFilter).
targetContract: 0x5fd84259d66Cd46123540766Be93DFE6D43130D7

endBlock

The last block in the range to test. The tool runs on the block range from endBlock - blockRange to endBlock.
endBlock: 31336940  // Test up to this block

blockRange

Number of blocks to test, counting backwards from endBlock. The tool will test blocks from endBlock - blockRange to endBlock.
blockRange: 100  // Tests blocks 31336841 to 31336940 if endBlock is 31336940
To test against a specific transaction, set blockRange: 1 with the block number containing that transaction. This is useful for validating assertions against known exploit transactions. See the Balancer V2 Rate Manipulation Exploit for a real-world example.

assertionCreationCode

The bytecode of your assertion contract. Use type(YourAssertion).creationCode to get this value.
assertionCreationCode: type(MyAssertion).creationCode

assertionSelector

The function selector of the assertion function to trigger. This determines which assertion method runs during validation.
assertionSelector: MyAssertion.assertionBatchAccountHealth.selector

rpcUrl

The RPC endpoint URL to use for fetching blockchain data. Can also be set via the RPC_URL environment variable.
rpcUrl: "https://sepolia.optimism.io"
// or
rpcUrl: vm.envString("MAINNET_RPC_URL")

useTraceFilter

Controls how transactions are detected in the block range. true (recommended): Uses the trace_filter RPC method
  • Fetches all transactions in a single RPC call per batch
  • Detects both direct calls and internal calls to the target contract
  • RPC calls for 100 blocks: ~1-2 calls
false: Scans each block individually
  • One eth_getBlockByNumber call per block
  • Only detects direct calls where tx.to == targetContract
  • RPC calls for 100 blocks: ~100 calls
Use trace_filter for large block ranges or when you need to detect internal calls. Use block scanning when your RPC provider doesn’t support trace_filter.
useTraceFilter: true  // Recommended for most cases
useTraceFilter: false // For RPC providers and endpoints without trace_filter support

forkByTxHash

Controls how the EVM state is forked for each transaction replay. false (recommended): Forks at the start of the block
  • Uses vm.createSelectFork(rpcUrl, blockNumber)
  • Faster and more RPC-efficient
  • Suitable for most backtesting scenarios
true: Forks at the exact transaction
  • Uses vm.createSelectFork(rpcUrl, txHash)
  • Replays all prior transactions in the block to get exact state
  • Slower but necessary for transactions where transactions earlier in the block change the state of the target contract
Use false for normal backtesting. Use true only when debugging specific failures where exact transaction state is needed.
forkByTxHash: false // Recommended default
forkByTxHash: true  // Only for debugging specific failures

detailedBlocks

Controls logging verbosity. Currently reserved for future functionality.
detailedBlocks: false // Standard output

RPC Call Estimates

Understanding RPC usage helps you plan for rate limits and choose the right RPC provider. Configuration:
useTraceFilter: true
100 blocks, 50 matching transactions:
  • Fetching: 1-2 calls
  • Validation: 50 calls (one per transaction)
  • Total: ~52 RPC calls

Without trace_filter

Configuration:
useTraceFilter: false
100 blocks, 50 matching transactions:
  • Fetching: 100 calls (one per block)
  • Validation: 50 calls (one per transaction)
  • Total: ~150 RPC calls
For large block ranges (>1,000 blocks), use paid RPC providers to avoid rate limiting. The useTraceFilter option significantly reduces RPC calls.

API Reference

executeBacktest

Executes a backtest with the provided configuration and returns detailed results.
function executeBacktest(
    BacktestingTypes.BacktestingConfig memory config
) public returns (BacktestingTypes.BacktestingResults memory results)
Parameters:
  • config: Configuration struct containing all backtesting parameters
Returns:
  • results: Results struct containing test execution statistics
Example:
BacktestingTypes.BacktestingResults memory results = executeBacktest(
    BacktestingTypes.BacktestingConfig({
        targetContract: 0x5fd84259d66Cd46123540766Be93DFE6D43130D7,
        endBlock: 31336940,
        blockRange: 20,
        assertionCreationCode: type(MyAssertion).creationCode,
        assertionSelector: MyAssertion.assertionInvariant.selector,
        rpcUrl: "https://sepolia.optimism.io",
        detailedBlocks: false,
        useTraceFilter: true,
        forkByTxHash: false
    })
);

BacktestingConfig

Configuration struct for backtesting parameters.
struct BacktestingConfig {
    address targetContract;             // Contract to test
    uint256 endBlock;                   // Latest block in range
    uint256 blockRange;                 // Number of blocks to test
    bytes assertionCreationCode;        // Assertion contract bytecode
    bytes4 assertionSelector;           // Assertion function selector
    string rpcUrl;                      // RPC endpoint
    bool detailedBlocks;                // Detailed logging (reserved)
    bool useTraceFilter;                // Use trace_filter API
    bool forkByTxHash;                  // Fork at exact transaction
}
Field Descriptions:
FieldTypeDescription
targetContractaddressContract address to test assertions against
endBlockuint256Most recent block in the test range
blockRangeuint256Number of blocks to test backwards from endBlock
assertionCreationCodebytesBytecode of the assertion contract
assertionSelectorbytes4Function selector of the assertion to execute
rpcUrlstringRPC endpoint URL for blockchain data
detailedBlocksboolEnable detailed logging (reserved for future use)
useTraceFilterboolUse trace_filter API for transaction detection
forkByTxHashboolFork at exact transaction vs. block start

BacktestingResults

Results struct returned after backtesting execution.
struct BacktestingResults {
    uint256 totalTransactions;      // Total transactions found
    uint256 processedTransactions;  // Transactions validated
    uint256 successfulValidations;  // Passing validations
    uint256 failedValidations;      // Failing validations
    uint256 assertionFailures;      // Protocol violations detected
    uint256 unknownErrors;          // Unexpected errors
}
Field Descriptions:
FieldTypeDescription
totalTransactionsuint256Total number of transactions found in the block range
processedTransactionsuint256Number of transactions that were validated
successfulValidationsuint256Transactions that passed validation
failedValidationsuint256Transactions that failed validation
assertionFailuresuint256Number of protocol violations detected
unknownErrorsuint256Unexpected errors during execution
Interpreting Results: The assertionFailures field indicates how many transactions triggered assertion reverts. Since backtesting runs against historical transactions, non-zero values most commonly indicate false positives in your assertion logic rather than actual exploits.
// Check results
if (results.assertionFailures > 0) {
    console.log("Assertion failures detected:", results.assertionFailures);
    console.log("Review assertion logic for false positives or gas issues");
}

// Log detailed results
console.log("Total transactions:", results.totalTransactions);
console.log("Successful validations:", results.successfulValidations);
console.log("Failed validations:", results.failedValidations);
Common Causes of Assertion Failures:
  • False positives: Assertion logic incorrectly flags legitimate protocol behavior (most common)
  • Gas limit exceeded: Assertion ran out of gas (300k limit) during complex operations
  • Assertion bugs: Logic errors in the assertion code
  • Known exploits: If testing against historical exploit blocks, failures may be expected
See the Understanding Results section in the Backtesting Guide for detailed information on all result categories.

Example Configurations

Standard Configuration

Basic configuration suitable for most backtesting scenarios:
BacktestingTypes.BacktestingConfig({
    targetContract: TARGET_CONTRACT,
    endBlock: LATEST_BLOCK,
    blockRange: 100,
    assertionCreationCode: type(MyAssertion).creationCode,
    assertionSelector: MyAssertion.assertionInvariant.selector,
    rpcUrl: vm.envString("MAINNET_RPC_URL"),
    detailedBlocks: false,
    useTraceFilter: true,   // Efficient fetching
    forkByTxHash: false     // Fast validation
})
See the Backtesting Guide for more configuration examples including debugging and quick test scenarios.

Learn More