Documentation Index
Fetch the complete documentation index at: https://docs.phylax.systems/llms.txt
Use this file to discover all available pages before exploring further.
When to Use This Pattern
Monitors oracle price updates to detect and prevent price manipulation within a single transaction, protecting against flash loan attacks and malicious arbitrage. Critical for AMM-based DEXs using oracle prices, lending protocols relying on oracles for collateral valuation, synthetic asset platforms, derivatives protocols using oracles for settlement, and yield farming platforms calculating rewards based on asset prices.
Intra-transaction price manipulations can lead to flash loan attacks, theft of funds through manipulated liquidations, or protocol insolvency due to incorrect asset valuations.
What This Pattern Checks
Implements approach to verify oracle price updates using both pre/post state comparison and intra-transaction inspection:
_preTx() / _postTx() with Reshiram snapshot reads: Capture oracle price before and after transaction, compare for deviations
getCallInputs(): Monitor all price update function calls within transaction
registerFnCallTrigger(): Trigger when oracle price update is detected
- Verify each update’s price parameter against initial price
- Enforce no price update can deviate more than maximum allowed percentage
The assertion ensures oracle prices remain within acceptable deviation range throughout transaction, detecting and preventing flash loan attacks that attempt to manipulate prices.
For more information about cheatcodes, see the Cheatcodes Documentation.
Assertion Pattern
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Assertion} from "credible-std/Assertion.sol";
import {AssertionSpec} from "credible-std/SpecRecorder.sol";
import {PhEvm} from "credible-std/PhEvm.sol";
contract IntraTxOracleDeviationAssertion is Assertion {
constructor() {
registerAssertionSpec(AssertionSpec.Reshiram);
}
// Maximum allowed price deviation (10% by default)
uint256 public constant MAX_DEVIATION_PERCENTAGE = 10;
function triggers() external view override {
// Register trigger for oracle price update calls
registerFnCallTrigger(this.assertOracleDeviation.selector, IOracle.updatePrice.selector);
}
// Check that price updates don't deviate more than the allowed percentage
// from the initial price at any point during the transaction
function assertOracleDeviation() external {
// Get the assertion adopter address
IOracle adopter = IOracle(ph.getAssertionAdopter());
// Start with a simple check comparing pre and post state
PhEvm.ForkId memory preFork = _preTx();
uint256 prePrice = adopter.price();
// Calculate allowed deviation thresholds (10% by default)
uint256 maxAllowedPrice = (prePrice * (100 + MAX_DEVIATION_PERCENTAGE)) / 100;
uint256 minAllowedPrice = (prePrice * (100 - MAX_DEVIATION_PERCENTAGE)) / 100;
// First check post-state price
PhEvm.ForkId memory postFork = _postTx();
uint256 postPrice = adopter.price();
// Verify post-state price is within allowed deviation from initial price
require(
postPrice >= minAllowedPrice && postPrice <= maxAllowedPrice,
"Oracle post-state price deviation exceeds threshold"
);
// Get all price update calls in this transaction
// Since this assertion is triggered by updatePrice calls, we know there's at least one
PhEvm.CallInputs[] memory priceUpdates = ph.getCallInputs(address(adopter), adopter.updatePrice.selector);
// Check each price update to ensure none deviate more than allowed from initial price
for (uint256 i = 0; i < priceUpdates.length; i++) {
PhEvm.ForkId memory postCallFork = PhEvm.ForkId({forkType: 3, callIndex: priceUpdates[i].id});
// Call the price function at the given frame in the call stack
uint256 updatedPrice = adopter.price();
// Verify each update is within allowed deviation from initial pre-state price
require(
updatedPrice >= minAllowedPrice && updatedPrice <= maxAllowedPrice,
"Oracle intra-tx price deviation exceeds threshold"
);
}
}
}
interface IOracle {
function updatePrice(uint256 price) external;
function price() external view returns (uint256);
}
Full examples and mock protocol code are available in
credible-std.