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
Ensures the constant product (k = x * y) is maintained in AMM pools, preventing unauthorized manipulation of pool reserves and potential flash loan attacks. Critical for Uniswap V2-style AMMs, DEX aggregators, lending protocols using AMM price oracles, and yield farming protocols providing liquidity.
The constant product formula is fundamental to many AMM designs - any deviation could indicate a serious security issue or potential exploit.
What This Pattern Checks
Monitors the product of reserves (k) in AMM pools to ensure it remains constant after transactions using:
_preTx() / _postTx() with Reshiram snapshot reads: Compare reserve product before and after transaction
ph.loadStateAt(): Load reserve slots before and after the transaction
- Direct validation that k = reserve0 * reserve1 remains unchanged
The assertion verifies both initial/final states and detects potential manipulations during transaction execution.
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 ConstantProductAssertion is Assertion {
bytes32 internal constant RESERVE0_SLOT = bytes32(uint256(0));
bytes32 internal constant RESERVE1_SLOT = bytes32(uint256(1));
constructor() {
registerAssertionSpec(AssertionSpec.Reshiram);
}
function triggers() external view override {
registerTxEndTrigger(this.assertionConstantProduct.selector);
}
/// @notice Checks that the pool's stored reserve product is not reduced by the transaction.
function assertionConstantProduct() external view {
address pool = ph.getAssertionAdopter();
PhEvm.ForkId memory preFork = _preTx();
PhEvm.ForkId memory postFork = _postTx();
uint256 reserve0Pre = uint256(ph.loadStateAt(pool, RESERVE0_SLOT, preFork));
uint256 reserve1Pre = uint256(ph.loadStateAt(pool, RESERVE1_SLOT, preFork));
uint256 reserve0Post = uint256(ph.loadStateAt(pool, RESERVE0_SLOT, postFork));
uint256 reserve1Post = uint256(ph.loadStateAt(pool, RESERVE1_SLOT, postFork));
require(reserve0Post * reserve1Post >= reserve0Pre * reserve1Pre, "Constant product invariant reduced");
}
}
Full examples and mock protocol code are available in
credible-std.