univ3diff

Created Diff never expires
0 हटाए गए
लाइनें
कुल
हटाया गया
शब्द
कुल
हटाया गया
इस सुविधा का उपयोग जारी रखने के लिए, अपग्रेड करें
Diffchecker logo
Diffchecker Pro
638 लाइनें
0 जोड़े गए
लाइनें
कुल
जोड़ा गया
शब्द
कुल
जोड़ा गया
इस सुविधा का उपयोग जारी रखने के लिए, अपग्रेड करें
Diffchecker logo
Diffchecker Pro
638 लाइनें
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
using LowGasSafeMath for uint256;
using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
using LowGasSafeMath for int256;
using SafeCast for uint256;
using SafeCast for uint256;
using SafeCast for int256;
using SafeCast for int256;
using Tick for mapping(int24 => Tick.Info);
using Tick for mapping(int24 => Tick.Info);
using TickBitmap for mapping(int16 => uint256);
using TickBitmap for mapping(int16 => uint256);
using Position for mapping(bytes32 => Position.Info);
using Position for mapping(bytes32 => Position.Info);
using Position for Position.Info;
using Position for Position.Info;
using Oracle for Oracle.Observation[65535];
using Oracle for Oracle.Observation[65535];


/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override factory;
address public immutable override factory;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token0;
address public immutable override token0;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token1;
address public immutable override token1;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
uint24 public immutable override fee;
uint24 public immutable override fee;


/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
int24 public immutable override tickSpacing;
int24 public immutable override tickSpacing;


/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
uint128 public immutable override maxLiquidityPerTick;
uint128 public immutable override maxLiquidityPerTick;


struct Slot0 {
struct Slot0 {
// the current price
// the current price
uint160 sqrtPriceX96;
uint160 sqrtPriceX96;
// the current tick
// the current tick
int24 tick;
int24 tick;
// the most-recently updated index of the observations array
// the most-recently updated index of the observations array
uint16 observationIndex;
uint16 observationIndex;
// the current maximum number of observations that are being stored
// the current maximum number of observations that are being stored
uint16 observationCardinality;
uint16 observationCardinality;
// the next maximum number of observations to store, triggered in observations.write
// the next maximum number of observations to store, triggered in observations.write
uint16 observationCardinalityNext;
uint16 observationCardinalityNext;
// the current protocol fee as a percentage of the swap fee taken on withdrawal
// the current protocol fee as a percentage of the swap fee taken on withdrawal
// represented as an integer denominator (1/x)%
// represented as an integer denominator (1/x)%
uint8 feeProtocol;
uint8 feeProtocol;
// whether the pool is locked
// whether the pool is locked
bool unlocked;
bool unlocked;
}
}
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
Slot0 public override slot0;
Slot0 public override slot0;


/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal0X128;
uint256 public override feeGrowthGlobal0X128;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal1X128;
uint256 public override feeGrowthGlobal1X128;


// accumulated protocol fees in token0/token1 units
// accumulated protocol fees in token0/token1 units
struct ProtocolFees {
struct ProtocolFees {
uint128 token0;
uint128 token0;
uint128 token1;
uint128 token1;
}
}
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
ProtocolFees public override protocolFees;
ProtocolFees public override protocolFees;


/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint128 public override liquidity;
uint128 public override liquidity;


/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(int24 => Tick.Info) public override ticks;
mapping(int24 => Tick.Info) public override ticks;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(int16 => uint256) public override tickBitmap;
mapping(int16 => uint256) public override tickBitmap;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(bytes32 => Position.Info) public override positions;
mapping(bytes32 => Position.Info) public override positions;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
Oracle.Observation[65535] public override observations;
Oracle.Observation[65535] public override observations;


/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
modifier lock() {
modifier lock() {
require(slot0.unlocked, 'LOK');
require(slot0.unlocked, 'LOK');
slot0.unlocked = false;
slot0.unlocked = false;
_;
_;
slot0.unlocked = true;
slot0.unlocked = true;
}
}


/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
modifier onlyFactoryOwner() {
modifier onlyFactoryOwner() {
require(msg.sender == IUniswapV3Factory(factory).owner());
require(msg.sender == IUniswapV3Factory(factory).owner());
_;
_;
}
}


constructor() {
constructor() {
int24 _tickSpacing;
int24 _tickSpacing;
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
tickSpacing = _tickSpacing;
tickSpacing = _tickSpacing;


maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
}
}


