credible-std
Standard library for implementing assertions in the Phylax Credible Layer (PCL). Provides the core contracts and interfaces needed to create, test, and validate assertions for smart contract security monitoring.
Documentation
Full API documentation is available at: https://phylaxsystems.github.io/credible-std
Installation
Using Foundry (Recommended)
Install the latest stable release:
forge install phylaxsystems/credible-std@0.4.0
Or install from master (latest development version):
forge install phylaxsystems/credible-std
Add the remapping to your remappings.txt:
credible-std/=lib/credible-std/src/
Overview
The Phylax Credible Layer (PCL) is a security framework that enables real-time monitoring and validation of smart contract behavior through assertions. credible-std provides the foundational contracts and utilities needed to implement these assertions.
Key Components
| Contract | Description |
|---|---|
Assertion.sol | Base contract for creating assertions with trigger registration |
Credible.sol | Provides access to the PhEvm precompile for transaction state inspection |
PhEvm.sol | Interface for the PhEvm precompile (state forking, logs, call inputs) |
StateChanges.sol | Type-safe utilities for tracking contract state changes |
TriggerRecorder.sol | Interface for registering assertion triggers |
CredibleTest.sol | Base contract for testing assertions with Forge |
CredibleTestWithBacktesting.sol | Extended test contract with historical transaction backtesting |
Features
- Trigger System: Register triggers for function calls, storage changes, and balance changes
- State Inspection: Fork to pre/post transaction state, inspect logs, call inputs, and storage
- Type-Safe State Changes: Built-in converters for uint256, address, bool, and bytes32 state changes
- Testing Framework: Test assertions locally with Forge before deployment
- Backtesting: Validate assertions against historical blockchain transactions
- Internal Call Detection: Automatically detect transactions that call your contract internally (not just direct calls)
Quick Start
1. Create an Assertion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Assertion} from "credible-std/Assertion.sol";
contract MyAssertion is Assertion {
// Register when this assertion should run
function triggers() external view override {
// Run on any call to the adopter contract
registerCallTrigger(this.checkInvariant.selector);
// Or run only on specific function calls
// registerCallTrigger(this.checkInvariant.selector, ITarget.deposit.selector);
}
// Implement your invariant check
function checkInvariant() external {
address target = ph.getAssertionAdopter();
ph.forkPreTx();
uint256 balanceBefore = target.balance;
ph.forkPostTx();
uint256 balanceAfter = target.balance;
require(balanceAfter >= balanceBefore, "Balance decreased unexpectedly");
}
}
2. Test Your Assertion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {CredibleTest} from "credible-std/CredibleTest.sol";
import {Test} from "forge-std/Test.sol";
import {MyAssertion} from "./MyAssertion.sol";
import {MyContract} from "./MyContract.sol";
contract TestMyAssertion is CredibleTest, Test {
MyContract target;
function setUp() public {
target = new MyContract();
}
function test_assertionPasses() public {
// Register the assertion
cl.assertion({
adopter: address(target),
createData: type(MyAssertion).creationCode,
fnSelector: MyAssertion.checkInvariant.selector
});
// Execute a transaction - assertion runs automatically
target.deposit{value: 1 ether}();
}
function test_assertionFails() public {
cl.assertion({
adopter: address(target),
createData: type(MyAssertion).creationCode,
fnSelector: MyAssertion.checkInvariant.selector
});
// This should revert because the assertion fails
vm.expectRevert("Balance decreased unexpectedly");
target.withdraw(1 ether);
}
}
Run tests with:
pcl test
PhEvm Cheatcodes
Access these via the ph instance inherited from Credible:
| Function | Description |
|---|---|
forkPreTx() | Fork to state before the transaction |
forkPostTx() | Fork to state after the transaction |
forkPreCall(uint256 id) | Fork to state before a specific call |
forkPostCall(uint256 id) | Fork to state after a specific call |
load(address, bytes32) | Load a storage slot value |
getLogs() | Get all logs emitted in the transaction |
getCallInputs(address, bytes4) | Get CALL inputs for target/selector |
getStaticCallInputs(address, bytes4) | Get STATICCALL inputs |
getDelegateCallInputs(address, bytes4) | Get DELEGATECALL inputs |
getAllCallInputs(address, bytes4) | Get all call types |
getStateChanges(address, bytes32) | Get state changes for a slot |
getAssertionAdopter() | Get the adopter contract address |
Trigger Types
Register triggers in your triggers() function:
function triggers() external view override {
// Trigger on any call to the adopter
registerCallTrigger(this.myAssertion.selector);
// Trigger on specific function call
registerCallTrigger(this.myAssertion.selector, ITarget.transfer.selector);
// Trigger on any storage change
registerStorageChangeTrigger(this.myAssertion.selector);
// Trigger on specific storage slot change
registerStorageChangeTrigger(this.myAssertion.selector, bytes32(uint256(0)));
// Trigger on balance change
registerBalanceChangeTrigger(this.myAssertion.selector);
}
Backtesting
Test your assertions against historical blockchain transactions to validate correctness before deployment. The backtesting framework automatically detects both direct calls AND internal/nested calls to your target contract.
Setup
Add to your foundry.toml:
[profile.backtesting]
src = "src"
test = "test"
ffi = true
gas_limit = 100000000
Block Range Backtesting
Test all transactions in a block range:
import {CredibleTestWithBacktesting} from "credible-std/CredibleTestWithBacktesting.sol";
import {BacktestingTypes} from "credible-std/utils/BacktestingTypes.sol";
contract MyBacktest is CredibleTestWithBacktesting {
function testHistoricalTransactions() public {
BacktestingTypes.BacktestingResults memory results = executeBacktest(
BacktestingTypes.BacktestingConfig({
targetContract: 0x1234..., // Contract to monitor
endBlock: 1000000, // End block
blockRange: 100, // Number of blocks to test
assertionCreationCode: type(MyAssertion).creationCode,
assertionSelector: MyAssertion.check.selector,
rpcUrl: "https://eth.llamarpc.com",
detailedBlocks: false, // Verbose block output
forkByTxHash: true // Fork by tx hash for accurate state
})
);
// Check no assertions failed
assertEq(results.assertionFailures, 0, "Assertions failed on historical data");
}
}
Single Transaction Backtesting
Test a specific transaction by hash:
contract MyBacktest is CredibleTestWithBacktesting {
function testSpecificTransaction() public {
bytes32 txHash = 0xabc123...;
BacktestingTypes.BacktestingResults memory results = executeBacktestForTransaction(
txHash,
0x1234..., // Target contract
type(MyAssertion).creationCode,
MyAssertion.check.selector,
"https://eth.llamarpc.com"
);
assertEq(results.assertionFailures, 0);
}
}
Running Backtests
# Run with verbose output
pcl test --ffi -vvvv --match-test testHistoricalTransactions
# Or with the backtesting profile
FOUNDRY_PROFILE=backtesting pcl test -vvvv
Internal Call Detection
The backtesting framework automatically detects transactions that call your target contract internally (e.g., through a router or aggregator). It tries multiple trace APIs with automatic fallback:
- trace_filter - Fastest, requires Erigon or archive node with trace API
- debug_traceBlockByNumber - Slower but widely supported
- debug_traceTransaction - Slowest, per-transaction tracing
- Direct calls only - Fallback when no trace APIs available
Example output:
=== TRANSACTION DISCOVERY ===
Target: 0x1234...
Blocks: 1000000 to 1000100
[INFO] Detecting both direct calls AND internal/nested calls to target
[INFO] Trying trace APIs with automatic fallback...
[TRACE] Using trace_filter API (fastest method for internal call detection)
[TRACE] trace_filter not supported by this RPC endpoint
[TRACE] Falling back to debug_traceBlockByNumber (slower but widely supported)
=== DISCOVERY COMPLETE ===
[INFO] Detection method: debug_traceBlockByNumber
[INFO] Internal calls: ENABLED
Understanding Results
The backtesting framework provides detailed categorization:
| Result | Description |
|---|---|
| Success | Transaction passed assertion validation |
| Skipped | Transaction didn't trigger the assertion (selector mismatch) |
| Assertion Failed | Real protocol violation detected |
| Replay Failure | Transaction reverted before assertion could run |
| Unknown Error | Unexpected failure |
When an assertion fails, the framework automatically replays the transaction with full Foundry tracing enabled, showing the complete execution path for debugging.
State Change Helpers
The StateChanges contract provides type-safe helpers for inspecting storage changes:
// Get state changes as specific types
uint256[] memory uintChanges = getStateChangesUint(target, slot);
address[] memory addrChanges = getStateChangesAddress(target, slot);
bool[] memory boolChanges = getStateChangesBool(target, slot);
bytes32[] memory rawChanges = getStateChangesBytes32(target, slot);
// With mapping key support
uint256[] memory balanceChanges = getStateChangesUint(target, balancesSlot, userKey);
// With slot offset for struct fields
uint256[] memory fieldChanges = getStateChangesUint(target, structSlot, key, fieldOffset);
Resources
License
MIT
Contents
Contents
Target
State Variables
value
uint256 value = 1
Functions
readStorage
function readStorage() external view returns (uint256);
writeStorage
function writeStorage(uint256 value_) public;
incrementStorage
function incrementStorage() public;
writeStorageAndRevert
function writeStorageAndRevert(uint256 value_) external;
receive
receive() external payable;
fallback
fallback() external;
Events
Log
event Log(uint256 value);
Constants
TARGET
Target constant TARGET = Target(payable(0xdCCf1eEB153eF28fdc3CF97d33f60576cF092e9c))
Contents
TestAccessAssertionStorage
Inherits: Assertion
State Variables
someAddress
address public someAddress
Functions
accessAssertionStorage
function accessAssertionStorage() external;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
Contents
- TestCallInputs
- TriggeringTx
- TestForking
- TriggeringTx
- TestGetAdopter
- TriggeringTx
- TestLoad
- TriggeringTx
- TestLogs
- TriggeringTx
- TestStateChanges1
- TriggeringTx
- TestStateChanges2
- TriggeringTx
- TestStateChangesNone
- TriggeringTx
TestCallInputs
Inherits: Assertion
Functions
constructor
constructor() payable;
getCallInputs
function getCallInputs() external view;
callInputsWrongTarget
function callInputsWrongTarget() external view;
callNoSelector
function callNoSelector() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestForking
Inherits: Assertion
State Variables
sum
uint256 public sum = 0
someInitValue
uint256 public someInitValue = 1
Functions
forkSwitchStorage
function forkSwitchStorage() external;
forkSwitchNewDeployedContract
function forkSwitchNewDeployedContract() external;
forkSwitchBalance
function forkSwitchBalance() external;
persistTargetContracts
function persistTargetContracts() external;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestGetAdopter
Inherits: Assertion
Functions
constructor
constructor() payable;
getAdopter
function getAdopter() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestLoad
Inherits: Assertion
Functions
constructor
constructor() payable;
_loadCount
function _loadCount() internal view returns (uint256);
load
function load() external;
loadRandomAccount
function loadRandomAccount() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestLogs
Inherits: Assertion
Functions
constructor
constructor() payable;
getLogs
function getLogs() external;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestStateChanges1
Inherits: Assertion
Functions
constructor
constructor() payable;
getStateChanges1
function getStateChanges1() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestStateChanges2
Inherits: Assertion
Functions
constructor
constructor() payable;
getStateChanges2
function getStateChanges2() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestStateChangesNone
Inherits: Assertion
Functions
constructor
constructor() payable;
getStateChangesNone
function getStateChangesNone() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
Contents
- TestAllCallTrigger
- TriggeringTx
- TestAllStorageChangeTrigger
- TriggeringTx
- TestBalanceTrigger
- TriggeringTx
- TestSomeCallTrigger
- TriggeringTx
- TestStorageSlotChangeTrigger
- TriggeringTx
TestAllCallTrigger
Inherits: Assertion
Functions
triggered
function triggered() external pure;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestAllStorageChangeTrigger
Inherits: Assertion
Functions
triggered
function triggered() external;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestBalanceTrigger
Inherits: Assertion
Functions
triggered
function triggered() external pure;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestSomeCallTrigger
Inherits: Assertion
Functions
triggered
function triggered() external pure;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
TestStorageSlotChangeTrigger
Inherits: Assertion
Functions
triggered
function triggered() external pure;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
Contents
BacktestingTypes
Title: BacktestingTypes
Author: Phylax Systems
Type definitions for the backtesting framework
Contains structs for configuration, transaction data, and results used by CredibleTestWithBacktesting
Structs
TransactionData
Transaction data from blockchain
struct TransactionData {
bytes32 hash;
address from;
address to;
uint256 value;
bytes data;
uint256 blockNumber;
uint256 transactionIndex;
uint256 gasPrice;
uint256 gasLimit;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
}
ValidationDetails
Detailed validation result with error information
struct ValidationDetails {
ValidationResult result;
string errorMessage;
bool isProtocolViolation;
}
BacktestingConfig
Configuration for backtesting runs (block range mode)
Internal call detection is automatic - the system tries trace_filter first, then falls back to debug_traceBlockByNumber, debug_traceTransaction, and finally direct-calls-only if no trace methods are supported.
struct BacktestingConfig {
address targetContract;
uint256 endBlock;
uint256 blockRange;
bytes assertionCreationCode;
bytes4 assertionSelector;
string rpcUrl;
bool detailedBlocks; // Enable detailed block summaries in output
bool forkByTxHash; // Fork by transaction hash for correct pre-tx state; block forks are unsafe.
}
BacktestingResults
Enhanced backtesting results with detailed categorization
struct BacktestingResults {
uint256 totalTransactions;
uint256 processedTransactions; // Transactions that were actually processed
uint256 successfulValidations;
uint256 skippedTransactions; // Transactions where assertion wasn't triggered (selector mismatch)
uint256 assertionFailures; // Real protocol violations
uint256 replayFailures; // Transactions that reverted during replay before assertion
uint256 unknownErrors; // Unexpected failures
}
Enums
ValidationResult
Validation result categories for detailed error analysis
enum ValidationResult {
Success, // Transaction passed all assertions
Skipped, // Transaction didn't trigger the assertion (function selector mismatch)
ReplayFailure, // Transaction reverted during replay before assertion could execute
AssertionFailed, // Assertion logic failed (actual protocol violation)
UnknownError // Unexpected error during validation
}
BacktestingUtils
Title: BacktestingUtils
Author: Phylax Systems
Utility functions for the backtesting framework
Provides parsing, string manipulation, and error decoding utilities used internally by CredibleTestWithBacktesting
Functions
extractDataLine
Extract transaction data from fetcher output
function extractDataLine(string memory output) internal pure returns (string memory);
_isWhitespace
Check if a character is whitespace
function _isWhitespace(bytes1 char) private pure returns (bool);
splitString
Simple pipe-delimited string splitter
function splitString(string memory str, string memory) internal pure returns (string[] memory);
stringToUint
Parse hex or decimal string to uint256
function stringToUint(string memory str) internal pure returns (uint256);
stringToAddress
Parse hex address string to address
function stringToAddress(string memory str) internal pure returns (address);
stringToBytes32
Parse hex string to bytes32
function stringToBytes32(string memory str) internal pure returns (bytes32);
hexStringToBytes
Parse hex string to bytes
function hexStringToBytes(string memory str) internal pure returns (bytes memory);
bytes32ToHex
Convert bytes32 to hex string
function bytes32ToHex(bytes32 data) internal pure returns (string memory);
extractFunctionSelector
Extract function selector from calldata
function extractFunctionSelector(bytes memory data) internal pure returns (string memory);
parseMultipleTransactions
Parse multiple transactions from a single data line
function parseMultipleTransactions(string memory txDataString)
internal
pure
returns (BacktestingTypes.TransactionData[] memory transactions);
_hexCharToUint8
Convert hex character to uint8
function _hexCharToUint8(bytes1 char) private pure returns (uint8);
substring
Helper to get substring for debugging
function substring(string memory str, uint256 start, uint256 len) private pure returns (string memory);
decodeRevertReason
Decode revert reason from error data
function decodeRevertReason(bytes memory data) internal pure returns (string memory);
Parameters
| Name | Type | Description |
|---|---|---|
data | bytes | The error data from a failed call |
Returns
| Name | Type | Description |
|---|---|---|
<none> | string | The decoded revert reason string |
_panicCodeToString
Convert panic code to human-readable string
function _panicCodeToString(uint256 code) private pure returns (string memory);
bytesToHex
Convert bytes to hex string
function bytesToHex(bytes memory data) internal pure returns (bytes memory);
Parameters
| Name | Type | Description |
|---|---|---|
data | bytes | The bytes to convert |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes | The hex string representation |
getErrorTypeString
Get human-readable error type string from validation result
function getErrorTypeString(BacktestingTypes.ValidationResult result) internal pure returns (string memory);
Parameters
| Name | Type | Description |
|---|---|---|
result | BacktestingTypes.ValidationResult | The validation result enum |
Returns
| Name | Type | Description |
|---|---|---|
<none> | string | The human-readable string representation |
startsWith
Check if a string starts with a prefix
function startsWith(string memory str, string memory prefix) internal pure returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
str | string | The string to check |
prefix | string | The prefix to look for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if str starts with prefix |
getDefaultScriptSearchPaths
Get the standard search paths for transaction_fetcher.sh
function getDefaultScriptSearchPaths() internal pure returns (string[] memory);
Returns
| Name | Type | Description |
|---|---|---|
<none> | string[] | Array of paths to check, in order of preference |
Assertion
Inherits: Credible, StateChanges
Title: Assertion
Author: Phylax Systems
Base contract for creating Credible Layer assertions
Inherit from this contract to create custom assertions. Assertions can inspect
transaction state via the inherited ph precompile and register triggers to specify
when the assertion should be executed.
Example:
contract MyAssertion is Assertion {
function triggers() external view override {
registerCallTrigger(this.checkInvariant.selector, ITarget.deposit.selector);
}
function checkInvariant() external {
ph.forkPostTx();
Check invariants...
}
}
State Variables
triggerRecorder
The trigger recorder precompile for registering assertion triggers
Address is derived from a deterministic hash for consistency
TriggerRecorder constant triggerRecorder = TriggerRecorder(address(uint160(uint256(keccak256("TriggerRecorder")))))
Functions
triggers
Used to record fn selectors and their triggers.
function triggers() external view virtual;
registerCallTrigger
Registers a call trigger for the AA without specifying an AA function selector. This will trigger the assertion function on any call to the AA.
function registerCallTrigger(bytes4 fnSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
registerCallTrigger
Registers a call trigger for calls to the AA with a specific AA function selector.
function registerCallTrigger(bytes4 fnSelector, bytes4 triggerSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
triggerSelector | bytes4 | The function selector upon which the assertion will be triggered. |
registerStorageChangeTrigger
Registers storage change trigger for any slot
function registerStorageChangeTrigger(bytes4 fnSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
registerStorageChangeTrigger
Registers storage change trigger for a specific slot
function registerStorageChangeTrigger(bytes4 fnSelector, bytes32 slot) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
slot | bytes32 | The storage slot to trigger on. |
registerBalanceChangeTrigger
Registers balance change trigger for the AA
function registerBalanceChangeTrigger(bytes4 fnSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
console
Title: console
Author: Phylax Systems
Logging library for Credible Layer assertions
Provides console logging functionality within assertion execution context. Logs are captured by the Credible Layer runtime for debugging purposes.
State Variables
CONSOLE_ADDRESS
The console precompile address
Derived from a deterministic hash to ensure consistency with the runtime
address constant CONSOLE_ADDRESS = address(uint160(uint256(keccak256("Kim Jong Un Sucks"))))
Functions
log
Log a string message
Messages are captured by the Credible Layer runtime
function log(string memory message) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
message | string | The message to log |
Credible
Title: Credible
Author: Phylax Systems
Base contract providing access to the PhEvm precompile interface
All assertion contracts should inherit from this contract (via Assertion) to access the PhEvm precompile for reading transaction state, logs, and call inputs.
State Variables
ph
The PhEvm precompile instance for accessing transaction state
The address is derived from a deterministic hash to ensure consistency
PhEvm constant ph = PhEvm(address(uint160(uint256(keccak256("Kim Jong Un Sucks")))))
VmEx
Inherits: Vm
Title: VmEx
Extended Vm interface with assertion testing capabilities
Extends the standard Forge Vm interface with Credible Layer specific cheatcodes
Functions
assertion
Register an assertion for testing
function assertion(address adopter, bytes calldata createData, bytes4 fnSelector) external;
Parameters
| Name | Type | Description |
|---|---|---|
adopter | address | The address of the contract that adopts the assertion |
createData | bytes | The creation bytecode of the assertion contract |
fnSelector | bytes4 | The function selector of the assertion function to test |
CredibleTest
Title: CredibleTest
Author: Phylax Systems
Base contract for testing Credible Layer assertions with Forge
Inherit from this contract (or CredibleTestWithBacktesting) to test assertions locally
State Variables
cl
The extended Vm cheatcode interface for assertion testing
Provides access to assertion-specific cheatcodes
VmEx public constant cl = VmEx(address(uint160(uint256(keccak256("hevm cheat code")))))
CredibleTestWithBacktesting
Inherits: CredibleTest, Test
Title: CredibleTestWithBacktesting
Author: Phylax Systems
Extended CredibleTest with historical transaction backtesting capabilities
Inherit from this contract to test assertions against historical blockchain transactions. Supports two modes:
- Block range mode: Test all transactions in a block range via
executeBacktest(config) - Single transaction mode: Test a specific transaction via
executeBacktestForTransaction(txHash, ...)Example:
contract MyBacktest is CredibleTestWithBacktesting {
function testHistorical() public {
executeBacktest(BacktestingTypes.BacktestingConfig({
targetContract: 0x...,
endBlock: 1000000,
blockRange: 100,
assertionCreationCode: type(MyAssertion).creationCode,
assertionSelector: MyAssertion.check.selector,
rpcUrl: "https://eth.llamarpc.com",
detailedBlocks: false,
forkByTxHash: true
}));
}
}
State Variables
_cachedScriptPath
Cached script path to avoid repeated filesystem lookups
string private _cachedScriptPath
Functions
executeBacktestForTransaction
Execute backtesting for a single transaction by hash (overload for single tx mode)
function executeBacktestForTransaction(
bytes32 txHash,
address targetContract,
bytes memory assertionCreationCode,
bytes4 assertionSelector,
string memory rpcUrl
) public returns (BacktestingTypes.BacktestingResults memory results);
Parameters
| Name | Type | Description |
|---|---|---|
txHash | bytes32 | The transaction hash to backtest |
targetContract | address | The target contract address |
assertionCreationCode | bytes | The assertion contract creation code |
assertionSelector | bytes4 | The assertion function selector |
rpcUrl | string | The RPC URL to use |
Returns
| Name | Type | Description |
|---|---|---|
results | BacktestingTypes.BacktestingResults | The backtesting results |
executeBacktest
Execute backtesting with config struct (block range mode)
function executeBacktest(BacktestingTypes.BacktestingConfig memory config)
public
returns (BacktestingTypes.BacktestingResults memory results);
_getScriptSearchPaths
Get the standard search paths for transaction_fetcher.sh
Override this in your test contract to add custom search paths
function _getScriptSearchPaths() internal view virtual returns (string[] memory);
Returns
| Name | Type | Description |
|---|---|---|
<none> | string[] | Array of paths to check, in order of preference |
_findScriptPath
Find the transaction_fetcher.sh script path
Checks environment variable first, then searches common locations,
and finally uses find to auto-detect the script location.
Override _getScriptSearchPaths() to customize search locations
function _findScriptPath() internal virtual returns (string memory);
Returns
| Name | Type | Description |
|---|---|---|
<none> | string | The path to transaction_fetcher.sh |
_autoDetectScriptPath
Auto-detect the script path using find command
Searches the project directory for transaction_fetcher.sh
function _autoDetectScriptPath() internal virtual returns (string memory);
Returns
| Name | Type | Description |
|---|---|---|
<none> | string | The detected path, or empty string if not found |
_fetchTransactions
Fetch transactions using FFI
Automatically detects internal calls using trace APIs with fallback: trace_filter -> debug_traceBlockByNumber -> debug_traceTransaction -> direct calls only
function _fetchTransactions(address targetContract, uint256 startBlock, uint256 endBlock, string memory rpcUrl)
private
returns (BacktestingTypes.TransactionData[] memory transactions);
_executeBacktestForSingleTransaction
Execute backtesting for a single transaction specified by hash
function _executeBacktestForSingleTransaction(
bytes32 txHash,
address targetContract,
bytes memory assertionCreationCode,
bytes4 assertionSelector,
string memory rpcUrl
) private returns (BacktestingTypes.BacktestingResults memory results);
_fetchTransactionByHash
Fetch a single transaction by hash using FFI
function _fetchTransactionByHash(bytes32 txHash, string memory rpcUrl)
private
returns (BacktestingTypes.TransactionData memory txData);
_parseTransactionFromTsv
Parse transaction data from tab-separated output
function _parseTransactionFromTsv(string memory tsvLine)
private
pure
returns (BacktestingTypes.TransactionData memory txData);
_validateTransaction
Validate a single transaction with detailed error categorization
function _validateTransaction(
address targetContract,
bytes memory assertionCreationCode,
bytes4 assertionSelector,
string memory rpcUrl,
BacktestingTypes.TransactionData memory txData,
bool forkByTxHash
) private returns (BacktestingTypes.ValidationDetails memory validation);
_replayTransactionForTrace
Replay a failed transaction to show the full execution trace
Forks to state before the tx and makes a raw call so Foundry prints the full trace
function _replayTransactionForTrace(
address, // targetContract - unused, kept for interface compatibility
bytes memory, // assertionCreationCode - unused
bytes4, // assertionSelector - unused
string memory rpcUrl,
BacktestingTypes.TransactionData memory txData
) private;
_categorizeAndLogError
Categorize and log error details
function _categorizeAndLogError(BacktestingTypes.ValidationDetails memory validation) private pure;
_printDetailedResults
Print detailed results with error categorization
function _printDetailedResults(
uint256 startBlock,
uint256 endBlock,
BacktestingTypes.BacktestingResults memory results
) private pure;
PhEvm
Title: PhEvm
Author: Phylax Systems
Precompile interface for accessing transaction state within assertions
This interface provides access to the Credible Layer's execution environment, allowing assertions to inspect transaction state, logs, call inputs, and storage changes. The precompile is available at a deterministic address during assertion execution.
Functions
forkPreTx
Fork to the state before the assertion-triggering transaction
Allows inspection of pre-transaction state for comparison
function forkPreTx() external;
forkPostTx
Fork to the state after the assertion-triggering transaction
Allows inspection of post-transaction state for validation
function forkPostTx() external;
forkPreCall
Fork to the state before a specific call execution
Useful for inspecting state at specific points during transaction execution
function forkPreCall(uint256 id) external;
Parameters
| Name | Type | Description |
|---|---|---|
id | uint256 | The call identifier from CallInputs.id |
forkPostCall
Fork to the state after a specific call execution
Useful for inspecting state changes from specific calls
function forkPostCall(uint256 id) external;
Parameters
| Name | Type | Description |
|---|---|---|
id | uint256 | The call identifier from CallInputs.id |
load
Load a storage slot value from any address
function load(address target, bytes32 slot) external view returns (bytes32 data);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The address to read storage from |
slot | bytes32 | The storage slot to read |
Returns
| Name | Type | Description |
|---|---|---|
data | bytes32 | The value stored at the slot |
getLogs
Get all logs emitted during the transaction
Returns logs in emission order
function getLogs() external returns (Log[] memory logs);
Returns
| Name | Type | Description |
|---|---|---|
logs | Log[] | Array of Log structs containing all emitted events |
getAllCallInputs
Get all call inputs for a target and selector (all call types)
Includes CALL, STATICCALL, DELEGATECALL, and CALLCODE
function getAllCallInputs(address target, bytes4 selector) external view returns (CallInputs[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target contract address |
selector | bytes4 | The function selector to filter by |
Returns
| Name | Type | Description |
|---|---|---|
calls | CallInputs[] | Array of CallInputs matching the criteria |
getCallInputs
Get call inputs for regular CALL opcode only
function getCallInputs(address target, bytes4 selector) external view returns (CallInputs[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target contract address |
selector | bytes4 | The function selector to filter by |
Returns
| Name | Type | Description |
|---|---|---|
calls | CallInputs[] | Array of CallInputs from CALL opcodes |
getStaticCallInputs
Get call inputs for STATICCALL opcode only
function getStaticCallInputs(address target, bytes4 selector) external view returns (CallInputs[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target contract address |
selector | bytes4 | The function selector to filter by |
Returns
| Name | Type | Description |
|---|---|---|
calls | CallInputs[] | Array of CallInputs from STATICCALL opcodes |
getDelegateCallInputs
Get call inputs for DELEGATECALL opcode only
function getDelegateCallInputs(address target, bytes4 selector) external view returns (CallInputs[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target/proxy contract address |
selector | bytes4 | The function selector to filter by |
Returns
| Name | Type | Description |
|---|---|---|
calls | CallInputs[] | Array of CallInputs from DELEGATECALL opcodes |
getCallCodeInputs
Get call inputs for CALLCODE opcode only
function getCallCodeInputs(address target, bytes4 selector) external view returns (CallInputs[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target contract address |
selector | bytes4 | The function selector to filter by |
Returns
| Name | Type | Description |
|---|---|---|
calls | CallInputs[] | Array of CallInputs from CALLCODE opcodes |
getStateChanges
Get all state changes for a specific storage slot
Returns the sequence of values the slot held during transaction execution
function getStateChanges(address contractAddress, bytes32 slot)
external
view
returns (bytes32[] memory stateChanges);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract whose storage to inspect |
slot | bytes32 | The storage slot to get changes for |
Returns
| Name | Type | Description |
|---|---|---|
stateChanges | bytes32[] | Array of values the slot held (in order of changes) |
getAssertionAdopter
Get the assertion adopter address for the current transaction
The adopter is the contract that registered the assertion
function getAssertionAdopter() external view returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | The address of the assertion adopter contract |
Structs
Log
Represents an Ethereum log emitted during transaction execution
Used by getLogs() to return transaction logs for inspection
struct Log {
/// @notice The topics of the log, including the event signature if any
bytes32[] topics;
/// @notice The raw ABI-encoded data of the log
bytes data;
/// @notice The address of the contract that emitted the log
address emitter;
}
CallInputs
Represents the inputs to a call made during transaction execution
Used by getCallInputs() and related functions to inspect call details
struct CallInputs {
/// @notice The calldata of the call
bytes input;
/// @notice The gas limit of the call
uint64 gas_limit;
/// @notice The address of the bytecode being executed (code address)
address bytecode_address;
/// @notice The target address whose storage may be modified
address target_address;
/// @notice The address that initiated this call
address caller;
/// @notice The ETH value sent with the call
uint256 value;
/// @notice Unique identifier for this call, used with forkPreCall/forkPostCall
uint256 id;
}
StateChanges
Inherits: Credible
Title: StateChanges
Helper contract for converting state changes from bytes32 arrays to typed arrays
Inherits from Credible to access the PhEvm interface
Functions
getStateChangesUint
Converts state changes for a slot to uint256 array
function getStateChangesUint(address contractAddress, bytes32 slot) internal view returns (uint256[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The address of the contract to get state changes from |
slot | bytes32 | The storage slot to get state changes for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256[] | Array of state changes as uint256 values |
getStateChangesAddress
Converts state changes for a slot to address array
function getStateChangesAddress(address contractAddress, bytes32 slot) internal view returns (address[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The address of the contract to get state changes from |
slot | bytes32 | The storage slot to get state changes for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address[] | Array of state changes as address values |
getStateChangesBool
Converts state changes for a slot to boolean array
function getStateChangesBool(address contractAddress, bytes32 slot) internal view returns (bool[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The address of the contract to get state changes from |
slot | bytes32 | The storage slot to get state changes for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool[] | Array of state changes as boolean values |
getStateChangesBytes32
Gets raw state changes as bytes32 array
function getStateChangesBytes32(address contractAddress, bytes32 slot) internal view returns (bytes32[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The address of the contract to get state changes from |
slot | bytes32 | The storage slot to get state changes for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32[] | Array of state changes as bytes32 values |
getSlotMapping
Calculates the storage slot for a mapping with a given key and offset
function getSlotMapping(bytes32 slot, uint256 key, uint256 offset) private pure returns (bytes32);
Parameters
| Name | Type | Description |
|---|---|---|
slot | bytes32 | The base storage slot of the mapping |
key | uint256 | The key in the mapping |
offset | uint256 | Additional offset to add to the calculated slot |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | The storage slot for the mapping entry |
getStateChangesUint
Gets uint256 state changes for a mapping entry
function getStateChangesUint(address contractAddress, bytes32 slot, uint256 key)
internal
view
returns (uint256[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256[] | Array of state changes as uint256 values |
getStateChangesAddress
Gets address state changes for a mapping entry
function getStateChangesAddress(address contractAddress, bytes32 slot, uint256 key)
internal
view
returns (address[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address[] | Array of state changes as address values |
getStateChangesBool
Gets boolean state changes for a mapping entry
function getStateChangesBool(address contractAddress, bytes32 slot, uint256 key)
internal
view
returns (bool[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool[] | Array of state changes as boolean values |
getStateChangesBytes32
Gets bytes32 state changes for a mapping entry
function getStateChangesBytes32(address contractAddress, bytes32 slot, uint256 key)
internal
view
returns (bytes32[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32[] | Array of state changes as bytes32 values |
getStateChangesUint
Gets uint256 state changes for a mapping entry with offset
function getStateChangesUint(address contractAddress, bytes32 slot, uint256 key, uint256 slotOffset)
internal
view
returns (uint256[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
slotOffset | uint256 | Additional offset to add to the slot |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256[] | Array of state changes as uint256 values |
getStateChangesAddress
Gets address state changes for a mapping entry with offset
function getStateChangesAddress(address contractAddress, bytes32 slot, uint256 key, uint256 slotOffset)
internal
view
returns (address[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
slotOffset | uint256 | Additional offset to add to the slot |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address[] | Array of state changes as address values |
getStateChangesBool
Gets boolean state changes for a mapping entry with offset
function getStateChangesBool(address contractAddress, bytes32 slot, uint256 key, uint256 slotOffset)
internal
view
returns (bool[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
slotOffset | uint256 | Additional offset to add to the slot |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool[] | Array of state changes as boolean values |
getStateChangesBytes32
Gets bytes32 state changes for a mapping entry with offset
function getStateChangesBytes32(address contractAddress, bytes32 slot, uint256 key, uint256 slotOffset)
internal
view
returns (bytes32[] memory);
Parameters
| Name | Type | Description |
|---|---|---|
contractAddress | address | The contract address |
slot | bytes32 | The mapping's slot |
key | uint256 | The mapping key |
slotOffset | uint256 | Additional offset to add to the slot |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32[] | Array of state changes as bytes32 values |
TriggerRecorder
Title: TriggerRecorder
Author: Phylax Systems
Precompile interface for registering assertion triggers
Used within the triggers() function of assertion contracts to specify
when assertions should be executed. Supports call triggers, storage change triggers,
and balance change triggers.
Functions
registerStorageChangeTrigger
Registers storage change trigger for all slots
function registerStorageChangeTrigger(bytes4 fnSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
registerStorageChangeTrigger
Registers storage change trigger for a slot
function registerStorageChangeTrigger(bytes4 fnSelector, bytes32 slot) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
slot | bytes32 | The storage slot to trigger on. |
registerBalanceChangeTrigger
Registers balance change trigger for the AA
function registerBalanceChangeTrigger(bytes4 fnSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
registerCallTrigger
Registers a call trigger for calls to the AA.
function registerCallTrigger(bytes4 fnSelector, bytes4 triggerSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |
triggerSelector | bytes4 | The function selector of the trigger function. |
registerCallTrigger
Records a call trigger for the specified assertion function. A call trigger signifies that the assertion function should be called if the assertion adopter is called.
function registerCallTrigger(bytes4 fnSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The function selector of the assertion function. |