Use Case

In Automated Market Makers (AMMs), particularly those using concentrated liquidity models like Uniswap V3, the price is represented by ticks. A tick is a discrete unit of measurement for the price of an asset, and maintaining proper tick boundaries is critical for protocol security.

This assertion is particularly important for:

  • Preventing price manipulation through tick boundary violations
  • Ensuring trades execute at valid price points
  • Maintaining protocol invariants around price representation
  • Detecting potential exploits that could manipulate tick boundaries

For example, if an attacker could push the price outside the valid tick range, they could potentially:

  • Execute trades at invalid prices
  • Manipulate liquidity positions
  • Cause protocol calculations to fail

Applicable Protocols

  • AMMs using concentrated liquidity models (e.g., Uniswap V3, PancakeSwap V3)
  • Protocols that rely on tick-based price calculations
  • DeFi protocols that interact with tick-based AMMs
  • Cross-chain bridges that use tick-based price feeds

Explanation

The assertion implements a focused approach to verify tick integrity:

  1. Tick Range Validation:

    • Verifies that ticks stay within global bounds (-887272 to 887272)
    • Prevents tick manipulation outside valid ranges
    • Ensures the AMM’s mathematical model remains valid
  2. Tick Spacing Alignment:

    • Ensures ticks align with the pool’s tick spacing
    • Maintains proper concentrated liquidity calculations
    • Prevents position management errors

The assertion uses the following cheatcodes and functions:

  • ph.forkPreState(): Captures the initial tick state
  • ph.forkPostState(): Captures the final tick state
  • registerStorageChangeTrigger(): Monitors changes to tick-related storage slots

This focused approach ensures:

  1. Ticks remain within protocol-defined bounds
  2. Tick spacing rules are enforced
  3. Core AMM functionality remains secure

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";

// Uniswap v3 pool style interface
interface IUniswapV3Pool {
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );
    function tickSpacing() external view returns (int24);
}

contract PriceWithinTicksAssertion is Assertion {
    IUniswapV3Pool public pool;

    // Uniswap V3 tick bounds
    int24 constant MIN_TICK = -887272;
    int24 constant MAX_TICK = 887272;

    constructor(IUniswapV3Pool pool_) {
        pool = pool_;
    }

    function triggers() external view override {
        // Register trigger for changes to the tick storage slot
        // Assumes tick is stored in slot0 of the pool contract
        registerStorageChangeTrigger(this.priceWithinTicks.selector, bytes32(uint256(0)));
    }

    // Check that the price is within the tick bounds and that the tick is divisible by the tick spacing
    function priceWithinTicks() external {
        // Get pre-swap state
        ph.forkPreState();
        (, int24 preTick,,,,,) = pool.slot0();

        // Get post-swap state
        ph.forkPostState();
        (, int24 postTick,,,,,) = pool.slot0();
        int24 spacing = pool.tickSpacing();

        // Check 1: Tick must be within global bounds
        require(postTick >= MIN_TICK && postTick <= MAX_TICK, "Tick outside global bounds");

        // Check 2: Tick must be divisible by tickSpacing
        require(postTick % spacing == 0, "Tick not aligned with spacing");
    }
}

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:

  1. Deploy a mock Uniswap V3 pool contract
  2. Create test scenarios that:
    • Execute trades within valid tick ranges
    • Attempt trades that would push ticks outside bounds
    • Test tick spacing alignment

Assertion Best Practices

  • Combine this assertion with other AMM-related assertions like Fee Calculations
  • Consider protocol-specific parameters for:
    • Tick spacing requirements
    • Global tick bounds (if different from Uniswap V3)