Batcher

Description:

Decentralized Finance (DeFi) protocol contract providing Swap, Liquidity, Factory functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/Batcher.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @notice Minimal subset of the Uniswap V3 pool interface needed for reads.
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);

    // ticks mapping: returns (liquidityGross, liquidityNet, feeGrowthOutside0X128, feeGrowthOutside1X128,
    // tickCumulativeOutside, secondsPerLiquidityOutsideX128, secondsOutside, initialized)
    function ticks(int24) external view returns (
        uint128 liquidityGross,
        int128 liquidityNet,
        uint256 feeGrowthOutside0X128,
        uint256 feeGrowthOutside1X128,
        int56 tickCumulativeOutside,
        uint160 secondsPerLiquidityOutsideX128,
        uint32 secondsOutside,
        bool initialized
    );
}
type PoolId is bytes32;


interface StateView {


    function getSlot0(PoolId poolId)
    external
    view
    returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFee, uint24 lpFee);

    function getTickLiquidity(PoolId poolId, int24 tick)
        external
        view
        returns (uint128 liquidityGross, int128 liquidityNet);
}

library TickMath {
    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
        require(absTick <= uint256(uint24(MAX_TICK)), 'T');

        uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
        if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

        if (tick > 0) ratio = type(uint256).max / ratio;

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
    }
}