/// @dev Common checks for valid tick inputs.
/// @dev Common checks for valid tick inputs.
function checkTicks(int24 tickLower, int24 tickUpper) private pure {
function checkTicks(int24 tickLower, int24 tickUpper) private pure {
require(tickLower < tickUpper, 'TLU');
require(tickLower < tickUpper, 'TLU');
require(tickLower >= TickMath.MIN_TICK, 'TLM');
require(tickLower >= TickMath.MIN_TICK, 'TLM');
require(tickUpper <= TickMath.MAX_TICK, 'TUM');
require(tickUpper <= TickMath.MAX_TICK, 'TUM');
}
}


/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
function _blockTimestamp() internal view virtual returns (uint32) {
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp); // truncation is desired
return uint32(block.timestamp); // truncation is desired
}
}


/// @dev Get the pool's balance of token0
/// @dev Get the pool's balance of token0
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
/// check
function balance0() private view returns (uint256) {
function balance0() private view returns (uint256) {
(bool success, bytes memory data) =
(bool success, bytes memory data) =
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
require(success && data.length >= 32);
return abi.decode(data, (uint256));
return abi.decode(data, (uint256));
}
}


/// @dev Get the pool's balance of token1
/// @dev Get the pool's balance of token1
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
/// check
function balance1() private view returns (uint256) {
function balance1() private view returns (uint256) {
(bool success, bytes memory data) =
(bool success, bytes memory data) =
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
require(success && data.length >= 32);
return abi.decode(data, (uint256));
return abi.decode(data, (uint256));
}
}


/// @inheritdoc IUniswapV3PoolDerivedState
/// @inheritdoc IUniswapV3PoolDerivedState
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
external
view
view
override
override
noDelegateCall
noDelegateCall
returns (
returns (
int56 tickCumulativeInside,
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
uint32 secondsInside
)
)
{
{
checkTicks(tickLower, tickUpper);
checkTicks(tickLower, tickUpper);


int56 tickCumulativeLower;
int56 tickCumulativeLower;
int56 tickCumulativeUpper;
int56 tickCumulativeUpper;
uint160 secondsPerLiquidityOutsideLowerX128;
uint160 secondsPerLiquidityOutsideLowerX128;
uint160 secondsPerLiquidityOutsideUpperX128;
uint160 secondsPerLiquidityOutsideUpperX128;
uint32 secondsOutsideLower;
uint32 secondsOutsideLower;
uint32 secondsOutsideUpper;
uint32 secondsOutsideUpper;


{
{
Tick.Info storage lower = ticks[tickLower];
Tick.Info storage lower = ticks[tickLower];
Tick.Info storage upper = ticks[tickUpper];
Tick.Info storage upper = ticks[tickUpper];
bool initializedLower;
bool initializedLower;
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
lower.tickCumulativeOutside,
lower.tickCumulativeOutside,
lower.secondsPerLiquidityOutsideX128,
lower.secondsPerLiquidityOutsideX128,
lower.secondsOutside,
lower.secondsOutside,
lower.initialized
lower.initialized
);
);
require(initializedLower);
require(initializedLower);


bool initializedUpper;
bool initializedUpper;
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
upper.tickCumulativeOutside,
upper.tickCumulativeOutside,
upper.secondsPerLiquidityOutsideX128,
upper.secondsPerLiquidityOutsideX128,
upper.secondsOutside,
upper.secondsOutside,
upper.initialized
upper.initialized
);
);
require(initializedUpper);
require(initializedUpper);
}
}


Slot0 memory _slot0 = slot0;
Slot0 memory _slot0 = slot0;


if (_slot0.tick < tickLower) {
if (_slot0.tick < tickLower) {
return (
return (
tickCumulativeLower - tickCumulativeUpper,
tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
secondsOutsideLower - secondsOutsideUpper
secondsOutsideLower - secondsOutsideUpper
);
);
} else if (_slot0.tick < tickUpper) {
} else if (_slot0.tick < tickUpper) {
uint32 time = _blockTimestamp();
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
observations.observeSingle(
time,
time,
0,
0,
_slot0.tick,
_slot0.tick,
_slot0.observationIndex,
_slot0.observationIndex,
liquidity,
liquidity,
_slot0.observationCardinality
_slot0.observationCardinality
);
);
return (
return (
tickCumulative - tickCumulativeLower - tickCumulativeUpper,
tickCumulative - tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityCumulativeX128 -
secondsPerLiquidityCumulativeX128 -
secondsPerLiquidityOutsideLowerX128 -
secondsPerLiquidityOutsideLowerX128 -
secondsPerLiquidityOutsideUpperX128,
secondsPerLiquidityOutsideUpperX128,
time - secondsOutsideLower - secondsOutsideUpper
time - secondsOutsideLower - secondsOutsideUpper
);
);
} else {
} else {
return (
return (
tickCumulativeUpper - tickCumulativeLower,
tickCumulativeUpper - tickCumulativeLower,
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
secondsOutsideUpper - secondsOutsideLower
secondsOutsideUpper - secondsOutsideLower
);
);
}
}
}
}


