//SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** @dev This contract is not meant to be deployed. Instead, use a static call with the deployment bytecode as payload. */ contract GetUniswapV3TickDataBatchRequest { int24 internal constant MIN_TICK = -887272; int24 internal constant MAX_TICK = -MIN_TICK; struct TickData { bool initialized; int24 tick; int128 liquidityNet; } constructor( address pool, bool zeroForOne, int24 currentTick, uint16 numTicks, int24 tickSpacing ) { TickData[] memory tickData = new TickData[](numTicks); //Instantiate current word position to keep track of the word count uint256 counter = 0; while (counter < numTicks) { ( int24 nextTick, bool initialized ) = nextInitializedTickWithinOneWord( pool, currentTick, tickSpacing, zeroForOne ); //Make sure the next tick is initialized (, int128 liquidityNet, , , , , , ) = IUniswapV3PoolState(pool) .ticks(nextTick); //Make sure not to overshoot the max/min tick //If we do, break the loop, and set the last initialized tick to the max/min tick= if (nextTick < MIN_TICK) { nextTick = MIN_TICK; tickData[counter].initialized = initialized; tickData[counter].tick = nextTick; tickData[counter].liquidityNet = liquidityNet; break; } else if (nextTick > MAX_TICK) { nextTick = MIN_TICK; tickData[counter].initialized = initialized; tickData[counter].tick = nextTick; tickData[counter].liquidityNet = liquidityNet; break; } else { tickData[counter].initialized = initialized; tickData[counter].tick = nextTick; tickData[counter].liquidityNet = liquidityNet; } counter++; //Set the current tick to the next tick and repeat currentTick = zeroForOne ? nextTick - 1 : nextTick; } // ensure abi encoding, not needed here but increase reusability for different return types // note: abi.encode add a first 32 bytes word with the address of the original data bytes memory abiEncodedData = abi.encode(tickData, block.number); assembly { // Return from the start of the data (discarding the original data address) // up to the end of the memory used let dataStart := add(abiEncodedData, 0x20) return(dataStart, sub(msize(), dataStart)) } } function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { unchecked { wordPos = int16(tick >> 8); bitPos = uint8(int8(tick % 256)); } } function nextInitializedTickWithinOneWord( address pool, int24 tick, int24 tickSpacing, bool lte ) internal view returns (int24 next, bool initialized) { unchecked { int24 compressed = tick / tickSpacing; if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity if (lte) { (int16 wordPos, uint8 bitPos) = position(compressed); // all the 1s at or to the right of the current bitPos uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); uint256 masked = IUniswapV3PoolState(pool).tickBitmap(wordPos) & mask; // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word initialized = masked != 0; // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick next = initialized ? (compressed - int24( uint24(bitPos - BitMath.mostSignificantBit(masked)) )) * tickSpacing : (compressed - int24(uint24(bitPos))) * tickSpacing; } else { // start from the word of the next tick, since the current tick state doesn't matter (int16 wordPos, uint8 bitPos) = position(compressed + 1); // all the 1s at or to the left of the bitPos uint256 mask = ~((1 << bitPos) - 1); uint256 masked = IUniswapV3PoolState(pool).tickBitmap(wordPos) & mask; // if there are no initialized ticks to the left of the current tick, return leftmost in the word initialized = masked != 0; // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick next = initialized ? (compressed + 1 + int24( uint24(BitMath.leastSignificantBit(masked) - bitPos) )) * tickSpacing : (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing; } } } } /// @title BitMath /// @dev This library provides functionality for computing bit properties of an unsigned integer library BitMath { /// @notice Returns the index of the most significant bit of the number, /// where the least significant bit is at index 0 and the most significant bit is at index 255 /// @dev The function satisfies the property: /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) /// @param x the value for which to compute the most significant bit, must be greater than 0 /// @return r the index of the most significant bit function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); unchecked { if (x >= 0x100000000000000000000000000000000) { x >>= 128; r += 128; } if (x >= 0x10000000000000000) { x >>= 64; r += 64; } if (x >= 0x100000000) { x >>= 32; r += 32; } if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 0x4) { x >>= 2; r += 2; } if (x >= 0x2) r += 1; } } /// @notice Returns the index of the least significant bit of the number, /// where the least significant bit is at index 0 and the most significant bit is at index 255 /// @dev The function satisfies the property: /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) /// @param x the value for which to compute the least significant bit, must be greater than 0 /// @return r the index of the least significant bit function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); unchecked { r = 255; if (x & type(uint128).max > 0) { r -= 128; } else { x >>= 128; } if (x & type(uint64).max > 0) { r -= 64; } else { x >>= 64; } if (x & type(uint32).max > 0) { r -= 32; } else { x >>= 32; } if (x & type(uint16).max > 0) { r -= 16; } else { x >>= 16; } if (x & type(uint8).max > 0) { r -= 8; } else { x >>= 8; } if (x & 0xf > 0) { r -= 4; } else { x >>= 4; } if (x & 0x3 > 0) { r -= 2; } else { x >>= 2; } if (x & 0x1 > 0) r -= 1; } } } /// @title Pool state that can change /// @notice These methods compose the pool's state, and can change with any frequency including multiple times /// per transaction interface IUniswapV3PoolState { function ticks(int24 tick) external view returns ( uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128, int56 tickCumulativeOutside, uint160 secondsPerLiquidityOutsideX128, uint32 secondsOutside, bool initialized ); /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information function tickBitmap(int16 wordPosition) external view returns (uint256); }