Use Case
This assertion monitors oracle price updates to detect and prevent price manipulation within a single transaction. Price oracles are critical security components that many DeFi protocols rely on for valuation, liquidations, and trading decisions. Intra-transaction price manipulations can lead to:
- Flash loan attacks that temporarily manipulate oracle prices
- Malicious arbitrage exploiting large price deviations
- Theft of funds through manipulated liquidations or swaps
- Protocol insolvency due to incorrect asset valuations
This assertion is particularly important for:
- Preventing price manipulation attacks that exploit multiple price updates
- Ensuring price stability across oracle updates within a transaction
- Protecting protocols from flash-loan based oracle attacks
- Maintaining protocol security by enforcing price deviation thresholds
Applicable Protocols
- AMM-based DEXs that use oracle prices for various operations
- Lending protocols that rely on oracles for collateral valuation
- Synthetic asset platforms that need stable and accurate price feeds
- Derivatives protocols that use oracles for settlement prices
- Yield farming platforms that calculate rewards based on asset prices
These protocols need this assertion because:
- DEXs can be exploited through price manipulation to extract value
- Lending protocols need accurate price data to maintain proper collateralization
- Synthetic asset platforms require stable oracle prices to maintain peg
- Derivatives protocols depend on reliable pricing for fair settlement
- Yield platforms may miscalculate rewards with manipulated prices
Explanation
The assertion implements a simple but effective approach to verify oracle price updates:
-
Pre/Post State Comparison:
- Uses
forkPreState()
to capture the price before the transaction
- Uses
forkPostState()
to capture the price after the transaction
- Compares pre and post states to detect price deviations (reverts early if deviation is detected)
-
Intra-Transaction Inspection:
- Uses
getCallInputs()
to capture all price update calls within the transaction
- Verifies each update’s price parameter against the initial price
- Enforces that no price update can deviate more than the maximum allowed percentage from the initial price
The assertion uses the following cheatcodes:
forkPreState()
: Captures the oracle price before any updates
forkPostState()
: Captures the oracle price after all updates
getCallInputs()
: Monitors all price update function calls within the transaction
registerCallTrigger()
: Triggers the assertion when an oracle price update is detected
This approach ensures that:
- Oracle prices remain within an acceptable deviation range throughout a transaction
- Flash loan attacks attempting to manipulate prices are detected and prevented
- Both the final price and any intermediate price updates are validated against the initial price
For more information about cheatcodes, see the Cheatcodes Documentation.
Code Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Assertion} from "credible-std/Assertion.sol";
import {PhEvm} from "credible-std/PhEvm.sol";
interface IOracle {
function updatePrice(uint256 price) external;
function price() external view returns (uint256);
}
contract IntraTxOracleDeviationAssertion is Assertion {
// The oracle contract to monitor
IOracle public oracle;
// Maximum allowed price deviation (10% by default)
uint256 public constant MAX_DEVIATION_PERCENTAGE = 10;
constructor(address oracle_) {
oracle = IOracle(oracle_);
}
function triggers() external view override {
// Register trigger for oracle price update calls
registerCallTrigger(this.assertOracleDeviation.selector, oracle.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 {
// Start with a simple check comparing pre and post state
ph.forkPreState();
uint256 prePrice = oracle.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
ph.forkPostState();
uint256 postPrice = oracle.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(oracle), oracle.updatePrice.selector);
// Check each price update to ensure none deviate more than allowed from initial price
for (uint256 i = 0; i < priceUpdates.length; i++) {
// Decode the price from the function call data
uint256 updatedPrice = abi.decode(priceUpdates[i].input, (uint256));
// Verify each update is within allowed deviation from initial pre-state price
require(
updatedPrice >= minAllowedPrice && updatedPrice <= maxAllowedPrice,
"Oracle intra-tx price deviation exceeds threshold"
);
}
}
}
Note: This code example is maintained in the Phylax Assertion Examples Repository. For a full examples with mock protocol code and tests please refer to the repository.
Testing
To test this assertion:
- Deploy a test oracle contract with price update functionality
- Create test scenarios with varying price update patterns:
- Single price updates with different deviation amounts
- Multiple price updates within acceptable ranges
- Multiple price updates with excessive deviations
- Flash loan-style attacks with price manipulation
- Verify the assertion correctly:
- Allows updates within acceptable deviation thresholds
- Blocks updates exceeding deviation thresholds
- Detects suspicious patterns of sequential updates
Assertion Best Practices
- Adjust the
MAX_DEVIATION_PERCENTAGE
based on the specific asset’s volatility
- Combine with Oracle Validation for comprehensive oracle protection
- Consider implementing time-weighted average prices (TWAP) in volatile markets
- Add specific detection for sandwich patterns (price up then down, or vice versa)
- Monitor transaction origin to detect potential flash loan sources
- Consider different thresholds for different assets or market conditions
Responses are generated using AI and may contain mistakes.