/// @inheritdoc IUniswapV3PoolDerivedState
/// @inheritdoc IUniswapV3PoolDerivedState
function observe(uint32[] calldata secondsAgos)
function observe(uint32[] calldata secondsAgos)
external
external
view
view
override
override
noDelegateCall
noDelegateCall
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
{
{
return
return
observations.observe(
observations.observe(
_blockTimestamp(),
_blockTimestamp(),
secondsAgos,
secondsAgos,
slot0.tick,
slot0.tick,
slot0.observationIndex,
slot0.observationIndex,
liquidity,
liquidity,
slot0.observationCardinality
slot0.observationCardinality
);
);
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
external
external
override
override
lock
lock
noDelegateCall
noDelegateCall
{
{
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
uint16 observationCardinalityNextNew =
uint16 observationCardinalityNextNew =
observations.grow(observationCardinalityNextOld, observationCardinalityNext);
observations.grow(observationCardinalityNextOld, observationCardinalityNext);
slot0.observationCardinalityNext = observationCardinalityNextNew;
slot0.observationCardinalityNext = observationCardinalityNextNew;
if (observationCardinalityNextOld != observationCardinalityNextNew)
if (observationCardinalityNextOld != observationCardinalityNextNew)
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev not locked because it initializes unlocked
/// @dev not locked because it initializes unlocked
function initialize(uint160 sqrtPriceX96) external override {
function initialize(uint160 sqrtPriceX96) external override {
require(slot0.sqrtPriceX96 == 0, 'AI');
require(slot0.sqrtPriceX96 == 0, 'AI');


int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);


(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());


slot0 = Slot0({
slot0 = Slot0({
sqrtPriceX96: sqrtPriceX96,
sqrtPriceX96: sqrtPriceX96,
tick: tick,
tick: tick,
observationIndex: 0,
observationIndex: 0,
observationCardinality: cardinality,
observationCardinality: cardinality,
observationCardinalityNext: cardinalityNext,
observationCardinalityNext: cardinalityNext,
feeProtocol: 0,
feeProtocol: 0,
unlocked: true
unlocked: true
});
});


emit Initialize(sqrtPriceX96, tick);
emit Initialize(sqrtPriceX96, tick);
}
}


struct ModifyPositionParams {
struct ModifyPositionParams {
// the address that owns the position
// the address that owns the position
address owner;
address owner;
// the lower and upper tick of the position
// the lower and upper tick of the position
int24 tickLower;
int24 tickLower;
int24 tickUpper;
int24 tickUpper;
// any change in liquidity
// any change in liquidity
int128 liquidityDelta;
int128 liquidityDelta;
}
}


/// @dev Effect some changes to a position
/// @dev Effect some changes to a position
/// @param params the position details and the change to the position's liquidity to effect
/// @param params the position details and the change to the position's liquidity to effect
/// @return position a storage pointer referencing the position with the given owner and tick range
/// @return position a storage pointer referencing the position with the given owner and tick range
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
function _modifyPosition(ModifyPositionParams memory params)
function _modifyPosition(ModifyPositionParams memory params)
private
private
noDelegateCall
noDelegateCall
returns (
returns (
Position.Info storage position,
Position.Info storage position,
int256 amount0,
int256 amount0,
int256 amount1
int256 amount1
)
)
{
{
checkTicks(params.tickLower, params.tickUpper);
checkTicks(params.tickLower, params.tickUpper);


Slot0 memory _slot0 = slot0; // SLOAD for gas optimization
Slot0 memory _slot0 = slot0; // SLOAD for gas optimization


position = _updatePosition(
position = _updatePosition(
params.owner,
params.owner,
params.tickLower,
params.tickLower,
params.tickUpper,
params.tickUpper,
params.liquidityDelta,
params.liquidityDelta,
_slot0.tick
_slot0.tick
);
);


if (params.liquidityDelta != 0) {
if (params.liquidityDelta != 0) {
if (_slot0.tick < params.tickLower) {
if (_slot0.tick < params.tickLower) {
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
amount0 = SqrtPriceMath.getAmount0Delta(
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
} else if (_slot0.tick < params.tickUpper) {
} else if (_slot0.tick < params.tickUpper) {
// current tick is inside the passed range
// current tick is inside the passed range
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization


// write an oracle entry
// write an oracle entry
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
_slot0.observationIndex,
_slot0.observationIndex,
_blockTimestamp(),
_blockTimestamp(),
_slot0.tick,
_slot0.tick,
liquidityBefore,
liquidityBefore,
_slot0.observationCardinality,
_slot0.observationCardinality,
_slot0.observationCardinalityNext
_slot0.observationCardinalityNext
);
);


amount0 = SqrtPriceMath.getAmount0Delta(
amount0 = SqrtPriceMath.getAmount0Delta(
_slot0.sqrtPriceX96,
_slot0.sqrtPriceX96,
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
amount1 = SqrtPriceMath.getAmount1Delta(
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
_slot0.sqrtPriceX96,
_slot0.sqrtPriceX96,
params.liquidityDelta
params.liquidityDelta
);
);


liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
} else {
} else {
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
amount1 = SqrtPriceMath.getAmount1Delta(
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
}
}
}
}
}
}


/// @dev Gets and updates a position with the given liquidity delta
/// @dev Gets and updates a position with the given liquidity delta
/// @param owner the owner of the position
/// @param owner the owner of the position
/// @param tickLower the lower tick of the position's tick range
/// @param tickLower the lower tick of the position's tick range
/// @param tickUpper the upper tick of the position's tick range
/// @param tickUpper the upper tick of the position's tick range
/// @param tick the current tick, passed to avoid sloads
/// @param tick the current tick, passed to avoid sloads
function _updatePosition(
function _updatePosition(
address owner,
address owner,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
int128 liquidityDelta,
int128 liquidityDelta,
int24 tick
int24 tick
) private returns (Position.Info storage position) {
) private returns (Position.Info storage position) {
position = positions.get(owner, tickLower, tickUpper);
position = positions.get(owner, tickLower, tickUpper);


uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization


// if we need to update the ticks, do it
// if we need to update the ticks, do it
bool flippedLower;
bool flippedLower;
bool flippedUpper;
bool flippedUpper;
if (liquidityDelta != 0) {
if (liquidityDelta != 0) {
uint32 time = _blockTimestamp();
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
observations.observeSingle(
time,
time,
0,
0,
slot0.tick,
slot0.tick,
slot0.observationIndex,
slot0.observationIndex,
liquidity,
liquidity,
slot0.observationCardinality
slot0.observationCardinality
);
);


flippedLower = ticks.update(
flippedLower = ticks.update(
tickLower,
tickLower,
tick,
tick,
liquidityDelta,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
tickCumulative,
time,
time,
false,
false,
maxLiquidityPerTick
maxLiquidityPerTick
);
);
flippedUpper = ticks.update(
flippedUpper = ticks.update(
tickUpper,
tickUpper,
tick,
tick,
liquidityDelta,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
tickCumulative,
time,
time,
true,
true,
maxLiquidityPerTick
maxLiquidityPerTick
);
);


if (flippedLower) {
if (flippedLower) {
tickBitmap.flipTick(tickLower, tickSpacing);
tickBitmap.flipTick(tickLower, tickSpacing);
}
}
if (flippedUpper) {
if (flippedUpper) {
tickBitmap.flipTick(tickUpper, tickSpacing);
tickBitmap.flipTick(tickUpper, tickSpacing);
}
}
}
}


(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);


position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);


// clear any tick data that is no longer needed
// clear any tick data that is no longer needed
if (liquidityDelta < 0) {
if (liquidityDelta < 0) {
if (flippedLower) {
if (flippedLower) {
ticks.clear(tickLower);
ticks.clear(tickLower);
}
}
if (flippedUpper) {
if (flippedUpper) {
ticks.clear(tickUpper);
ticks.clear(tickUpper);
}
}
}
}
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function mint(
function mint(
address recipient,
address recipient,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount,
uint128 amount,
bytes calldata data
bytes calldata data
) external override lock returns (uint256 amount0, uint256 amount1) {
) external override lock returns (uint256 amount0, uint256 amount1) {
require(amount > 0);
require(amount > 0);
(, int256 amount0Int, int256 amount1Int) =
(, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
_modifyPosition(
ModifyPositionParams({
ModifyPositionParams({
owner: recipient,
owner: recipient,
tickLower: tickLower,
tickLower: tickLower,
tickUpper: tickUpper,
tickUpper: tickUpper,
liquidityDelta: int256(amount).toInt128()
liquidityDelta: int256(amount).toInt128()
})
})
);
);


amount0 = uint256(amount0Int);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
amount1 = uint256(amount1Int);


uint256 balance0Before;
uint256 balance0Before;
uint256 balance1Before;
uint256 balance1Before;
if (amount0 > 0) balance0Before = balance0();
if (amount0 > 0) balance0Before = balance0();
if (amount1 > 0) balance1Before = balance1();
if (amount1 > 0) balance1Before = balance1();
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');


emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function collect(
function collect(
address recipient,
address recipient,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount0Requested,
uint128 amount1Requested
uint128 amount1Requested
) external override lock returns (uint128 amount0, uint128 amount1) {
) external override lock returns (uint128 amount0, uint128 amount1) {
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);


amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;


if (amount0 > 0) {
if (amount0 > 0) {
position.tokensOwed0 -= amount0;
position.tokensOwed0 -= amount0;
TransferHelper.safeTransfer(token0, recipient, amount0);
TransferHelper.safeTransfer(token0, recipient, amount0);
}
}
if (amount1 > 0) {
if (amount1 > 0) {
position.tokensOwed1 -= amount1;
position.tokensOwed1 -= amount1;
TransferHelper.safeTransfer(token1, recipient, amount1);
TransferHelper.safeTransfer(token1, recipient, amount1);
}
}


emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function burn(
function burn(
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount
uint128 amount
) external override lock returns (uint256 amount0, uint256 amount1) {
) external override lock returns (uint256 amount0, uint256 amount1) {
(Position.Info storage position, int256 amount0Int, int256 amount1Int) =
(Position.Info storage position, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
_modifyPosition(
ModifyPositionParams({
ModifyPositionParams({
owner: msg.sender,
owner: msg.sender,
tickLower: tickLower,
tickLower: tickLower,
tickUpper: tickUpper,
tickUpper: tickUpper,
liquidityDelta: -int256(amount).toInt128()
liquidityDelta: -int256(amount).toInt128()
})
})
);
);


amount0 = uint256(-amount0Int);
amount0 = uint256(-amount0Int);
amount1 = uint256(-amount1Int);
amount1 = uint256(-amount1Int);


if (amount0 > 0 || amount1 > 0) {
if (amount0 > 0 || amount1 > 0) {
(position.tokensOwed0, position.tokensOwed1) = (
(position.tokensOwed0, position.tokensOwed1) = (
position.tokensOwed0 + uint128(amount0),
position.tokensOwed0 + uint128(amount0),
position.tokensOwed1 + uint128(amount1)
position.tokensOwed1 + uint128(amount1)
);
);
}
}


emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
}
}


struct SwapCache {
struct SwapCache {
// the protocol fee for the input token
// the protocol fee for the input token
uint8 feeProtocol;
uint8 feeProtocol;
// liquidity at the beginning of the swap
// liquidity at the beginning of the swap
uint128 liquidityStart;
uint128 liquidityStart;
// the timestamp of the current block
// the timestamp of the current block
uint32 blockTimestamp;
uint32 blockTimestamp;
// the current value of the tick accumulator, computed only if we cross an initialized tick
// the current value of the tick accumulator, computed only if we cross an initialized tick
int56 tickCumulative;
int56 tickCumulative;
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
uint160 secondsPerLiquidityCumulativeX128;
uint160 secondsPerLiquidityCumulativeX128;
// whether we've computed and cached the above two accumulators
// whether we've computed and cached the above two accumulators
bool computedLatestObservation;
bool computedLatestObservation;
}
}


// the top level state of the swap, the results of which are recorded in storage at the end
// the top level state of the swap, the results of which are recorded in storage at the end
struct SwapState {
struct SwapState {
// the amount remaining to be swapped in/out of the input/output asset
// the amount remaining to be swapped in/out of the input/output asset
int256 amountSpecifiedRemaining;
int256 amountSpecifiedRemaining;
// the amount already swapped out/in of the output/input asset
// the amount already swapped out/in of the output/input asset
int256 amountCalculated;
int256 amountCalculated;
// current sqrt(price)
// current sqrt(price)
uint160 sqrtPriceX96;
uint160 sqrtPriceX96;
// the tick associated with the current price
// the tick associated with the current price
int24 tick;
int24 tick;
// the global fee growth of the input token
// the global fee growth of the input token
uint256 feeGrowthGlobalX128;
uint256 feeGrowthGlobalX128;
// amount of input token paid as protocol fee
// amount of input token paid as protocol fee
uint128 protocolFee;
uint128 protocolFee;
// the current liquidity in range
// the current liquidity in range
uint128 liquidity;
uint128 liquidity;
}
}


struct StepComputations {
struct StepComputations {
// the price at the beginning of the step
// the price at the beginning of the step
uint160 sqrtPriceStartX96;
uint160 sqrtPriceStartX96;
// the next tick to swap to from the current tick in the swap direction
// the next tick to swap to from the current tick in the swap direction
int24 tickNext;
int24 tickNext;
// whether tickNext is initialized or not
// whether tickNext is initialized or not
bool initialized;
bool initialized;
// sqrt(price) for the next tick (1/0)
// sqrt(price) for the next tick (1/0)
uint160 sqrtPriceNextX96;
uint160 sqrtPriceNextX96;
// how much is being swapped in in this step
// how much is being swapped in in this step
uint256 amountIn;
uint256 amountIn;
// how much is being swapped out
// how much is being swapped out
uint256 amountOut;
uint256 amountOut;
// how much fee is being paid in
// how much fee is being paid in
uint256 feeAmount;
uint256 feeAmount;
}
}


/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function swap(
function swap(
address recipient,
address recipient,
bool zeroForOne,
bool zeroForOne,
int256 amountSpecified,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
uint160 sqrtPriceLimitX96,
bytes calldata data
bytes calldata data
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
require(amountSpecified != 0, 'AS');
require(amountSpecified != 0, 'AS');


Slot0 memory slot0Start = slot0;
Slot0 memory slot0Start = slot0;


require(slot0Start.unlocked, 'LOK');
require(slot0Start.unlocked, 'LOK');
require(
require(
zeroForOne
zeroForOne
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
'SPL'
'SPL'
);
);


slot0.unlocked = false;
slot0.unlocked = false;


SwapCache memory cache =
SwapCache memory cache =
SwapCache({
SwapCache({
liquidityStart: liquidity,
liquidityStart: liquidity,
blockTimestamp: _blockTimestamp(),
blockTimestamp: _blockTimestamp(),
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
secondsPerLiquidityCumulativeX128: 0,
secondsPerLiquidityCumulativeX128: 0,
tickCumulative: 0,
tickCumulative: 0,
computedLatestObservation: false
computedLatestObservation: false
});
});


bool exactInput = amountSpecified > 0;
bool exactInput = amountSpecified > 0;


SwapState memory state =
SwapState memory state =
SwapState({
SwapState({
amountSpecifiedRemaining: amountSpecified,
amountSpecifiedRemaining: amountSpecified,
amountCalculated: 0,
amountCalculated: 0,
sqrtPriceX96: slot0Start.sqrtPriceX96,
sqrtPriceX96: slot0Start.sqrtPriceX96,
tick: slot0Start.tick,
tick: slot0Start.tick,
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
protocolFee: 0,
protocolFee: 0,
liquidity: cache.liquidityStart
liquidity: cache.liquidityStart
});
});


// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
StepComputations memory step;
StepComputations memory step;


step.sqrtPriceStartX96 = state.sqrtPriceX96;
step.sqrtPriceStartX96 = state.sqrtPriceX96;


(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
state.tick,
state.tick,
tickSpacing,
tickSpacing,
zeroForOne
zeroForOne
);
);


// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
if (step.tickNext < TickMath.MIN_TICK) {
if (step.tickNext < TickMath.MIN_TICK) {
step.tickNext = TickMath.MIN_TICK;
step.tickNext = TickMath.MIN_TICK;
} else if (step.tickNext > TickMath.MAX_TICK) {
} else if (step.tickNext > TickMath.MAX_TICK) {
step.tickNext = TickMath.MAX_TICK;
step.tickNext = TickMath.MAX_TICK;
}
}


// get the price for the next tick
// get the price for the next tick
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);


// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
state.sqrtPriceX96,
state.sqrtPriceX96,
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
? sqrtPriceLimitX96
? sqrtPriceLimitX96
: step.sqrtPriceNe
: step.sqrtPriceNe