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
Examples
The Assertions Book examples live in examples/assertions-book. This directory
is the canonical source for the code snippets imported by
phylaxsystems/phylax-docs.
| Directory | Purpose |
|---|---|
examples/assertions-book/assertions/src | Assertion contracts used by the Assertions Book |
examples/assertions-book/src | Mock protocols and helper contracts imported by assertion examples |
Compile the Assertions Book example sources with:
FOUNDRY_PROFILE=assertions-book forge build
Docs automation imports snippets from
examples/assertions-book/assertions/src; update examples here before updating
the docs pages that reference them.
After example changes merge, run the Import Credible Std Assertion Examples
workflow in phylaxsystems/phylax-docs to refresh the rendered snippets.
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 |
callinputAt(uint256) | Get full recorded calldata for one call id |
callOutputAt(uint256) | Get full recorded return or revert bytes for one call id |
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
Contents
- IERC20MetadataLike
- AaveV3Types
- IAaveV3PoolLike
- IAaveV3AddressesProviderLike
- IAaveV3OracleLike
- AaveV3HorizonProtectionSuite
- AaveV3HorizonOperationSafetyAssertion
- AaveV3ProtectionSuite
- AaveV3OperationSafetyAssertion
- AaveV3PostOperationSolvencyAssertion
IERC20MetadataLike
Minimal ERC20 metadata surface used by the Aave v3 Horizon example.
Functions
balanceOf
function balanceOf(address account) external view returns (uint256);
decimals
function decimals() external view returns (uint8);
AaveV3Types
Structs
UserConfigurationMap
struct UserConfigurationMap {
uint256 data;
}
ReserveDataLegacy
struct ReserveDataLegacy {
uint256 configurationData;
uint128 liquidityIndex;
uint128 currentLiquidityRate;
uint128 variableBorrowIndex;
uint128 currentVariableBorrowRate;
uint128 currentStableBorrowRate;
uint40 lastUpdateTimestamp;
uint16 id;
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
address interestRateStrategyAddress;
uint128 accruedToTreasury;
uint128 unbacked;
uint128 isolationModeTotalDebt;
}
IAaveV3PoolLike
Minimal pool surface matching the local Aave v3 Horizon pool interface.
This example was derived against ~/Documents/code/solidity/aave-v3-horizon/.
Functions
borrow
function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf)
external;
withdraw
function withdraw(address asset, uint256 amount, address to) external returns (uint256);
liquidationCall
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;
setUserUseReserveAsCollateral
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
finalizeTransfer
function finalizeTransfer(
address asset,
address from,
address to,
uint256 amount,
uint256 balanceFromBefore,
uint256 balanceToBefore
) external;
getUserAccountData
function getUserAccountData(address user)
external
view
returns (
uint256 totalCollateralBase,
uint256 totalDebtBase,
uint256 availableBorrowsBase,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
getUserConfiguration
function getUserConfiguration(address user) external view returns (AaveV3Types.UserConfigurationMap memory);
getReserveData
function getReserveData(address asset) external view returns (AaveV3Types.ReserveDataLegacy memory);
getReservesList
function getReservesList() external view returns (address[] memory);
ADDRESSES_PROVIDER
function ADDRESSES_PROVIDER() external view returns (address);
IAaveV3AddressesProviderLike
Functions
getPriceOracle
function getPriceOracle() external view returns (address);
IAaveV3OracleLike
Functions
getAssetPrice
function getAssetPrice(address asset) external view returns (uint256);
AaveV3HorizonProtectionSuite
Inherits: LendingProtectionSuiteBase
Title: AaveV3HorizonProtectionSuite
Author: Phylax Systems
Example ILendingProtectionSuite targeting a local Aave v3 Horizon deployment.
This is intentionally aligned with the Aave v3 Horizon pool paths found in
~/Documents/code/solidity/aave-v3-horizon/:
borrow(...)withdraw(...)liquidationCall(...)setUserUseReserveAsCollateral(asset, false)finalizeTransfer(...)for aToken transfers that reduce effective collateral.
Constants
HEALTH_FACTOR_METRIC
bytes32 internal constant HEALTH_FACTOR_METRIC = 0x4845414c54485f464143544f5200000000000000000000000000000000000000
WITHDRAW_CLAIM_CHECK
bytes32 internal constant WITHDRAW_CLAIM_CHECK = "WITHDRAW_CLAIM"
LIQUIDATION_DEBT_CHECK
bytes32 internal constant LIQUIDATION_DEBT_CHECK = "LIQUIDATION_DEBT"
LIQUIDATION_COLLATERAL_CHECK
bytes32 internal constant LIQUIDATION_COLLATERAL_CHECK = "LIQUIDATION_COLLATERAL"
HEALTH_FACTOR_THRESHOLD
uint256 internal constant HEALTH_FACTOR_THRESHOLD = 1e18
HEALTH_FACTOR_THRESHOLD_INT
int256 internal constant HEALTH_FACTOR_THRESHOLD_INT = 1e18
POOL
address internal immutable POOL
ADDRESSES_PROVIDER
address internal immutable ADDRESSES_PROVIDER
Functions
constructor
Creates an Aave v3 Horizon suite bound to a specific pool.
The assertion adopter should also be this pool, since the monitored selectors are all pool entrypoints or pool callbacks.
constructor(address pool_) ;
Parameters
| Name | Type | Description |
|---|---|---|
pool_ | address | Aave v3 Horizon pool address whose accounting and selectors this suite targets. |
getMonitoredSelectors
Returns the Aave v3 Horizon pool selectors relevant to the shared lending invariants.
These map directly to the protocol paths that Aave v3 Horizon guards with the health factor checks or bounded-consumption checks relevant to the shared lending invariants.
function getMonitoredSelectors() external pure override returns (bytes4[] memory selectors);
Returns
| Name | Type | Description |
|---|---|---|
selectors | bytes4[] | Pool selectors that should trigger the generic lending operation-safety check. |
decodeOperation
Decodes an Aave v3 Horizon pool call into the shared lending operation model.
Aave v3 Horizon mixes calldata-owned and caller-owned account semantics:
borrow(...)checks theonBehalfOfaccount.withdraw(...)andsetUserUseReserveAsCollateral(...)act onmsg.sender.liquidationCall(...)checks the liquidated user, withasset = debtAssetandrelatedAsset = collateralAsset.finalizeTransfer(...)checks thefromaccount when collateral leaves. Calls that do not reduce risk headroom are returned as neutral operations and filtered later byshouldCheckPostOperationSolvency(...).
function decodeOperation(TriggeredCall calldata triggered)
external
pure
override
returns (OperationContext memory operation);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The exact Aave v3 Horizon pool frame that caused the assertion to run. |
Returns
| Name | Type | Description |
|---|---|---|
operation | OperationContext | Protocol-normalized description of the triggered action. |
shouldCheckPostOperationSolvency
Filters decoded Aave v3 Horizon operations down to the ones that must preserve solvency.
This suite only checks paths that either increase debt or reduce effective collateral for a concrete account. Neutral operations are ignored to keep the assertion cheap and avoid false positives from selectors that are monitored broadly.
function shouldCheckPostOperationSolvency(OperationContext calldata operation)
external
pure
override
returns (bool shouldCheck);
Parameters
| Name | Type | Description |
|---|---|---|
operation | OperationContext | The decoded operation context. |
Returns
| Name | Type | Description |
|---|---|---|
shouldCheck | bool | True when the generic assertion should read post-call state. |
getConsumptionChecks
Returns the bounded-consumption checks implied by the decoded Aave v3 Horizon operation.
This example exposes two shared resource bounds:
- withdraws cannot consume more aToken claim than the user had before the call
- liquidations cannot move more debt from the liquidator into the debt reserve than the user owed before the call, and cannot move more collateral to the liquidator than the user had before the call Other operation kinds return an empty array.
function getConsumptionChecks(
TriggeredCall calldata triggered,
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork,
PhEvm.ForkId calldata afterFork
) external view override returns (ConsumptionCheck[] memory checks);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The exact Aave v3 Horizon pool frame that caused the assertion to run. |
operation | OperationContext | The decoded operation context. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
checks | ConsumptionCheck[] | Operation-specific bounded-consumption checks for the successful call. |
getAccountSnapshot
Reads the post-call account snapshot needed by the Aave v3 Horizon solvency invariant.
Hot path override: the invariant itself only needs Aave’s aggregate health factor, so the
suite skips per-reserve enumeration here. getAccountBalances(...) remains available for
richer debugging or derived assertions.
function getAccountSnapshot(address account, PhEvm.ForkId calldata fork)
external
view
virtual
override
returns (AccountSnapshot memory snapshot);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose health factor should be checked. |
fork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
snapshot | AccountSnapshot | Snapshot containing aggregate state and the derived health-factor decision. |
getAccountState
Reads Aave v3 Horizon aggregate account metrics from Pool.getUserAccountData(...).
The returned AccountState uses Aave’s base-currency values for total collateral and
total debt, and stores the additional health-factor inputs in metadata.
function getAccountState(address account, PhEvm.ForkId calldata fork)
external
view
override
returns (AccountState memory state);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose aggregate risk data should be queried. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate Aave v3 Horizon account state. |
getAccountBalances
Enumerates Aave v3 reserve balances for the account.
This is not needed on the hot path for the example invariant, but it shows how a suite can still expose per-reserve state for debugging, richer assertions, or protocols whose solvency rule depends on per-asset inspection.
function getAccountBalances(address account, PhEvm.ForkId calldata fork)
external
view
override
returns (AccountBalance[] memory balances);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose reserve balances should be queried. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
balances | AccountBalance[] | One entry per non-zero reserve position. |
evaluateSolvency
Evaluates Aave v3 Horizon solvency from aggregate account state.
The example intentionally ignores per-reserve balances because Aave v3 Horizon’s own post-action
checks reduce to health factor over aggregate pool data. Suites for other protocols may
need to inspect the balances argument instead.
function evaluateSolvency(
AccountState calldata state,
AccountBalance[] calldata balances,
PhEvm.ForkId calldata fork
) external pure override returns (SolvencyState memory solvency);
Parameters
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate account state produced by getAccountState(...). |
balances | AccountBalance[] | The per-reserve balances, unused in this implementation. |
fork | PhEvm.ForkId | The snapshot fork, unused because all required information is in state. |
Returns
| Name | Type | Description |
|---|---|---|
solvency | SolvencyState | Health-factor-based solvency result. |
_getAccountState
Internal helper that reads and normalizes Aave v3 Horizon aggregate account data.
This is the canonical aggregate-state implementation used by both getAccountState(...)
and the optimized getAccountSnapshot(...) hot path.
function _getAccountState(address account, PhEvm.ForkId memory fork)
internal
view
returns (AccountState memory state);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose risk data should be queried. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate state encoded in the common suite format. |
_getAccountBalances
Internal helper that expands the account into reserve-level balances and values.
Reads the reserve list, user collateral bitset, reserve token addresses, and oracle prices, then emits one balance entry per reserve with a non-zero collateral or debt balance. The values are normalized to Aave’s base currency.
function _getAccountBalances(address account, PhEvm.ForkId memory fork)
internal
view
returns (AccountBalance[] memory balances);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose reserve positions should be queried. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
balances | AccountBalance[] | One entry per non-zero reserve position. |
_getWithdrawClaimCheck
Builds the withdraw bounded-consumption check from Aave v3 Horizon call output and pre-state.
Pool.withdraw(...) returns the actual amount withdrawn, which already handles the
type(uint256).max full-withdraw path. The available pre-operation claim is the user’s
aToken balance before the call.
function _getWithdrawClaimCheck(
TriggeredCall calldata triggered,
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork
) internal view returns (ConsumptionCheck memory check);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The traced withdraw call. |
operation | OperationContext | The decoded withdraw operation. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
check | ConsumptionCheck | Bound requiring actual withdrawn amount to be no greater than pre-call supply. |
_getLiquidationDebtCheck
Builds the liquidation debt-consumption check from actual debt-asset transfers.
Aave v3 Horizon clips liquidation debt to the user’s actual debt or the close-factor cap. The assertion therefore measures the actual debt asset moved from the liquidator to the debt reserve’s aToken during the successful liquidation call.
function _getLiquidationDebtCheck(
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork,
PhEvm.ForkId calldata afterFork
) internal view returns (ConsumptionCheck memory check);
Parameters
| Name | Type | Description |
|---|---|---|
operation | OperationContext | The decoded liquidation operation. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
check | ConsumptionCheck | Bound requiring repaid debt to be no greater than pre-call debt. |
_getLiquidationCollateralCheck
Builds the liquidation collateral-consumption check from actual collateral transfers.
This measures what the liquidator actually receives:
- underlying collateral when
receiveAToken == false - collateral aTokens when
receiveAToken == trueThe bound is still capped by the user’s pre-call collateral claim.
function _getLiquidationCollateralCheck(
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork,
PhEvm.ForkId calldata afterFork
) internal view returns (ConsumptionCheck memory check);
Parameters
| Name | Type | Description |
|---|---|---|
operation | OperationContext | The decoded liquidation operation. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
check | ConsumptionCheck | Bound requiring seized collateral to be no greater than pre-call collateral. |
_evaluateHealthFactor
Converts Aave v3 Horizon aggregate metrics into the common solvency representation.
This is the protocol-specific core of the invariant for Aave v3 Horizon: an account with debt is
solvent iff healthFactor >= 1e18.
function _evaluateHealthFactor(AccountState memory state) internal pure returns (SolvencyState memory solvency);
Parameters
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate Aave v3 account state whose metadata contains health-factor inputs. |
Returns
| Name | Type | Description |
|---|---|---|
solvency | SolvencyState | Common solvency output expressed in terms of health factor. |
_buildAccountBalance
Builds a single reserve-level balance entry for the account.
Returns (false, ...) when the account has neither supplied balance nor variable debt in
the reserve, allowing the caller to compact the final array. This example intentionally
follows the local Horizon borrow surface, which only exposes variable-rate borrowing.
function _buildAccountBalance(
address asset,
address account,
uint256 userConfigData,
address oracle,
PhEvm.ForkId memory fork
) internal view returns (bool include, AccountBalance memory balance);
Parameters
| Name | Type | Description |
|---|---|---|
asset | address | The reserve asset being inspected. |
account | address | The account whose reserve position should be read. |
userConfigData | uint256 | Aave v3 user-configuration bitset used to determine collateral usage. |
oracle | address | Price oracle used to value the reserve balances. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
include | bool | Whether the resulting balance entry should be kept. |
balance | AccountBalance | Normalized reserve-level balance information. |
_getReserveData
Reads Aave v3 Horizon reserve metadata for a single asset at the requested snapshot fork.
function _getReserveData(address asset, PhEvm.ForkId memory fork)
internal
view
returns (AaveV3Types.ReserveDataLegacy memory reserveData);
Parameters
| Name | Type | Description |
|---|---|---|
asset | address | The reserve asset whose metadata should be queried. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
reserveData | AaveV3Types.ReserveDataLegacy | Reserve metadata returned by Pool.getReserveData(...). |
_getUserReserveDebt
Reads the user’s total debt for one reserve from Aave v3 Horizon debt-token balances.
The example sums stable and variable debt token balances to make the liquidation bound robust across reserve configurations.
function _getUserReserveDebt(address asset, address account, PhEvm.ForkId memory fork)
internal
view
returns (uint256 debtBalance);
Parameters
| Name | Type | Description |
|---|---|---|
asset | address | The reserve asset whose debt position should be queried. |
account | address | The user whose debt should be read. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
debtBalance | uint256 | Total reserve debt for the user. |
_readOptionalBalance
Reads a token balance when the token address may be unset.
Some deployments disable one debt-token flavor and leave the corresponding address at zero. This helper treats that case as a zero balance instead of reverting.
function _readOptionalBalance(address token, address account, PhEvm.ForkId memory fork)
internal
view
returns (uint256 balance);
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The token contract to query, or address(0) when absent. |
account | address | The account whose balance should be read. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
balance | uint256 | The token balance, or zero when token == address(0). |
_valueInBase
Converts an asset-denominated balance into Aave base currency.
Uses the Aave v3 Horizon oracle price and token decimals from the snapshot fork. This helper is suitable for example code and debugging, but suites with stricter precision requirements may want protocol-exact valuation logic.
function _valueInBase(address oracle, address asset, uint256 balance, PhEvm.ForkId memory fork)
internal
view
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
oracle | address | Aave v3 price oracle. |
asset | address | Asset whose price and decimals should be read. |
balance | uint256 | Raw token amount to convert. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | value Asset value expressed in Aave base currency units. |
_isUsingAsCollateral
Returns whether the reserve is enabled as collateral in Aave v3 Horizon’s user config bitset.
Aave v3 Horizon stores collateral and borrow flags as packed bit pairs keyed by reserve id. This helper reads the collateral bit for the reserve.
function _isUsingAsCollateral(uint256 userConfigData, uint256 reserveId) internal pure returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
userConfigData | uint256 | Packed Aave v3 user configuration. |
reserveId | uint256 | Reserve id from getReserveData(...). |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | isCollateral True when the reserve currently counts as collateral. |
_toInt256
Safely casts a uint256 metric to int256 for SolvencyState.
Solvency metrics use signed integers in the common interface to support protocols with
negative liquidity margins. Aave v3 health factor is always non-negative, so values above
int256.max are saturated instead of reverting.
function _toInt256(uint256 value) internal pure returns (int256);
Parameters
| Name | Type | Description |
|---|---|---|
value | uint256 | Unsigned metric value to convert. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | signedValue Saturated signed representation of value. |
Structs
AaveAccountMetrics
Extra aggregate Aave v3 Horizon metrics kept in AccountState.metadata.
The common account shape does not have dedicated fields for these values, but they are useful for debugging and for reconstructing the health-factor-based solvency decision.
struct AaveAccountMetrics {
uint256 availableBorrowsBase;
uint256 currentLiquidationThreshold;
uint256 ltv;
uint256 healthFactor;
}
AaveV3HorizonOperationSafetyAssertion
Inherits: LendingBaseAssertion
Title: AaveV3HorizonOperationSafetyAssertion
Author: Phylax Systems
Example single-entry assertion bundle for Aave v3 Horizon.
Usage:
cl.assertion({ adopter: aaveV3HorizonPool, createData: abi.encodePacked(type(AaveV3HorizonOperationSafetyAssertion).creationCode, abi.encode(aaveV3HorizonPool)), fnSelector: AaveV3HorizonOperationSafetyAssertion.assertOperationSafety.selector })
Constants
SUITE
Dedicated Horizon suite deployed by this assertion bundle.
Keeping the suite in a helper contract preserves the one-create-data UX while keeping the assertion runtime below the EIP-170 size limit enforced by CI.
ILendingProtectionSuite internal immutable SUITE
Functions
constructor
Creates an Aave v3 Horizon assertion bundle with an internally deployed suite.
constructor(address pool_) ;
Parameters
| Name | Type | Description |
|---|---|---|
pool_ | address | Aave v3 Horizon pool used both as the suite data source and assertion adopter. |
_suite
Returns the suite implementation used by the generic lending assertion base.
The assertion keeps protocol-specific logic in a dedicated helper suite contract so the assertion runtime stays within CI’s contract-size limit.
function _suite() internal view override returns (ILendingProtectionSuite);
Returns
| Name | Type | Description |
|---|---|---|
<none> | ILendingProtectionSuite | suite The internally deployed common lending suite implementation. |
AaveV3ProtectionSuite
Inherits: AaveV3HorizonProtectionSuite
Title: AaveV3ProtectionSuite
Author: Phylax Systems
Compatibility alias preserving the old generic Aave v3 suite name.
The implementation is Horizon-specific and derived from the local Horizon repository.
Functions
constructor
Creates the compatibility alias.
constructor(address pool_) AaveV3HorizonProtectionSuite(pool_);
Parameters
| Name | Type | Description |
|---|---|---|
pool_ | address | Aave v3 Horizon pool used by the underlying suite. |
AaveV3OperationSafetyAssertion
Inherits: AaveV3HorizonOperationSafetyAssertion
Title: AaveV3OperationSafetyAssertion
Author: Phylax Systems
Compatibility alias preserving the old generic Aave v3 assertion name.
New users should prefer AaveV3HorizonOperationSafetyAssertion to make the Horizon scope explicit.
Functions
constructor
Creates the compatibility alias.
constructor(address pool_) AaveV3HorizonOperationSafetyAssertion(pool_);
Parameters
| Name | Type | Description |
|---|---|---|
pool_ | address | Aave v3 Horizon pool used by the underlying assertion bundle. |
AaveV3PostOperationSolvencyAssertion
Inherits: AaveV3HorizonOperationSafetyAssertion
Title: AaveV3PostOperationSolvencyAssertion
Author: Phylax Systems
Deprecated compatibility alias for the pre-operation-safety contract name.
New users should prefer AaveV3HorizonOperationSafetyAssertion.
Functions
constructor
Creates the deprecated compatibility alias.
constructor(address pool_) AaveV3HorizonOperationSafetyAssertion(pool_);
Parameters
| Name | Type | Description |
|---|---|---|
pool_ | address | Aave v3 Horizon pool used by the underlying assertion bundle. |
ILendingProtectionSuite
Title: ILendingProtectionSuite
Author: Phylax Systems
Step-oriented interface for protocol-specific lending protection suites.
Implementations expose the protocol-specific plumbing needed to assert a shared family of lending-protocol invariants:
- any successful action that increases debt or reduces effective collateral must leave the affected account solvent under the protocol’s own risk metric
- successful withdrawals must not consume more claim than the account had before the call
- successful liquidations must not consume more debt or collateral than existed before the call The bounded-consumption portion should be measured from the successful call’s actual effect, such as traced return data, ERC20 transfer deltas, or protocol-emitted events, rather than from the requested input amount alone. Expected assertion flow:
- Read monitored selectors from
getMonitoredSelectors(). - Resolve the caller-aware
TriggeredCall. - Decode the triggered call with
decodeOperation(...). - Read operation-specific consumption checks with
getConsumptionChecks(...)and requireconsumed <= availableBeforefor each returned check. - Filter to solvency-relevant operations with
shouldCheckPostOperationSolvency(...). - Read the account snapshot with
getAccountSnapshot(...). - Require
snapshot.solvency.isSolvent == true. Different protocols can encode different solvency metrics while preserving the same invariant:
- Aave-like systems can expose
metric = healthFactor,threshold = 1e18,comparison = Gte. - Euler-like systems can expose
metric = collateralValue - liabilityValue,threshold = 0,comparison = Gt.
Functions
getMonitoredSelectors
Returns the adopter selectors that can participate in the shared lending invariants.
Implementations should return the selectors that the generic assertion must subscribe to on
the adopter. These selectors do not all need to be solvency-worsening on every invocation;
decodeOperation(...) and shouldCheckPostOperationSolvency(...) can later discard benign
or no-op cases. The returned list is typically protocol-entrypoint specific.
function getMonitoredSelectors() external view returns (bytes4[] memory selectors);
Returns
| Name | Type | Description |
|---|---|---|
selectors | bytes4[] | Selectors that should trigger the generic lending operation-safety assertion. |
decodeOperation
Decodes the triggered adopter call into a protocol-normalized operation context.
Caller-aware decoding is necessary because some protocols encode the affected account in
msg.sender instead of calldata. Implementations should populate operation.account
with the account whose post-operation solvency must be checked and mark whether the call
increased debt and/or reduced effective collateral. Unsupported or irrelevant selectors
should normally return the zero-value OperationContext rather than revert.
function decodeOperation(TriggeredCall calldata triggered) external view returns (OperationContext memory operation);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The exact adopter frame that caused the assertion to run. |
Returns
| Name | Type | Description |
|---|---|---|
operation | OperationContext | Protocol-normalized context used by downstream filtering and checks. |
shouldCheckPostOperationSolvency
Returns whether the decoded action must preserve post-operation solvency.
This is the last protocol-specific filter before snapshot reads happen. Implementations
should return false for actions that are not risk-increasing in the shared sense,
such as enabling collateral, zero-amount paths, or protocol no-ops.
function shouldCheckPostOperationSolvency(OperationContext calldata operation)
external
view
returns (bool shouldCheck);
Parameters
| Name | Type | Description |
|---|---|---|
operation | OperationContext | The decoded operation context returned by decodeOperation(...). |
Returns
| Name | Type | Description |
|---|---|---|
shouldCheck | bool | True when the assertion should read state and enforce solvency. |
getConsumptionChecks
Returns the bounded-consumption checks implied by the decoded operation.
Each returned entry is enforced as consumed <= availableBefore. Implementations should
return an empty array when the operation has no shared bounded-consumption invariant.
consumed must reflect the actual effect of the successful call, not merely the
requested amount. This lets suites support clipped operations such as partial withdraws
or capped liquidations without bespoke assertion logic in the base contract. Suitable
data sources include traced call output, ERC20 transfer introspection, or decoded logs.
function getConsumptionChecks(
TriggeredCall calldata triggered,
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork,
PhEvm.ForkId calldata afterFork
) external view returns (ConsumptionCheck[] memory checks);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The exact adopter frame that caused the assertion to run. |
operation | OperationContext | The decoded operation context returned by decodeOperation(...). |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
checks | ConsumptionCheck[] | Operation-specific bounds to enforce for the successful call. |
getAccountSnapshot
Reads the account snapshot used by the post-operation solvency assertion.
Implementations can override this with a protocol-optimized hot path instead of forcing
the assertion to always compute per-asset balances. The returned snapshot must describe
the post-operation state at fork. The shared suite default in
LendingBaseAssertion.sol composes this from getAccountState(...),
getAccountBalances(...), and evaluateSolvency(...).
function getAccountSnapshot(address account, PhEvm.ForkId calldata fork)
external
view
returns (AccountSnapshot memory snapshot);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose post-operation solvency is being checked. |
fork | PhEvm.ForkId | The post-call snapshot fork that should be queried. |
Returns
| Name | Type | Description |
|---|---|---|
snapshot | AccountSnapshot | Aggregate state, optional per-asset balances, and the final solvency result. |
getAccountState
Reads protocol-normalized aggregate account state at a given snapshot fork.
Implementations should return enough aggregate information for downstream solvency logic
and debugging. Protocol-specific fields that do not fit the common shape belong in
state.metadata.
function getAccountState(address account, PhEvm.ForkId calldata fork)
external
view
returns (AccountState memory state);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose risk state should be inspected. |
fork | PhEvm.ForkId | The snapshot fork to read from. |
Returns
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate account state in protocol-defined accounting units. |
getAccountBalances
Reads protocol-normalized per-asset balances for an account at a snapshot fork.
Implementations may return an empty array when balances are not required for the
hot-path solvency decision and getAccountSnapshot(...) already exposes an optimized path.
When provided, balances should use the same valuation units as AccountState.
function getAccountBalances(address account, PhEvm.ForkId calldata fork)
external
view
returns (AccountBalance[] memory balances);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose positions should be inspected. |
fork | PhEvm.ForkId | The snapshot fork to read from. |
Returns
| Name | Type | Description |
|---|---|---|
balances | AccountBalance[] | Per-asset collateral and debt entries relevant to the account. |
evaluateSolvency
Evaluates the protocol’s solvency rule from the decoded account snapshot.
Implementations should encode the exact rule the protocol uses to decide whether an
account is solvent or liquidatable. state and balances are passed separately so
simple protocols can decide from aggregate state alone while more complex protocols can
inspect per-asset positions.
function evaluateSolvency(
AccountState calldata state,
AccountBalance[] calldata balances,
PhEvm.ForkId calldata fork
) external view returns (SolvencyState memory solvency);
Parameters
| Name | Type | Description |
|---|---|---|
state | AccountState | Aggregate account state returned by getAccountState(...). |
balances | AccountBalance[] | Per-asset balances returned by getAccountBalances(...). |
fork | PhEvm.ForkId | The snapshot fork used for the solvency evaluation. |
Returns
| Name | Type | Description |
|---|---|---|
solvency | SolvencyState | Protocol-defined solvency decision and supporting metric data. |
Structs
TriggeredCall
Resolved information about the exact adopter call that triggered the assertion.
struct TriggeredCall {
/// @notice Function selector invoked on the adopter.
bytes4 selector;
/// @notice Immediate caller of the adopter frame.
address caller;
/// @notice Adopter target address that was called.
address target;
/// @notice Raw calldata for the adopter frame.
bytes input;
/// @notice Call identifier used to construct a PreCall snapshot.
uint256 callStart;
/// @notice Call identifier used to construct a PostCall snapshot.
uint256 callEnd;
}
OperationContext
Protocol-decoded context for a monitored lending call.
struct OperationContext {
/// @notice The adopter selector that produced this operation context.
bytes4 selector;
/// @notice The high-level action kind.
OperationKind kind;
/// @notice The immediate caller of the adopter frame.
address caller;
/// @notice The primary account whose solvency should be checked after the operation.
address account;
/// @notice The primary asset involved in the action, if any.
address asset;
/// @notice Optional second asset involved in the action, if any.
/// @dev This is primarily useful for multi-asset operations such as liquidation, where
/// `asset` can represent the debt asset and `relatedAsset` the collateral asset.
address relatedAsset;
/// @notice Secondary account involved in the action, if any (receiver, liquidator, etc.).
address counterparty;
/// @notice Protocol-decoded requested or declared amount associated with the action, if any.
/// @dev For clipped operations, this may differ from the actual amount later enforced by
/// `getConsumptionChecks(...)`.
uint256 amount;
/// @notice True when the action increases the account's debt exposure.
bool increasesDebt;
/// @notice True when the action reduces the account's effective collateral.
bool reducesEffectiveCollateral;
/// @notice Extension point for protocol-specific metadata.
bytes metadata;
}
AccountState
Protocol-normalized aggregate state for an account at a snapshot fork.
struct AccountState {
/// @notice The account whose state was read.
address account;
/// @notice Aggregate collateral value using the implementation's protocol-defined accounting units.
uint256 totalCollateralValue;
/// @notice Aggregate debt value using the implementation's protocol-defined accounting units.
uint256 totalDebtValue;
/// @notice Whether the account currently has any open debt.
bool hasDebt;
/// @notice Extension point for protocol-specific aggregate data.
bytes metadata;
}
AccountBalance
Per-asset balance and value data for an account at a snapshot fork.
struct AccountBalance {
/// @notice The reserve, market, or asset address represented by this balance entry.
address asset;
/// @notice Raw collateral or supplied balance tracked for the account.
uint256 collateralBalance;
/// @notice Raw debt balance tracked for the account.
uint256 debtBalance;
/// @notice Protocol-normalized collateral value for this asset.
uint256 collateralValue;
/// @notice Protocol-normalized debt value for this asset.
uint256 debtValue;
/// @notice Whether this asset currently counts as collateral for the account.
bool countsAsCollateral;
/// @notice Extension point for protocol-specific per-asset metadata.
bytes metadata;
}
SolvencyState
Protocol-defined solvency output for an account at a snapshot fork.
struct SolvencyState {
/// @notice Whether the account is solvent under the protocol's own rules.
bool isSolvent;
/// @notice Whether the protocol would consider the account liquidatable at this snapshot.
bool isLiquidatable;
/// @notice Identifier for the solvency metric, e.g. "HEALTH_FACTOR" or "LIQUIDITY_EXCESS".
bytes32 metricName;
/// @notice Protocol-normalized solvency metric.
int256 metric;
/// @notice Threshold that the metric is compared against.
int256 threshold;
/// @notice Comparison rule used to interpret `metric` vs `threshold`.
ComparisonKind comparison;
/// @notice Extension point for protocol-specific evidence or decoded fields.
bytes metadata;
}
AccountSnapshot
Full post-operation snapshot for a monitored account.
struct AccountSnapshot {
/// @notice Aggregate state for the monitored account.
AccountState state;
/// @notice Per-asset balances. Implementations may return an empty array on the hot path.
AccountBalance[] balances;
/// @notice Protocol-defined solvency decision for the snapshot.
SolvencyState solvency;
}
ConsumptionCheck
One concrete resource-consumption bound that must hold for a successful operation.
struct ConsumptionCheck {
/// @notice Identifier for the bound being asserted, e.g. "WITHDRAW_CLAIM".
bytes32 checkName;
/// @notice The account whose pre-operation resource balance caps the consumption.
address account;
/// @notice The asset whose pre-operation balance or claim is being bounded.
address asset;
/// @notice Resource available before the operation in protocol-defined accounting units.
uint256 availableBefore;
/// @notice Actual resource consumed by the successful operation in the same units.
uint256 consumed;
/// @notice Extension point for protocol-specific evidence or decoded fields.
bytes metadata;
}
Enums
OperationKind
The lending action being inspected for shared post-operation safety checks.
enum OperationKind {
Unknown,
Borrow,
WithdrawCollateral,
DisableCollateral,
TransferCollateral,
Liquidation
}
ComparisonKind
Comparison rule for the protocol-defined solvency metric.
enum ComparisonKind {
Unknown,
Gte,
Gt
}
LendingProtectionSuiteBase
Inherits: ForkUtils, ILendingProtectionSuite
Title: LendingProtectionSuiteBase
Author: Phylax Systems
Shared default implementations for lending protection suites.
Functions
getConsumptionChecks
Default bounded-consumption implementation for suites with no extra resource checks.
Override this when the protocol needs withdraw, liquidation, or other consumption bounds. Returning an empty array keeps solvency-only suites source-compatible with the generic lending assertion.
function getConsumptionChecks(
TriggeredCall calldata triggered,
OperationContext calldata operation,
PhEvm.ForkId calldata beforeFork,
PhEvm.ForkId calldata afterFork
) external view virtual override returns (ConsumptionCheck[] memory checks);
Parameters
| Name | Type | Description |
|---|---|---|
triggered | TriggeredCall | The exact adopter frame that caused the assertion to run. |
operation | OperationContext | The decoded operation context. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
Returns
| Name | Type | Description |
|---|---|---|
checks | ConsumptionCheck[] | Empty by default. |
getAccountSnapshot
Composes a full account snapshot from the step-oriented suite functions.
This is the default implementation for step-based suites. Override it only when the
protocol exposes a materially cheaper or more direct way to answer the invariant than
calling getAccountState(...), getAccountBalances(...), and evaluateSolvency(...)
separately.
function getAccountSnapshot(address account, PhEvm.ForkId calldata fork)
external
view
virtual
override
returns (AccountSnapshot memory snapshot);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | The account whose post-operation state should be read. |
fork | PhEvm.ForkId | The snapshot fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
snapshot | AccountSnapshot | Aggregate state, balances, and solvency produced by the suite steps. |
_viewFailureMessage
Returns the suite-specific revert string for failed fork-time static calls.
function _viewFailureMessage() internal pure virtual override returns (string memory);
LendingBaseAssertion
Inherits: Assertion
Title: LendingBaseAssertion
Author: Phylax Systems
Generic lending operation-safety assertion for lending protocols.
Inherit this together with a concrete ILendingProtectionSuite implementation. The base
contract handles one decode pass per triggered call, then enforces both:
- any bounded-consumption checks returned by the suite
- post-operation solvency for risk-increasing operations
Functions
_suite
Returns the protocol-specific lending suite that powers this assertion.
Concrete assertions typically inherit both this base contract and a suite contract, then
return ILendingProtectionSuite(address(this)). Returning a different contract is also
valid if the assertion delegates protocol logic elsewhere.
function _suite() internal view virtual returns (ILendingProtectionSuite);
Returns
| Name | Type | Description |
|---|---|---|
<none> | ILendingProtectionSuite | suite The suite used to decode operations and evaluate solvency. |
triggers
Registers one generic lending operation-safety check for every monitored selector.
This is the only trigger wiring most lending assertions need to implement. The suite
decides which selectors matter through getMonitoredSelectors(), and this base maps all
of them to assertOperationSafety().
function triggers() external view virtual override;
assertOperationSafety
Enforces the shared lending operation-safety invariants for a successful call.
Assertion authors should usually point Credible at this selector. The method resolves the triggering adopter frame, decodes the protocol operation once, enforces any bounded- consumption checks returned by the suite, and then enforces post-operation solvency when the suite marks the operation as risk-increasing.
function assertOperationSafety() external view;
assertPostOperationSolvency
Backwards-compatible alias for the legacy solvency-only entrypoint name.
Older bundles may still reference this selector directly. It now runs the full generic lending operation-safety pipeline rather than only the solvency portion.
function assertPostOperationSolvency() external view;
_assertOperationSafety
Internal implementation shared by the public lending assertion entrypoints.
Runs all shared lending checks exposed by the suite against the triggered adopter call.
function _assertOperationSafety() internal view;
_assertConsumptionChecks
Enforces the suite-provided bounded-consumption checks for the triggered operation.
Suites may return zero, one, or many bounds depending on the operation kind. Each bound
is enforced as consumed <= availableBefore, where both values must already be expressed
in the same protocol-defined units.
function _assertConsumptionChecks(
ILendingProtectionSuite suite,
ILendingProtectionSuite.TriggeredCall memory triggered,
ILendingProtectionSuite.OperationContext memory operation,
PhEvm.ForkId memory beforeFork,
PhEvm.ForkId memory afterFork
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
suite | ILendingProtectionSuite | The protocol-specific lending suite. |
triggered | ILendingProtectionSuite.TriggeredCall | The exact adopter frame that caused the assertion to run. |
operation | ILendingProtectionSuite.OperationContext | The decoded lending operation. |
beforeFork | PhEvm.ForkId | The pre-call snapshot fork. |
afterFork | PhEvm.ForkId | The post-call snapshot fork. |
_assertPostOperationSolvency
Enforces post-operation solvency for risk-increasing operations.
Operations that do not increase debt or reduce effective collateral are skipped. This is the original shared lending invariant, now run as one stage of the broader operation-safety pipeline.
function _assertPostOperationSolvency(
ILendingProtectionSuite suite,
ILendingProtectionSuite.TriggeredCall memory triggered,
ILendingProtectionSuite.OperationContext memory operation,
PhEvm.ForkId memory afterFork
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
suite | ILendingProtectionSuite | The protocol-specific lending suite. |
triggered | ILendingProtectionSuite.TriggeredCall | The exact adopter frame that caused the assertion to run. |
operation | ILendingProtectionSuite.OperationContext | The decoded lending operation. |
afterFork | PhEvm.ForkId | The post-call snapshot fork used for the solvency read. |
_resolveTriggeredCall
Resolves the exact adopter frame that caused the current assertion execution.
Credible exposes the selector and call identifiers in ph.context(), but the suite also
needs raw calldata and caller information for correct protocol decoding. This helper
reconstructs that frame from getAllCallInputs(...) and packages it into
ILendingProtectionSuite.TriggeredCall.
function _resolveTriggeredCall() internal view returns (ILendingProtectionSuite.TriggeredCall memory triggered);
Returns
| Name | Type | Description |
|---|---|---|
triggered | ILendingProtectionSuite.TriggeredCall | Caller-aware information about the adopter call being checked. |
Errors
LendingTriggeredCallNotFound
error LendingTriggeredCallNotFound(bytes4 selector, uint256 callStart);
LendingOperationAccountMissing
error LendingOperationAccountMissing(bytes4 selector);
LendingConsumptionCheckViolated
error LendingConsumptionCheckViolated(
address account,
bytes4 selector,
ILendingProtectionSuite.OperationKind kind,
bytes32 checkName,
address asset,
uint256 consumed,
uint256 availableBefore
);
LendingPostOperationSolvencyViolated
error LendingPostOperationSolvencyViolated(
address account,
bytes4 selector,
ILendingProtectionSuite.OperationKind kind,
bytes32 metricName,
int256 metric,
int256 threshold
);
Contents
- ERC4626AssetFlowAssertion
- ERC4626BaseAssertion
- ERC4626CumulativeOutflowAssertion
- ERC4626PreviewAssertion
- ERC4626SharePriceAssertion
- IERC4626
ERC4626AssetFlowAssertion
Inherits: ERC4626BaseAssertion
Title: ERC4626AssetFlowAssertion
Author: Phylax Systems
Asserts that ERC-20 token movement and the vault’s internal asset accounting agree, and that fundamental share-token invariants hold. Invariants covered:
- Token movement matches accounting: the change in totalAssets across the transaction equals the net ERC-20 flow into/out of the vault. This catches transfer-fee tokens, rebasing tokens, or accounting bugs where totalAssets drifts from reality.
- Zero address never holds shares: balanceOf(address(0)) == 0 after every share-minting operation.
Uses V2 registerTxEndTrigger for tx-wide checks and
registerFnCallTrigger + ph.context() for call-scoped checks.
Functions
_registerAssetFlowTriggers
Register the default trigger set for asset-flow invariants.
function _registerAssetFlowTriggers() internal view;
assertAssetFlowMatchesAccounting
Verifies the change in totalAssets across the tx matches the net ERC-20 flow.
function assertAssetFlowMatchesAccounting() external;
_netAssetFlow
Compute net ERC-20 flow into (+) or out of (-) the vault across the tx.
Override for vaults that deploy assets through adapters or external protocols. The override should include flows to/from all relevant addresses (vault + adapters).
function _netAssetFlow() internal view virtual returns (int256 netFlow);
assertZeroAddressHasNoShares
Verifies the zero address never holds vault shares.
Uses ph.context() to check at PostCall of the triggering call.
function assertZeroAddressHasNoShares() external;
ERC4626BaseAssertion
Inherits: Assertion
Title: ERC4626BaseAssertion
Author: Phylax Systems
Base contract for ERC-4626 vault assertions (V2 syntax).
Provides vault-specific state accessors on top of the shared Assertion helpers.
Inherit from this (and one or more invariant contracts), then implement triggers().
Example – combine share-price, preview, and outflow invariants:
contract MyVaultAssertion is ERC4626SharePriceAssertion, ERC4626PreviewAssertion, ERC4626CumulativeOutflowAssertion {
constructor(address _vault)
ERC4626BaseAssertion(_vault)
ERC4626SharePriceAssertion(50) // 50 bps tolerance
ERC4626CumulativeOutflowAssertion(1_000, 24 hours) // 10% in 24h
{}
function triggers() external view override {
_registerSharePriceTriggers();
_registerPreviewTriggers();
_registerCumulativeOutflowTriggers();
}
}
Constants
vault
The ERC-4626 vault being monitored (assertion adopter).
address internal immutable vault
asset
The underlying ERC-20 asset of the vault.
address internal immutable asset
Functions
constructor
constructor(address _vault) ;
_totalAssetsAt
function _totalAssetsAt(PhEvm.ForkId memory fork) internal view returns (uint256);
_totalSupplyAt
function _totalSupplyAt(PhEvm.ForkId memory fork) internal view returns (uint256);
_shareBalanceAt
function _shareBalanceAt(address account, PhEvm.ForkId memory fork) internal view returns (uint256);
_assetBalanceAt
Uses the same balanceOf(address) selector — valid for any ERC-20.
function _assetBalanceAt(address account, PhEvm.ForkId memory fork) internal view returns (uint256);
ERC4626CumulativeOutflowAssertion
Inherits: ERC4626BaseAssertion
Title: ERC4626CumulativeOutflowAssertion
Author: Phylax Systems
Circuit breaker that triggers when cumulative ERC-20 outflow from the vault exceeds a percentage threshold within a rolling time window. Invariant covered:
- Cumulative outflow cap: the net outflow of the vault’s underlying asset must not
exceed
outflowThresholdBpsof the TVL snapshot within a rollingoutflowWindowDuration.
Uses watchCumulativeOutflow trigger registration — the executor handles all persistent
state tracking, TVL snapshots, and threshold enforcement internally. The assertion
function fires only when the threshold is breached.
Override assertCumulativeOutflow for smart breaker logic (e.g. deposit/repay-only mode).
The default implementation unconditionally reverts (hard breaker).
Constants
outflowThresholdBps
Maximum cumulative outflow as basis points of the TVL snapshot. 1000 = 10%.
uint256 public immutable outflowThresholdBps
outflowWindowDuration
Rolling window length in seconds.
uint256 public immutable outflowWindowDuration
Functions
constructor
constructor(uint256 _thresholdBps, uint256 _windowDuration) ;
_registerCumulativeOutflowTriggers
Register the cumulative outflow circuit breaker trigger.
Call this inside your triggers().
function _registerCumulativeOutflowTriggers() internal view;
assertCumulativeOutflow
Called when cumulative outflow exceeds the threshold.
Default is a hard breaker (unconditional revert). Override for smart breaker
logic — e.g. allow deposits but block withdrawals using ph.outflowContext()
and _matchingCalls().
function assertCumulativeOutflow() external virtual;
ERC4626PreviewAssertion
Inherits: ERC4626BaseAssertion
Title: ERC4626PreviewAssertion
Author: Phylax Systems
Asserts that ERC-4626 preview functions are consistent with the actual results of the corresponding state-changing operations, and that rounding favors the vault. Invariants covered:
- Preview consistency: for the same pre-state, previewDeposit(a) == shares minted by deposit(a) previewMint(s) == assets charged by mint(s) previewWithdraw(a) == shares burned by withdraw(a) previewRedeem(s) == assets returned by redeem(s)
- Rounding direction (implicit in the inequality checks): previewDeposit rounds DOWN (returns fewer shares -> favors vault) previewMint rounds UP (returns more assets -> favors vault) previewWithdraw rounds UP (returns more shares -> favors vault) previewRedeem rounds DOWN (returns fewer assets -> favors vault)
Uses V2 registerFnCallTrigger + ph.context() for call-scoped triggers,
ph.callinputAt() to read call arguments, and ph.callOutputAt() to read the
actual return value — replacing the totalSupply/totalAssets delta inference from V1.
Functions
_registerPreviewTriggers
Register the default trigger set for preview-consistency invariants.
Each ERC-4626 operation gets its own assertion function via registerFnCallTrigger.
function _registerPreviewTriggers() internal view;
_maxPreviewDeviation
Maximum acceptable deviation between a preview result and the actual result.
Defaults to 1 (single-unit rounding). Override for vaults with wider rounding (e.g. multi-step rounding, fee chunking, or decimal normalization).
function _maxPreviewDeviation() internal view virtual returns (uint256);
assertDepositPreview
For the triggering deposit(assets, receiver) call, verifies: previewDeposit(assets) <= actualSharesMinted (ERC-4626 spec) actualSharesMinted - previewDeposit(assets) <= maxDeviation
function assertDepositPreview() external;
assertMintPreview
For the triggering mint(shares, receiver) call, verifies: previewMint(shares) >= actualAssetsCharged (ERC-4626 spec) previewMint(shares) - actualAssetsCharged <= maxDeviation
function assertMintPreview() external;
assertWithdrawPreview
For the triggering withdraw(assets, receiver, owner) call, verifies: previewWithdraw(assets) >= actualSharesBurned (ERC-4626 spec) previewWithdraw(assets) - actualSharesBurned <= maxDeviation
function assertWithdrawPreview() external;
assertRedeemPreview
For the triggering redeem(shares, receiver, owner) call, verifies: previewRedeem(shares) <= actualAssetsReturned (ERC-4626 spec) actualAssetsReturned - previewRedeem(shares) <= maxDeviation
function assertRedeemPreview() external;
_stripSelector
Strip the 4-byte selector from raw call input bytes.
function _stripSelector(bytes memory input) internal pure returns (bytes memory args);
ERC4626SharePriceAssertion
Inherits: ERC4626BaseAssertion
Title: ERC4626SharePriceAssertion
Author: Phylax Systems
Asserts that the vault’s share price (totalAssets / totalSupply) does not decrease beyond a configurable tolerance, both transaction-wide and per individual user operation. Invariants covered:
- Non-dilutive entry/exit: absent explicit fee accrual or loss recognition, deposit/mint/withdraw/redeem must not reduce assets-per-share for remaining holders.
- Rounding favors incumbents: the share price must not move against the vault (i.e., existing holders) during ordinary user operations.
Uses the V2 assetsMatchSharePrice / assetsMatchSharePriceAt precompiles for the
primary check, and ratioGe for an explicit cross-multiplication comparison as a
second, readable signal.
The tolerance is expressed in basis points (1 bps = 0.01%).
A tolerance of 0 enforces strict non-decrease; values like 25-50 allow for rounding noise.
Constants
sharePriceToleranceBps
Maximum acceptable share-price decrease in basis points.
uint256 public immutable sharePriceToleranceBps
Functions
constructor
constructor(uint256 _toleranceBps) ;
_registerSharePriceTriggers
Register the default trigger set for share-price invariants.
Uses registerTxEndTrigger for the tx-wide envelope and registerFnCallTrigger
for per-call checks. Call this inside your triggers().
function _registerSharePriceTriggers() internal view;
assertSharePriceEnvelope
Verifies the share price did not decrease beyond tolerance across the entire transaction.
Uses assetsMatchSharePrice for a comprehensive all-forks check, then ratioGe for an explicit pre/post comparison as a second signal.
function assertSharePriceEnvelope() external;
assertPerCallSharePrice
Verifies each individual deposit/mint/withdraw/redeem call does not decrease the share price beyond tolerance.
Uses ph.context() to get the triggering call boundaries and assetsMatchSharePriceAt for a targeted pre/post-call comparison.
function assertPerCallSharePrice() external;
IERC4626
Title: IERC4626
Minimal ERC-4626 tokenized vault interface for assertion contracts.
Includes the ERC-20 view surface (totalSupply, balanceOf) since ERC-4626 extends ERC-20.
Functions
totalSupply
function totalSupply() external view returns (uint256);
balanceOf
function balanceOf(address account) external view returns (uint256);
asset
function asset() external view returns (address);
totalAssets
function totalAssets() external view returns (uint256);
convertToShares
function convertToShares(uint256 assets) external view returns (uint256);
convertToAssets
function convertToAssets(uint256 shares) external view returns (uint256);
previewDeposit
function previewDeposit(uint256 assets) external view returns (uint256);
previewMint
function previewMint(uint256 shares) external view returns (uint256);
previewWithdraw
function previewWithdraw(uint256 assets) external view returns (uint256);
previewRedeem
function previewRedeem(uint256 shares) external view returns (uint256);
maxDeposit
function maxDeposit(address receiver) external view returns (uint256);
maxMint
function maxMint(address receiver) external view returns (uint256);
maxWithdraw
function maxWithdraw(address owner) external view returns (uint256);
maxRedeem
function maxRedeem(address owner) external view returns (uint256);
deposit
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
mint
function mint(uint256 shares, address receiver) external returns (uint256 assets);
withdraw
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
redeem
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
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
- TestCallInputAt
- TriggeringTx
- TestCallInputs
- TriggeringTx
- TestCallOutputAt
- TriggeringTx
- CallFrameTrigger
- RevertingSubtreeTrigger
- TestForking
- TriggeringTx
- TestGetAdopter
- TriggeringTx
- TestLoad
- TriggeringTx
- TestLogs
- TriggeringTx
- MappingTarget
- TestChangedMappingKeys
- TestMappingValueDiff
- MappingTriggeringTx
- MappingTracing constants
- TestStateChanges1
- TriggeringTx
- TestStateChanges2
- TriggeringTx
- TestStateChangesNone
- TriggeringTx
TestCallInputAt
Inherits: Assertion
Functions
constructor
constructor() payable;
callInputAt
function callInputAt() external view;
emptyCalldataReturnsEmptyBytes
function emptyCalldataReturnsEmptyBytes() external view;
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
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;
TestCallOutputAt
Inherits: Assertion
Functions
constructor
constructor() payable;
callOutputAtReturnsEncodedReturnData
function callOutputAtReturnsEncodedReturnData() external view;
callOutputAtReturnsEmptyBytesForVoidCall
function callOutputAtReturnsEmptyBytesForVoidCall() external view;
callOutputAtReturnsRevertData
function callOutputAtReturnsRevertData() external view;
callOutputAtRejectsRevertedSubtreeCallId
function callOutputAtRejectsRevertedSubtreeCallId() external view;
_successfulNestedWriteId
function _successfulNestedWriteId() internal view returns (uint256 successfulNestedWriteId);
_revertedWriteStorageAndRevertId
function _revertedWriteStorageAndRevertId() internal view returns (uint256);
_revertedSubtreeChildCallId
function _revertedSubtreeChildCallId() internal view returns (uint256);
triggers
function triggers() external view override;
TriggeringTx
Functions
constructor
constructor() payable;
CallFrameTrigger
Functions
trigger
function trigger() external;
RevertingSubtreeTrigger
Functions
trigger
function trigger() external;
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;
MappingTarget
State Variables
balances
mapping(address => uint256) public balances
Functions
setBalance
function setBalance(address user, uint256 amount) external;
TestChangedMappingKeys
Inherits: Assertion
Functions
constructor
constructor() payable;
checkChangedKeys
function checkChangedKeys() external view;
triggers
function triggers() external view override;
TestMappingValueDiff
Inherits: Assertion
Functions
constructor
constructor() payable;
checkValueDiff
function checkValueDiff() external view;
triggers
function triggers() external view override;
MappingTriggeringTx
Functions
constructor
constructor() payable;
Constants
MAPPING_TARGET
MappingTarget constant MAPPING_TARGET = MappingTarget(0xdCCf1eEB153eF28fdc3CF97d33f60576cF092e9c)
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 |
IERC20BalanceReaderLike
Minimal token balance surface used by fork-read helpers.
Functions
balanceOf
function balanceOf(address account) external view returns (uint256);
ForkUtils
Inherits: Credible
Title: ForkUtils
Author: Phylax Systems
Shared fork-aware read and ERC20-delta helpers for assertions and protection suites.
Constants
FORK_VIEW_GAS
Gas forwarded to snapshot-time protocol view calls made through ph.staticcallAt.
uint64 internal constant FORK_VIEW_GAS = 500_000
Functions
_viewFailureMessage
Revert string used when a fork-time static call fails.
Override this when a more specific failure message is useful for a derived contract.
function _viewFailureMessage() internal pure virtual returns (string memory);
_viewAt
Executes a static call against target at a specific snapshot fork.
function _viewAt(address target, bytes memory data, PhEvm.ForkId memory fork)
internal
view
returns (bytes memory resultData);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The protocol contract to query. |
data | bytes | ABI-encoded calldata for the target view. |
fork | PhEvm.ForkId | The snapshot fork the read should execute against. |
Returns
| Name | Type | Description |
|---|---|---|
resultData | bytes | Raw return bytes from the static call. |
_readUintAt
Convenience wrapper that decodes a snapshot-time static call as uint256.
function _readUintAt(address target, bytes memory data, PhEvm.ForkId memory fork)
internal
view
returns (uint256 value);
_readUint8At
Convenience wrapper that decodes a snapshot-time static call as uint8.
function _readUint8At(address target, bytes memory data, PhEvm.ForkId memory fork)
internal
view
returns (uint8 value);
_readAddressAt
Convenience wrapper that decodes a snapshot-time static call as address.
function _readAddressAt(address target, bytes memory data, PhEvm.ForkId memory fork)
internal
view
returns (address value);
_readBoolAt
Convenience wrapper that decodes a snapshot-time static call as bool.
function _readBoolAt(address target, bytes memory data, PhEvm.ForkId memory fork)
internal
view
returns (bool value);
_readBalanceAt
Convenience wrapper that reads an ERC20-style balanceOf(account) at a snapshot fork.
function _readBalanceAt(address token, address account, PhEvm.ForkId memory fork)
internal
view
returns (uint256 balance);
_reducedErc20BalanceDeltasAt
Returns reduced ERC20 transfer deltas for a token within the selected fork scope.
function _reducedErc20BalanceDeltasAt(address token, PhEvm.ForkId memory fork)
internal
view
returns (PhEvm.Erc20TransferData[] memory deltas);
_transferredValueAt
Returns the total amount of token transferred from from to to in the fork scope.
function _transferredValueAt(address token, address from, address to, PhEvm.ForkId memory fork)
internal
view
returns (uint256 value);
_consumedBetween
Computes the non-negative decrease between two snapshot values.
function _consumedBetween(uint256 beforeValue, uint256 afterValue) internal pure returns (uint256 consumed);
IERC20LogUtils
Title: IERC20LogUtils
Author: Phylax Systems
Helpers for querying and decoding standard ERC20 logs returned by PhEvm.
Constants
TRANSFER_EVENT_SIGNATURE
Standard ERC20 Transfer event signature.
bytes32 internal constant TRANSFER_EVENT_SIGNATURE = keccak256("Transfer(address,address,uint256)")
APPROVAL_EVENT_SIGNATURE
Standard ERC20 Approval event signature.
bytes32 internal constant APPROVAL_EVENT_SIGNATURE = keccak256("Approval(address,address,uint256)")
Functions
transferQuery
Builds a PhEvm query for ERC20 Transfer logs emitted by token.
Pass address(0) to match Transfer logs from any emitter.
function transferQuery(address token) internal pure returns (PhEvm.LogQuery memory query);
approvalQuery
Builds a PhEvm query for ERC20 Approval logs emitted by token.
Pass address(0) to match Approval logs from any emitter.
function approvalQuery(address token) internal pure returns (PhEvm.LogQuery memory query);
isTransfer
Returns true when log has the canonical ERC20 Transfer event shape.
function isTransfer(PhEvm.Log memory log) internal pure returns (bool);
isApproval
Returns true when log has the canonical ERC20 Approval event shape.
function isApproval(PhEvm.Log memory log) internal pure returns (bool);
decodeTransfer
Decodes a canonical ERC20 Transfer log.
function decodeTransfer(PhEvm.Log memory log) internal pure returns (PhEvm.Erc20TransferData memory transfer);
decodeApproval
Decodes a canonical ERC20 Approval log.
function decodeApproval(PhEvm.Log memory log) internal pure returns (ApprovalData memory approval);
decodeTransfers
Decodes all logs as ERC20 Transfer events.
Reverts if any log is not a canonical Transfer event.
function decodeTransfers(PhEvm.Log[] memory logs)
internal
pure
returns (PhEvm.Erc20TransferData[] memory transfers);
decodeApprovals
Decodes all logs as ERC20 Approval events.
Reverts if any log is not a canonical Approval event.
function decodeApprovals(PhEvm.Log[] memory logs) internal pure returns (ApprovalData[] memory approvals);
_topicAddress
function _topicAddress(bytes32 topic) private pure returns (address);
Structs
ApprovalData
Decoded ERC20 Approval event data.
struct ApprovalData {
/// @notice The token contract that emitted the Approval event.
address token_addr;
/// @notice The token owner indexed in topic1.
address owner;
/// @notice The approved spender indexed in topic2.
address spender;
/// @notice The approved amount decoded from log data.
uint256 value;
}
LogUtils
Title: LogUtils
Author: Phylax Systems
Pure utility library for matching, filtering, and decoding EVM logs returned by
getLogs() or getLogsQuery(). All functions operate on in-memory PhEvm.Log
structs and do not call any precompiles.
Implemented with internal functions so they inline at the call site and avoid
external call overhead.
Functions
sig
Returns topic[0] of the log, or bytes32(0) if no topics.
function sig(PhEvm.Log memory log) internal pure returns (bytes32);
isSig
True if log.topics[0] == eventSig.
function isSig(PhEvm.Log memory log, bytes32 eventSig) internal pure returns (bool);
isFrom
True if log.emitter == emitter.
function isFrom(PhEvm.Log memory log, address emitter) internal pure returns (bool);
isEvent
True if log matches both emitter and eventSig.
function isEvent(PhEvm.Log memory log, address emitter, bytes32 eventSig) internal pure returns (bool);
indexedTopic
Returns topics[indexedIdx + 1] as raw bytes32. Reverts if out of bounds.
The +1 skips topic[0] (the event signature).
function indexedTopic(PhEvm.Log memory log, uint256 indexedIdx) internal pure returns (bytes32);
indexedAddress
Decodes indexed param as address.
function indexedAddress(PhEvm.Log memory log, uint256 indexedIdx) internal pure returns (address);
indexedUint
Decodes indexed param as uint256.
function indexedUint(PhEvm.Log memory log, uint256 indexedIdx) internal pure returns (uint256);
indexedBool
Decodes indexed param as bool.
function indexedBool(PhEvm.Log memory log, uint256 indexedIdx) internal pure returns (bool);
topic
Encodes address as topic-compatible bytes32 (left-pads 20 bytes to 32).
function topic(address value) internal pure returns (bytes32);
topic
Encodes uint256 as topic-compatible bytes32.
function topic(uint256 value) internal pure returns (bytes32);
first
Returns the first log matching (emitter, eventSig).
function first(PhEvm.Log[] memory logs, address emitter, bytes32 eventSig)
internal
pure
returns (bool found, PhEvm.Log memory log);
Returns
| Name | Type | Description |
|---|---|---|
found | bool | True if a matching log was found. |
log | PhEvm.Log | The first matching log, or an empty log if none found. |
count
Counts logs matching (emitter, eventSig).
function count(PhEvm.Log[] memory logs, address emitter, bytes32 eventSig) internal pure returns (uint256 n);
Assertion
Inherits: ForkUtils, 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...
}
}
Constants
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")))))
specRecorder
The spec recorder precompile for registering the assertion spec
Address is derived from keccak256(“SpecRecorder”)
SpecRecorder constant specRecorder = SpecRecorder(address(uint160(uint256(keccak256("SpecRecorder")))))
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. |
registerFnCallTrigger
Registers an onFnCall trigger. The assertion fires once per matching call, with TriggerContext available via ph.context().
function registerFnCallTrigger(bytes4 fnSelector, bytes4 triggerSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
triggerSelector | bytes4 | The 4-byte selector on the adopter to watch for. |
registerTxEndTrigger
Registers a trigger that fires once after the entire transaction completes.
function registerTxEndTrigger(bytes4 fnSelector) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
registerErc20ChangeTrigger
Registers a trigger that fires when a token’s balances change.
function registerErc20ChangeTrigger(bytes4 fnSelector, address token) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
token | address | The ERC20 token address to watch. |
watchCumulativeOutflow
Registers a circuit breaker trigger that fires when cumulative ERC20 outflow from the assertion adopter exceeds a percentage threshold within a rolling time window.
function watchCumulativeOutflow(address token, uint256 thresholdBps, uint256 windowDuration, bytes4 fnSelector)
internal
view;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address to monitor. |
thresholdBps | uint256 | Maximum cumulative outflow as basis points of the TVL snapshot taken at window start. 1000 = 10%. |
windowDuration | uint256 | Rolling window length in seconds. |
fnSelector | bytes4 | The assertion function to invoke when the threshold is breached. |
watchCumulativeInflow
Registers a circuit breaker trigger that fires when cumulative ERC20 inflow into the assertion adopter exceeds a percentage threshold within a rolling time window.
function watchCumulativeInflow(address token, uint256 thresholdBps, uint256 windowDuration, bytes4 fnSelector)
internal
view;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address to monitor. |
thresholdBps | uint256 | Maximum cumulative inflow as basis points of the TVL snapshot taken at window start. 1000 = 10%. |
windowDuration | uint256 | Rolling window length in seconds. |
fnSelector | bytes4 | The assertion function to invoke when the threshold is breached. |
_successOnlyFilter
Returns a CallFilter that only matches successful calls at any depth.
function _successOnlyFilter() internal pure returns (PhEvm.CallFilter memory filter);
_matchingCalls
Returns successful calls matching target and selector, up to limit.
function _matchingCalls(address target, bytes4 selector, uint256 limit)
internal
view
returns (PhEvm.TriggerCall[] memory);
_preTx
function _preTx() internal pure returns (PhEvm.ForkId memory);
_postTx
function _postTx() internal pure returns (PhEvm.ForkId memory);
_preCall
function _preCall(uint256 callId) internal pure returns (PhEvm.ForkId memory);
_postCall
function _postCall(uint256 callId) internal pure returns (PhEvm.ForkId memory);
registerAssertionSpec
Registers the desired assertion spec. Must be called within the constructor. The assertion spec defines what subset of precompiles are available. Can only be called once. For an assertion to be valid, it needs a defined spec.
function registerAssertionSpec(AssertionSpec spec) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
spec | AssertionSpec | The desired AssertionSpec. |
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.
Constants
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.
Constants
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
Constants
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
DEPRECATED: Use staticcallAt / loadStateAt with ForkId instead.
function forkPreTx() external;
forkPostTx
Fork to the state after the assertion-triggering transaction
DEPRECATED: Use staticcallAt / loadStateAt with ForkId instead.
function forkPostTx() external;
forkPreCall
Fork to the state before a specific call execution
DEPRECATED: Use staticcallAt / loadStateAt with ForkId instead.
function forkPreCall(uint256 id) external;
forkPostCall
Fork to the state after a specific call execution
DEPRECATED: Use staticcallAt / loadStateAt with ForkId instead.
function forkPostCall(uint256 id) external;
load
Load a storage slot value from any address
DEPRECATED: Use loadStateAt with ForkId instead.
function load(address target, bytes32 slot) external view returns (bytes32 data);
loadStateAt
Read a storage slot from the current assertion adopter at a snapshot.
function loadStateAt(bytes32 slot, ForkId calldata fork) external view returns (bytes32 value);
Parameters
| Name | Type | Description |
|---|---|---|
slot | bytes32 | The storage slot to read. |
fork | ForkId | The snapshot fork to read from. |
Returns
| Name | Type | Description |
|---|---|---|
value | bytes32 | The raw 32-byte value at the slot. |
loadStateAt
Read a storage slot from any account at a snapshot.
function loadStateAt(address target, bytes32 slot, ForkId calldata fork) external view returns (bytes32 value);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The address to read storage from. |
slot | bytes32 | The storage slot to read. |
fork | ForkId | The snapshot fork to read from. |
Returns
| Name | Type | Description |
|---|---|---|
value | bytes32 | The raw 32-byte value at the slot. |
staticcallAt
Execute a static call against a snapshot fork.
function staticcallAt(address target, bytes calldata data, uint64 gas_limit, ForkId calldata fork)
external
view
returns (StaticCallResult memory result);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The contract to call. |
data | bytes | The ABI-encoded function call. |
gas_limit | uint64 | The gas budget forwarded to the nested static call. |
fork | ForkId | The snapshot fork to execute against. |
Returns
| Name | Type | Description |
|---|---|---|
result | StaticCallResult | Success flag and return or revert bytes from the nested call. |
getLogsQuery
Get logs matching a query from a snapshot fork
function getLogsQuery(LogQuery calldata query, ForkId calldata fork) external view returns (Log[] memory logs);
Parameters
| Name | Type | Description |
|---|---|---|
query | LogQuery | The emitter and signature filters to apply |
fork | ForkId | The snapshot fork to read logs from |
Returns
| Name | Type | Description |
|---|---|---|
logs | Log[] | Array of logs matching the query inside the selected snapshot window |
getErc20Transfers
Returns all ERC20 transfers for a single token in the specified fork.
function getErc20Transfers(address token, ForkId calldata fork)
external
view
returns (Erc20TransferData[] memory transfers);
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address. |
fork | ForkId | The fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
transfers | Erc20TransferData[] | Array of decoded transfer records. |
getErc20TransfersForTokens
Returns all ERC20 transfers for multiple tokens in the specified fork.
function getErc20TransfersForTokens(address[] calldata tokens, ForkId calldata fork)
external
view
returns (Erc20TransferData[] memory transfers);
Parameters
| Name | Type | Description |
|---|---|---|
tokens | address[] | Array of ERC20 token addresses. |
fork | ForkId | The fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
transfers | Erc20TransferData[] | Combined array of decoded transfer records across all tokens. |
changedErc20BalanceDeltas
Returns all transfers involving the given token for the specified fork.
Semantic alias of getErc20Transfers for balance-delta workflows.
function changedErc20BalanceDeltas(address token, ForkId calldata fork)
external
view
returns (Erc20TransferData[] memory deltas);
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address. |
fork | ForkId | The fork to query. |
reduceErc20BalanceDeltas
Reduces transfers into net balance deltas per unique (from, to) pair.
function reduceErc20BalanceDeltas(address token, ForkId calldata fork)
external
view
returns (Erc20TransferData[] memory deltas);
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address. |
fork | ForkId | The fork to query. |
Returns
| Name | Type | Description |
|---|---|---|
deltas | Erc20TransferData[] | Aggregated transfer records in first-seen pair order. |
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 |
callOutputAt
Returns the raw return or revert bytes for a traced call.
function callOutputAt(uint256 callId) external view returns (bytes memory output);
Parameters
| Name | Type | Description |
|---|---|---|
callId | uint256 | The call identifier from CallInputs.id. |
Returns
| Name | Type | Description |
|---|---|---|
output | bytes | The raw ABI-encoded return bytes or revert bytes. |
callinputAt
Returns the calldata of a specific call.
function callinputAt(uint256 callId) external view returns (bytes memory input);
Parameters
| Name | Type | Description |
|---|---|---|
callId | uint256 | The call ID to read input from. |
Returns
| Name | Type | Description |
|---|---|---|
input | bytes | The raw calldata bytes (selector + ABI-encoded arguments). |
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) |
forbidChangeForSlot
Checks that a single storage slot on the assertion adopter was not modified.
function forbidChangeForSlot(bytes32 slot) external returns (bool ok);
Parameters
| Name | Type | Description |
|---|---|---|
slot | bytes32 | The slot to protect. |
Returns
| Name | Type | Description |
|---|---|---|
ok | bool | True when the slot was not written during the transaction. |
forbidChangeForSlots
Checks that none of the given storage slots on the assertion adopter were modified.
function forbidChangeForSlots(bytes32[] calldata slots) external returns (bool ok);
Parameters
| Name | Type | Description |
|---|---|---|
slots | bytes32[] | The slots to protect. |
Returns
| Name | Type | Description |
|---|---|---|
ok | bool | True when none of the slots were written during the transaction. |
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 |
getTxObject
Get the original transaction object that triggered the assertion
Returns the transaction envelope data for the assertion-triggering tx
function getTxObject() external view returns (TxObject memory txObject);
Returns
| Name | Type | Description |
|---|---|---|
txObject | TxObject | The transaction data struct |
context
Returns the context for the current onFnCall trigger invocation.
Only valid inside an assertion function triggered by registerFnCallTrigger. Reverts if called outside of an onFnCall-triggered assertion.
function context() external view returns (TriggerContext memory);
matchingCalls
Returns calls matching the given target, selector, and filter criteria.
function matchingCalls(address target, bytes4 selector, CallFilter calldata filter, uint256 limit)
external
view
returns (TriggerCall[] memory calls);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The target contract address. |
selector | bytes4 | The function selector to filter by. |
filter | CallFilter | Filtering criteria (call type, depth, success). |
limit | uint256 | Maximum number of results to return. |
Returns
| Name | Type | Description |
|---|---|---|
calls | TriggerCall[] | Array of matching call records. |
getLogsForCall
Returns logs emitted during a specific call frame.
function getLogsForCall(LogQuery calldata query, uint256 callId) external view returns (Log[] memory logs);
Parameters
| Name | Type | Description |
|---|---|---|
query | LogQuery | The emitter and signature filters to apply. |
callId | uint256 | The call ID to scope the log query to. |
Returns
| Name | Type | Description |
|---|---|---|
logs | Log[] | Array of logs emitted during the call. |
store
Write a bytes32 value to persistent assertion storage.
function store(bytes32 key, bytes32 value) external;
Parameters
| Name | Type | Description |
|---|---|---|
key | bytes32 | The storage key. |
value | bytes32 | The value to store. |
load
Read a bytes32 value from persistent assertion storage.
function load(bytes32 key) external view returns (bytes32 value);
Parameters
| Name | Type | Description |
|---|---|---|
key | bytes32 | The storage key. |
Returns
| Name | Type | Description |
|---|---|---|
value | bytes32 | The stored value. |
exists
Check if a key exists in persistent assertion storage.
function exists(bytes32 key) external view returns (bool doesExist);
Parameters
| Name | Type | Description |
|---|---|---|
key | bytes32 | The storage key. |
Returns
| Name | Type | Description |
|---|---|---|
doesExist | bool | True if the key has been written to. |
values_left
Returns remaining storage slots available to this assertion.
function values_left() external view returns (uint256 remaining);
changedMappingKeys
Returns canonical Solidity key encodings h(key) for keys whose mapping entry at baseSlot was written during the tx.
Best-effort heuristic: traces KECCAK256 -> SSTORE provenance in the execution trace. Custom inline assembly or precomputed hashed slots can bypass the visible keccak chain and produce false negatives.
function changedMappingKeys(address target, bytes32 baseSlot) external view returns (bytes[] memory keys);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The contract whose storage was modified. |
baseSlot | bytes32 | The Solidity mapping’s base storage slot. |
Returns
| Name | Type | Description |
|---|---|---|
keys | bytes[] | Array of encoded keys (each is the h(key) preimage). |
mappingValueDiff
Returns the pre/post values for a specific mapping entry.
Computes slot = keccak256(key ++ baseSlot) + fieldOffset, then reads pre from the PreTx fork and post from the PostTx fork.
function mappingValueDiff(address target, bytes32 baseSlot, bytes calldata key, uint256 fieldOffset)
external
view
returns (bytes32 pre, bytes32 post, bool changed);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The contract address. |
baseSlot | bytes32 | The mapping’s base slot. |
key | bytes | The canonical encoding h(key) of the mapping key. |
fieldOffset | uint256 | Struct field offset (0 for the first slot of the value). |
Returns
| Name | Type | Description |
|---|---|---|
pre | bytes32 | The PreTx value. |
post | bytes32 | The PostTx value. |
changed | bool | True if pre != post. |
assetsMatchSharePrice
Checks ERC4626 share price consistency across all fork points.
function assetsMatchSharePrice(address vault, uint256 toleranceBps) external returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
vault | address | The ERC4626 vault address. |
toleranceBps | uint256 | Maximum allowed deviation in basis points. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if share price stays within tolerance at all forks. |
assetsMatchSharePriceAt
Checks ERC4626 share price consistency between two specific forks.
function assetsMatchSharePriceAt(address vault, uint256 toleranceBps, ForkId calldata fork0, ForkId calldata fork1)
external
returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
vault | address | The ERC4626 vault address. |
toleranceBps | uint256 | Maximum allowed deviation in basis points. |
fork0 | ForkId | The baseline fork. |
fork1 | ForkId | The comparison fork. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if share price stays within tolerance. |
conserveBalance
Checks that an account’s ERC20 balance is unchanged between two forks.
function conserveBalance(ForkId calldata fork0, ForkId calldata fork1, address token, address account)
external
returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
fork0 | ForkId | The baseline fork. |
fork1 | ForkId | The comparison fork. |
token | address | The ERC20 token address. |
account | address | The account whose balance should remain unchanged. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if balanceOf(account) is identical at both forks. |
outflowContext
Returns context about the outflow that triggered this assertion.
Only valid inside an assertion function triggered by watchCumulativeOutflow. Returns a zeroed struct if called from a non-outflow trigger context.
function outflowContext() external view returns (OutflowContext memory ctx);
Returns
| Name | Type | Description |
|---|---|---|
ctx | OutflowContext | The outflow context for the current trigger invocation. |
inflowContext
Returns context about the inflow that triggered this assertion.
Only valid inside an assertion function triggered by watchCumulativeInflow. Returns a zeroed struct if called from a non-inflow trigger context.
function inflowContext() external view returns (InflowContext memory ctx);
Returns
| Name | Type | Description |
|---|---|---|
ctx | InflowContext | The inflow context for the current trigger invocation. |
oracleSanity
Checks oracle price consistency across all fork points.
function oracleSanity(address target, bytes calldata data, uint256 bpsDeviation) external returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The oracle contract address. |
data | bytes | The ABI-encoded oracle query. |
bpsDeviation | uint256 | Maximum allowed deviation in basis points. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if oracle price stays within tolerance. |
oracleSanityAt
Checks oracle price consistency between two specific forks.
function oracleSanityAt(
address target,
bytes calldata data,
uint256 bpsDeviation,
ForkId calldata initialFork,
ForkId calldata currentFork
) external returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
target | address | The oracle contract address. |
data | bytes | The ABI-encoded oracle query. |
bpsDeviation | uint256 | Maximum allowed deviation in basis points. |
initialFork | ForkId | The baseline fork. |
currentFork | ForkId | The comparison fork. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | True if oracle price stays within tolerance. |
mulDivDown
Computes (x * y) / denominator, rounded down. Uses 512-bit intermediates.
function mulDivDown(uint256 x, uint256 y, uint256 denominator) external pure returns (uint256 result);
mulDivUp
Computes (x * y) / denominator, rounded up. Uses 512-bit intermediates.
function mulDivUp(uint256 x, uint256 y, uint256 denominator) external pure returns (uint256 result);
normalizeDecimals
Scales an amount from one decimal base to another.
function normalizeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals)
external
pure
returns (uint256 result);
ratioGe
Compares two ratios with tolerance: num1/den1 >= num2/den2 * (1 - toleranceBps/10000).
Uses cross-multiplication with wide intermediates to avoid division and overflow.
function ratioGe(uint256 num1, uint256 den1, uint256 num2, uint256 den2, uint256 toleranceBps)
external
pure
returns (bool);
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;
}
LogQuery
Query used to filter transaction logs by emitter and/or signature
struct LogQuery {
/// @notice address(0) matches any emitter
address emitter;
/// @notice bytes32(0) matches any topic0 signature
bytes32 signature;
}
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;
}
TxObject
Contains data about the original assertion-triggering transaction
Provides access to transaction envelope data for inspection in assertions
struct TxObject {
/// @notice The address that initiated the transaction (tx.origin equivalent)
address from;
/// @notice The transaction recipient, or address(0) for contract creation
address to;
/// @notice The ETH value sent with the transaction
uint256 value;
/// @notice The chain ID, or 0 if not present
uint64 chain_id;
/// @notice The gas limit for the transaction
uint64 gas_limit;
/// @notice The gas price or max_fee_per_gas for EIP-1559 transactions
uint128 gas_price;
/// @notice The transaction calldata
bytes input;
}
StaticCallResult
Result of a nested static call executed against a snapshot.
struct StaticCallResult {
/// @notice Whether the nested call completed successfully
bool ok;
/// @notice Raw return data or revert data from the nested call
bytes data;
}
Erc20TransferData
Decoded ERC20 Transfer event data from a snapshot fork.
struct Erc20TransferData {
/// @notice The token contract that emitted the Transfer event
address token_addr;
/// @notice The sender indexed in topic1
address from;
/// @notice The receiver indexed in topic2
address to;
/// @notice The transferred amount decoded from log data
uint256 value;
}
ForkId
Identifies a read-only transaction snapshot.
forkType: 0 = PreTx, 1 = PostTx, 2 = PreCall, 3 = PostCall callIndex is used only for call-scoped snapshots.
struct ForkId {
uint8 forkType;
uint256 callIndex;
}
TriggerContext
Context for an onFnCall-triggered assertion invocation.
Only valid inside an assertion function triggered by registerFnCallTrigger.
struct TriggerContext {
/// @notice The function selector that was called on the adopter
bytes4 selector;
/// @notice Call index for constructing PreCall ForkId
uint256 callStart;
/// @notice Call index for constructing PostCall ForkId
uint256 callEnd;
}
CallFilter
Filter criteria for matchingCalls queries.
struct CallFilter {
/// @notice Call type: 0 = any, 1 = CALL, 2 = STATICCALL, 3 = DELEGATECALL, 4 = CALLCODE
uint8 callType;
/// @notice Minimum call depth to include
uint32 minDepth;
/// @notice Maximum call depth to include
uint32 maxDepth;
/// @notice If true, only return top-level calls (depth == 1)
bool topLevelOnly;
/// @notice If true, only return calls that succeeded
bool successOnly;
}
TriggerCall
Detailed record of a call in the transaction trace.
struct TriggerCall {
uint256 callId;
uint256 parentCallId;
address caller;
address target;
address codeAddress;
bytes4 selector;
uint32 depth;
uint8 callType;
bool success;
uint256 value;
bytes input;
}
OutflowContext
Context about the outflow that triggered an assertion via watchCumulativeOutflow.
Only valid inside an assertion function triggered by watchCumulativeOutflow. Returns a zeroed struct if called from a non-outflow trigger context.
struct OutflowContext {
/// @notice The ERC20 token that breached the threshold
address token;
/// @notice Net outflow within the window (token units)
uint256 cumulativeOutflow;
/// @notice Total absolute outflow within the window (token units, ignoring deposits)
uint256 absoluteOutflow;
/// @notice Current outflow as basis points of TVL snapshot
uint256 currentBps;
/// @notice Adopter's token balance at window start
uint256 tvlSnapshot;
/// @notice Timestamp when the current window began
uint256 windowStart;
/// @notice Timestamp when the current window expires
uint256 windowEnd;
}
InflowContext
Context about the inflow that triggered an assertion via watchCumulativeInflow.
Only valid inside an assertion function triggered by watchCumulativeInflow. Returns a zeroed struct if called from a non-inflow trigger context.
struct InflowContext {
/// @notice The ERC20 token that breached the threshold
address token;
/// @notice Net inflow within the window (token units)
uint256 cumulativeInflow;
/// @notice Total absolute inflow within the window (token units, ignoring withdrawals)
uint256 absoluteInflow;
/// @notice Current inflow as basis points of TVL snapshot
uint256 currentBps;
/// @notice Adopter's token balance at window start
uint256 tvlSnapshot;
/// @notice Timestamp when the current window began
uint256 windowStart;
/// @notice Timestamp when the current window expires
uint256 windowEnd;
}
AssertionSpec
The assertion spec defines what subset of precompiles are available. All new specs derive and expose all precompiles from the old definitions, unless specified otherwise.
enum AssertionSpec {
/// @notice Standard set of PhEvm precompiles available at launch.
Legacy,
/// @notice Contains tx object precompiles.
Reshiram,
/// @notice Unrestricted access to all available precompiles. May be untested and dangerous.
Experimental
}
SpecRecorder
Title: SpecRecorder
Author: Phylax Systems
Precompile interface for registering the desired assertion spec
Used within the constructor of assertion contracts to specify which subset of PhEvm precompiles should be available during assertion execution. You can only call registerAssertionSpec once per assertion.
Functions
registerAssertionSpec
Called within the constructor to set the desired assertion spec. The assertion spec defines what subset of precompiles are available. You can only call this function once. For an assertion to be valid, it needs to have a defined spec.
function registerAssertionSpec(AssertionSpec spec) external view;
Parameters
| Name | Type | Description |
|---|---|---|
spec | AssertionSpec | The desired AssertionSpec. |
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. |
registerFnCallTrigger
Registers a trigger that fires when the adopter receives a call matching triggerSelector. The assertion fires once per matching call, with TriggerContext available via ph.context().
function registerFnCallTrigger(bytes4 fnSelector, bytes4 triggerSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
triggerSelector | bytes4 | The 4-byte selector on the adopter to watch for. |
registerTxEndTrigger
Registers a trigger that fires once after the entire transaction completes.
function registerTxEndTrigger(bytes4 fnSelector) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
registerErc20ChangeTrigger
Registers a trigger that fires when a token’s balances change.
function registerErc20ChangeTrigger(bytes4 fnSelector, address token) external view;
Parameters
| Name | Type | Description |
|---|---|---|
fnSelector | bytes4 | The assertion function to invoke. |
token | address | The ERC20 token address to watch. |
watchCumulativeOutflow
Registers a circuit breaker trigger that fires when cumulative ERC20 outflow from the assertion adopter exceeds a percentage threshold within a rolling time window.
The executor handles all persistent state tracking, TVL snapshots, and threshold enforcement internally.
function watchCumulativeOutflow(address token, uint256 thresholdBps, uint256 windowDuration, bytes4 fnSelector)
external
view;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address to monitor. |
thresholdBps | uint256 | Maximum cumulative outflow as basis points of the TVL snapshot taken at window start. 1000 = 10%. |
windowDuration | uint256 | Rolling window length in seconds. Balance deltas are stored in 10-second buckets; old buckets that fall outside the window are dropped as the window slides forward. |
fnSelector | bytes4 | The assertion function to invoke when the threshold is breached. |
watchCumulativeInflow
Registers a circuit breaker trigger that fires when cumulative ERC20 inflow into the assertion adopter exceeds a percentage threshold within a rolling time window.
The executor handles all persistent state tracking, TVL snapshots, and threshold enforcement internally.
function watchCumulativeInflow(address token, uint256 thresholdBps, uint256 windowDuration, bytes4 fnSelector)
external
view;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token address to monitor. |
thresholdBps | uint256 | Maximum cumulative inflow as basis points of the TVL snapshot taken at window start. 1000 = 10%. |
windowDuration | uint256 | Rolling window length in seconds. Balance deltas are stored in 10-second buckets; old buckets that fall outside the window are dropped as the window slides forward. |
fnSelector | bytes4 | The assertion function to invoke when the threshold is breached. |