/// @notice Batch reader for Uniswap V3 ticks and computed prices.
/// The function `readTicksAroundCurrent` returns arrays describing ticks in the window.
///
/// Usage:
/// - call readTicksAroundCurrent(pool, N) to get `2N+1` items centered (aligned to tickSpacing).
contract Batcher {

    address immutable stateLibraryAddress = 0x7fFE42C4a5DEeA5b0feC41C94C136Cf115597227;

    struct TickInfov3 {
        int24 tick;
        uint160 sqrtPriceX96;
        uint256 priceX96; // price = (sqrtPriceX96^2) >> 96 (token1/token0 as Q96)
        uint128 liquidityGross;
        int128 liquidityNet;
        bool initialized;
    }


    struct TickInfov4 {
        int24 tick;
        uint160 sqrtPriceX96;
        uint256 priceX96; // price = (sqrtPriceX96^2) >> 96 (token1/token0 as Q96)
        uint128 liquidityGross;
        int128 liquidityNet;
    }

    /// @notice Read tick data for ticks in the range [-N, +N] (in tick steps of pool.tickSpacing()).
    /// @param poolAddress address of the Uniswap V3 pool
    /// @param tickRadius how many ticks on each side (total items returned = 2*N + 1)
    /// @return infos array of TickInfo structs (length = 2*N+1)
    function uniV3Ticks(address poolAddress, uint16 tickRadius) external view returns (TickInfov3[] memory infos) {
        IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);

        // get current tick from slot0
        (, int24 currentTick, , , , , ) = pool.slot0();

        // tick spacing (positive)
        int24 tickSpacing = pool.tickSpacing();
        require(tickSpacing > 0, "Invalid tickSpacing");

        // Align currentTick down to the nearest multiple of tickSpacing
        int24 alignedCurrent = _floorToTickSpacing(currentTick, tickSpacing);

        // prepare array
        uint256 total = uint256(2) * tickRadius + 1;
        infos = new TickInfov3[](total);

        // iterate from -N to +N
        for (int256 i = -int256(int16(tickRadius)); i <= int256(int16(tickRadius)); ++i) {
            uint256 idx = uint256(i + int256(int16(tickRadius))); // array index 0..2N
            int24 targetTick = int24(int256(alignedCurrent) + int256(tickSpacing) * i);

            // compute sqrtPrice for this tick (may revert if tick out of math bounds)
            uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(targetTick);

            // price in Q96 fixed point = (sqrtPriceX96 * sqrtPriceX96) >> 96
            uint256 priceX96 = (uint256(sqrtPriceX96) * uint256(sqrtPriceX96)) >> 96;

            // read tick storage
            (uint128 liquidityGross, int128 liquidityNet, , , , , , bool initialized) = pool.ticks(targetTick);

            infos[idx] = TickInfov3({
                tick: targetTick,
                sqrtPriceX96: sqrtPriceX96,
                priceX96: priceX96,
                liquidityGross: liquidityGross,
                liquidityNet: liquidityNet,
                initialized: initialized
            });
        }
        return infos;
    }

    function uniV4Ticks(PoolId poolId, uint16 tickRadius, int24 tickSpacing) external view returns (TickInfov4[] memory infos) {
        StateView stateLibrary = StateView(stateLibraryAddress);

        // get current tick from slot0
        (, int24 currentTick, , ) = stateLibrary.getSlot0(poolId);

        // tick spacing (positive)
        require(tickSpacing > 0, "Invalid tickSpacing");

        // Align currentTick down to the nearest multiple of tickSpacing
        int24 alignedCurrent = _floorToTickSpacing(currentTick, tickSpacing);

        // prepare array
        uint256 total = uint256(2) * tickRadius + 1;
        infos = new TickInfov4[](total);

        // iterate from -N to +N
        for (int256 i = -int256(int16(tickRadius)); i <= int256(int16(tickRadius)); ++i) {
            uint256 idx = uint256(i + int256(int16(tickRadius))); // array index 0..2N
            int24 targetTick = int24(int256(alignedCurrent) + int256(tickSpacing) * i);

            // compute sqrtPrice for this tick (may revert if tick out of math bounds)
            uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(targetTick);

            // price in Q96 fixed point = (sqrtPriceX96 * sqrtPriceX96) >> 96
            uint256 priceX96 = (uint256(sqrtPriceX96) * uint256(sqrtPriceX96)) >> 96;

            // read tick storage
            (uint128 liquidityGross, int128 liquidityNet) = stateLibrary.getTickLiquidity(poolId, targetTick);

            infos[idx] = TickInfov4({
                tick: targetTick,
                sqrtPriceX96: sqrtPriceX96,
                priceX96: priceX96,
                liquidityGross: liquidityGross,
                liquidityNet: liquidityNet
            });
        }
        return infos;
    }

    /// @dev floor currentTick to nearest multiple of tickSpacing (towards negative infinity)
    function _floorToTickSpacing(int24 tick, int24 tickSpacing) internal pure returns (int24) {
        // integer division toward negative infinity
        int256 ts = int256(tickSpacing);
        int256 t = int256(tick);
        if (t >= 0) {
            int256 down = (t / ts) * ts;
            return int24(down);
        } else {
            // for negative numbers, if not evenly divisible we need to subtract one spacing to floor
            int256 div = t / ts;
            if (t % ts != 0) div -= 1;
            int256 down = div * ts;
            return int24(down);
        }
    }
}
"
    }
  },
  "settings": {
    "remappings": [
      "@solmate/=lib/solmate/src/",
      "@permit2/=lib/permit2/src/",
      "@forge-std/=lib/forge-std/src/",
      "@forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
      "@uniswapv4/=lib/v4-core/src/",
      "@eulerswap/=lib/euler-swap/src/",
      "forge-std/src/=lib/forge-std/src/",
      "solmate/=lib/solmate/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 2000,
      "details": {
        "constantOptimizer": true,
        "yul": true
      }
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "none",
      "appendCBOR": false
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": true
  }
}}

Tags:
DeFi, Swap, Liquidity, Factory|addr:0xf4f73ffd5cb64bf139bb23bf5026baf25b3c8bc6|verified:true|block:23670316|tx:0x38218785a7154cc2000d4c680478c3eed98092137acfb9c952dafdade2eff0dc|first_check:1761590386

Submitted on: 2025-10-27 19:39:46

Comments

Log in to comment.

No comments yet.