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, including both direct calls and internal calls (e.g., through routers or aggregators).
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")

forkByTxHash

Controls how the EVM state is forked for each transaction replay. This parameter is kept for interface compatibility but is effectively always true internally to ensure correct pre-transaction state.
forkByTxHash: true  // Default and recommended

detailedBlocks

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

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,
        forkByTxHash: true
    })
);

executeBacktestForTransaction

Executes a backtest for a single transaction specified by its hash. This is useful for:
  • Testing assertions against specific known transactions (such as historical exploits)
  • Debugging potential false positives reported during staging (see Testing Strategy for more on the staging workflow)
  • Quickly validating assertion behavior on a problematic transaction without specifying a block range
function executeBacktestForTransaction(
    bytes32 txHash,
    address targetContract,
    bytes memory assertionCreationCode,
    bytes4 assertionSelector,
    string memory rpcUrl
) public returns (BacktestingTypes.BacktestingResults memory results)
Parameters:
  • txHash: The transaction hash to backtest
  • targetContract: The target contract address (assertion adopter)
  • assertionCreationCode: The assertion contract creation code
  • assertionSelector: The assertion function selector
  • rpcUrl: The RPC URL to use
Returns:
  • results: Results struct containing test execution statistics
Example:
bytes32 exploitTxHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef;

BacktestingTypes.BacktestingResults memory results = executeBacktestForTransaction(
    exploitTxHash,
    TARGET_CONTRACT,
    type(MyAssertion).creationCode,
    MyAssertion.assertionInvariant.selector,
    vm.envString("MAINNET_RPC_URL")
);

// Expect the assertion to catch the exploit
assertEq(results.assertionFailures, 1, "Assertion should catch the exploit");

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 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)
forkByTxHashboolFork at exact transaction (kept for interface compatibility)

BacktestingResults

Results struct returned after backtesting execution.
struct BacktestingResults {
    uint256 totalTransactions;      // Total transactions found
    uint256 processedTransactions;  // Transactions validated
    uint256 successfulValidations;  // Passing validations
    uint256 skippedTransactions;    // Transactions where assertion wasn't triggered
    uint256 assertionFailures;      // Protocol violations detected
    uint256 replayFailures;         // Transactions that failed to replay
    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
skippedTransactionsuint256Transactions where the assertion wasn’t triggered (function selector didn’t match)
assertionFailuresuint256Number of protocol violations detected (assertion reverted)
replayFailuresuint256Transactions that failed to replay before assertion could execute
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("Skipped transactions:", results.skippedTransactions);
console.log("Assertion failures:", results.assertionFailures);
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,
    forkByTxHash: true
})
See the Backtesting Guide for more configuration examples including debugging and quick test scenarios.

Learn More