infrastructure-upgrade/lib/v3-periphery/contracts/libraries/PoolTicksCounter.sol
T-Hax 735546619e
init
Signed-off-by: T-Hax <>
2023-04-08 18:46:18 +00:00

97 lines
4.1 KiB
Solidity

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
library PoolTicksCounter {
/// @dev This function counts the number of initialized ticks that would incur a gas cost between tickBefore and tickAfter.
/// When tickBefore and/or tickAfter themselves are initialized, the logic over whether we should count them depends on the
/// direction of the swap. If we are swapping upwards (tickAfter > tickBefore) we don't want to count tickBefore but we do
/// want to count tickAfter. The opposite is true if we are swapping downwards.
function countInitializedTicksCrossed(
IUniswapV3Pool self,
int24 tickBefore,
int24 tickAfter
) internal view returns (uint32 initializedTicksCrossed) {
int16 wordPosLower;
int16 wordPosHigher;
uint8 bitPosLower;
uint8 bitPosHigher;
bool tickBeforeInitialized;
bool tickAfterInitialized;
{
// Get the key and offset in the tick bitmap of the active tick before and after the swap.
int16 wordPos = int16((tickBefore / self.tickSpacing()) >> 8);
uint8 bitPos = uint8((tickBefore / self.tickSpacing()) % 256);
int16 wordPosAfter = int16((tickAfter / self.tickSpacing()) >> 8);
uint8 bitPosAfter = uint8((tickAfter / self.tickSpacing()) % 256);
// In the case where tickAfter is initialized, we only want to count it if we are swapping downwards.
// If the initializable tick after the swap is initialized, our original tickAfter is a
// multiple of tick spacing, and we are swapping downwards we know that tickAfter is initialized
// and we shouldn't count it.
tickAfterInitialized =
((self.tickBitmap(wordPosAfter) & (1 << bitPosAfter)) > 0) &&
((tickAfter % self.tickSpacing()) == 0) &&
(tickBefore > tickAfter);
// In the case where tickBefore is initialized, we only want to count it if we are swapping upwards.
// Use the same logic as above to decide whether we should count tickBefore or not.
tickBeforeInitialized =
((self.tickBitmap(wordPos) & (1 << bitPos)) > 0) &&
((tickBefore % self.tickSpacing()) == 0) &&
(tickBefore < tickAfter);
if (wordPos < wordPosAfter || (wordPos == wordPosAfter && bitPos <= bitPosAfter)) {
wordPosLower = wordPos;
bitPosLower = bitPos;
wordPosHigher = wordPosAfter;
bitPosHigher = bitPosAfter;
} else {
wordPosLower = wordPosAfter;
bitPosLower = bitPosAfter;
wordPosHigher = wordPos;
bitPosHigher = bitPos;
}
}
// Count the number of initialized ticks crossed by iterating through the tick bitmap.
// Our first mask should include the lower tick and everything to its left.
uint256 mask = type(uint256).max << bitPosLower;
while (wordPosLower <= wordPosHigher) {
// If we're on the final tick bitmap page, ensure we only count up to our
// ending tick.
if (wordPosLower == wordPosHigher) {
mask = mask & (type(uint256).max >> (255 - bitPosHigher));
}
uint256 masked = self.tickBitmap(wordPosLower) & mask;
initializedTicksCrossed += countOneBits(masked);
wordPosLower++;
// Reset our mask so we consider all bits on the next iteration.
mask = type(uint256).max;
}
if (tickAfterInitialized) {
initializedTicksCrossed -= 1;
}
if (tickBeforeInitialized) {
initializedTicksCrossed -= 1;
}
return initializedTicksCrossed;
}
function countOneBits(uint256 x) private pure returns (uint16) {
uint16 bits = 0;
while (x != 0) {
bits++;
x &= (x - 1);
}
return bits;
}
}