Diff
checker
Texte
Texte
Images
Documents
Excel
Dossiers
Legal
Enterprise
Application de bureau
Prix
Se connecter
Télécharger Diffchecker Desktop
Comparer le texte
Trouver la différence entre deux fichiers texte
Outils
Historique
Éditeur live
Cacher identiques
Sans retour à la ligne
Vue
Divisé
Unifié
Niveau de précision
Intelligent
Mot
Caractère
Coloration syntaxique
Choisir la syntaxe
Ignorer
Transformer le texte
Aller au premier écart
Modifier l'entrée
Diffchecker Desktop
La façon la plus sécurisée d'utiliser Diffchecker. Obtenez l'application Diffchecker Desktop : vos diffs ne quittent jamais votre ordinateur !
Obtenir Desktop
univ3diff
Créé
il y a 3 ans
Le diff n'expire jamais
Effacer
Exporter
Partager
Expliquer
0 suppressions
Lignes
Total
Supprimé
Caractères
Total
Supprimé
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
638 lignes
Copier tout
0 ajouts
Lignes
Total
Ajouté
Caractères
Total
Ajouté
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
638 lignes
Copier tout
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
Différences enregistrées
Texte d'origine
Ouvrir un fichier
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { using LowGasSafeMath for uint256; using LowGasSafeMath for int256; using SafeCast for uint256; using SafeCast for int256; using Tick for mapping(int24 => Tick.Info); using TickBitmap for mapping(int16 => uint256); using Position for mapping(bytes32 => Position.Info); using Position for Position.Info; using Oracle for Oracle.Observation[65535]; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override factory; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override token0; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override token1; /// @inheritdoc IUniswapV3PoolImmutables uint24 public immutable override fee; /// @inheritdoc IUniswapV3PoolImmutables int24 public immutable override tickSpacing; /// @inheritdoc IUniswapV3PoolImmutables uint128 public immutable override maxLiquidityPerTick; struct Slot0 { // the current price uint160 sqrtPriceX96; // the current tick int24 tick; // the most-recently updated index of the observations array uint16 observationIndex; // the current maximum number of observations that are being stored uint16 observationCardinality; // the next maximum number of observations to store, triggered in observations.write uint16 observationCardinalityNext; // the current protocol fee as a percentage of the swap fee taken on withdrawal // represented as an integer denominator (1/x)% uint8 feeProtocol; // whether the pool is locked bool unlocked; } /// @inheritdoc IUniswapV3PoolState Slot0 public override slot0; /// @inheritdoc IUniswapV3PoolState uint256 public override feeGrowthGlobal0X128; /// @inheritdoc IUniswapV3PoolState uint256 public override feeGrowthGlobal1X128; // accumulated protocol fees in token0/token1 units struct ProtocolFees { uint128 token0; uint128 token1; } /// @inheritdoc IUniswapV3PoolState ProtocolFees public override protocolFees; /// @inheritdoc IUniswapV3PoolState uint128 public override liquidity; /// @inheritdoc IUniswapV3PoolState mapping(int24 => Tick.Info) public override ticks; /// @inheritdoc IUniswapV3PoolState mapping(int16 => uint256) public override tickBitmap; /// @inheritdoc IUniswapV3PoolState mapping(bytes32 => Position.Info) public override positions; /// @inheritdoc IUniswapV3PoolState Oracle.Observation[65535] public override observations; /// @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 /// we use balance checks to determine the payment status of interactions such as mint, swap and flash. modifier lock() { require(slot0.unlocked, 'LOK'); slot0.unlocked = false; _; slot0.unlocked = true; } /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner() modifier onlyFactoryOwner() { require(msg.sender == IUniswapV3Factory(factory).owner()); _; } constructor() { int24 _tickSpacing; (factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters(); tickSpacing = _tickSpacing; maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); } /// @dev Common checks for valid tick inputs. function checkTicks(int24 tickLower, int24 tickUpper) private pure { require(tickLower < tickUpper, 'TLU'); require(tickLower >= TickMath.MIN_TICK, 'TLM'); 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. function _blockTimestamp() internal view virtual returns (uint32) { return uint32(block.timestamp); // truncation is desired } /// @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 /// check function balance0() private view returns (uint256) { (bool success, bytes memory data) = token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } /// @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 /// check function balance1() private view returns (uint256) { (bool success, bytes memory data) = token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } /// @inheritdoc IUniswapV3PoolDerivedState function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) external view override noDelegateCall returns ( int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside ) { checkTicks(tickLower, tickUpper); int56 tickCumulativeLower; int56 tickCumulativeUpper; uint160 secondsPerLiquidityOutsideLowerX128; uint160 secondsPerLiquidityOutsideUpperX128; uint32 secondsOutsideLower; uint32 secondsOutsideUpper; { Tick.Info storage lower = ticks[tickLower]; Tick.Info storage upper = ticks[tickUpper]; bool initializedLower; (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( lower.tickCumulativeOutside, lower.secondsPerLiquidityOutsideX128, lower.secondsOutside, lower.initialized ); require(initializedLower); bool initializedUpper; (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = ( upper.tickCumulativeOutside, upper.secondsPerLiquidityOutsideX128, upper.secondsOutside, upper.initialized ); require(initializedUpper); } Slot0 memory _slot0 = slot0; if (_slot0.tick < tickLower) { return ( tickCumulativeLower - tickCumulativeUpper, secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, secondsOutsideLower - secondsOutsideUpper ); } else if (_slot0.tick < tickUpper) { uint32 time = _blockTimestamp(); (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( time, 0, _slot0.tick, _slot0.observationIndex, liquidity, _slot0.observationCardinality ); return ( tickCumulative - tickCumulativeLower - tickCumulativeUpper, secondsPerLiquidityCumulativeX128 - secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, time - secondsOutsideLower - secondsOutsideUpper ); } else { return ( tickCumulativeUpper - tickCumulativeLower, secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, secondsOutsideUpper - secondsOutsideLower ); } } /// @inheritdoc IUniswapV3PoolDerivedState function observe(uint32[] calldata secondsAgos) external view override noDelegateCall returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { return observations.observe( _blockTimestamp(), secondsAgos, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality ); } /// @inheritdoc IUniswapV3PoolActions function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external override lock noDelegateCall { uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event uint16 observationCardinalityNextNew = observations.grow(observationCardinalityNextOld, observationCardinalityNext); slot0.observationCardinalityNext = observationCardinalityNextNew; if (observationCardinalityNextOld != observationCardinalityNextNew) emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); } /// @inheritdoc IUniswapV3PoolActions /// @dev not locked because it initializes unlocked function initialize(uint160 sqrtPriceX96) external override { require(slot0.sqrtPriceX96 == 0, 'AI'); int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); slot0 = Slot0({ sqrtPriceX96: sqrtPriceX96, tick: tick, observationIndex: 0, observationCardinality: cardinality, observationCardinalityNext: cardinalityNext, feeProtocol: 0, unlocked: true }); emit Initialize(sqrtPriceX96, tick); } struct ModifyPositionParams { // the address that owns the position address owner; // the lower and upper tick of the position int24 tickLower; int24 tickUpper; // any change in liquidity int128 liquidityDelta; } /// @dev Effect some changes to a position /// @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 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 function _modifyPosition(ModifyPositionParams memory params) private noDelegateCall returns ( Position.Info storage position, int256 amount0, int256 amount1 ) { checkTicks(params.tickLower, params.tickUpper); Slot0 memory _slot0 = slot0; // SLOAD for gas optimization position = _updatePosition( params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, _slot0.tick ); if (params.liquidityDelta != 0) { if (_slot0.tick < params.tickLower) { // 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 amount0 = SqrtPriceMath.getAmount0Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); } else if (_slot0.tick < params.tickUpper) { // current tick is inside the passed range uint128 liquidityBefore = liquidity; // SLOAD for gas optimization // write an oracle entry (slot0.observationIndex, slot0.observationCardinality) = observations.write( _slot0.observationIndex, _blockTimestamp(), _slot0.tick, liquidityBefore, _slot0.observationCardinality, _slot0.observationCardinalityNext ); amount0 = SqrtPriceMath.getAmount0Delta( _slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), _slot0.sqrtPriceX96, params.liquidityDelta ); liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta); } else { // 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 amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); } } } /// @dev Gets and updates a position with the given liquidity delta /// @param owner the owner of the position /// @param tickLower the lower 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 function _updatePosition( address owner, int24 tickLower, int24 tickUpper, int128 liquidityDelta, int24 tick ) private returns (Position.Info storage position) { position = positions.get(owner, tickLower, tickUpper); uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization // if we need to update the ticks, do it bool flippedLower; bool flippedUpper; if (liquidityDelta != 0) { uint32 time = _blockTimestamp(); (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( time, 0, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality ); flippedLower = ticks.update( tickLower, tick, liquidityDelta, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, time, false, maxLiquidityPerTick ); flippedUpper = ticks.update( tickUpper, tick, liquidityDelta, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, time, true, maxLiquidityPerTick ); if (flippedLower) { tickBitmap.flipTick(tickLower, tickSpacing); } if (flippedUpper) { tickBitmap.flipTick(tickUpper, tickSpacing); } } (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128); position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); // clear any tick data that is no longer needed if (liquidityDelta < 0) { if (flippedLower) { ticks.clear(tickLower); } if (flippedUpper) { ticks.clear(tickUpper); } } } /// @inheritdoc IUniswapV3PoolActions /// @dev noDelegateCall is applied indirectly via _modifyPosition function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external override lock returns (uint256 amount0, uint256 amount1) { require(amount > 0); (, int256 amount0Int, int256 amount1Int) = _modifyPosition( ModifyPositionParams({ owner: recipient, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: int256(amount).toInt128() }) ); amount0 = uint256(amount0Int); amount1 = uint256(amount1Int); uint256 balance0Before; uint256 balance1Before; if (amount0 > 0) balance0Before = balance0(); if (amount1 > 0) balance1Before = balance1(); IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0'); if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1'); emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); } /// @inheritdoc IUniswapV3PoolActions function collect( address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested ) 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} Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper); amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested; amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested; if (amount0 > 0) { position.tokensOwed0 -= amount0; TransferHelper.safeTransfer(token0, recipient, amount0); } if (amount1 > 0) { position.tokensOwed1 -= amount1; TransferHelper.safeTransfer(token1, recipient, amount1); } emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); } /// @inheritdoc IUniswapV3PoolActions /// @dev noDelegateCall is applied indirectly via _modifyPosition function burn( int24 tickLower, int24 tickUpper, uint128 amount ) external override lock returns (uint256 amount0, uint256 amount1) { (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( ModifyPositionParams({ owner: msg.sender, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: -int256(amount).toInt128() }) ); amount0 = uint256(-amount0Int); amount1 = uint256(-amount1Int); if (amount0 > 0 || amount1 > 0) { (position.tokensOwed0, position.tokensOwed1) = ( position.tokensOwed0 + uint128(amount0), position.tokensOwed1 + uint128(amount1) ); } emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); } struct SwapCache { // the protocol fee for the input token uint8 feeProtocol; // liquidity at the beginning of the swap uint128 liquidityStart; // the timestamp of the current block uint32 blockTimestamp; // the current value of the tick accumulator, computed only if we cross an initialized tick int56 tickCumulative; // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick uint160 secondsPerLiquidityCumulativeX128; // whether we've computed and cached the above two accumulators bool computedLatestObservation; } // the top level state of the swap, the results of which are recorded in storage at the end struct SwapState { // the amount remaining to be swapped in/out of the input/output asset int256 amountSpecifiedRemaining; // the amount already swapped out/in of the output/input asset int256 amountCalculated; // current sqrt(price) uint160 sqrtPriceX96; // the tick associated with the current price int24 tick; // the global fee growth of the input token uint256 feeGrowthGlobalX128; // amount of input token paid as protocol fee uint128 protocolFee; // the current liquidity in range uint128 liquidity; } struct StepComputations { // the price at the beginning of the step uint160 sqrtPriceStartX96; // the next tick to swap to from the current tick in the swap direction int24 tickNext; // whether tickNext is initialized or not bool initialized; // sqrt(price) for the next tick (1/0) uint160 sqrtPriceNextX96; // how much is being swapped in in this step uint256 amountIn; // how much is being swapped out uint256 amountOut; // how much fee is being paid in uint256 feeAmount; } /// @inheritdoc IUniswapV3PoolActions function swap( address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data ) external override noDelegateCall returns (int256 amount0, int256 amount1) { require(amountSpecified != 0, 'AS'); Slot0 memory slot0Start = slot0; require(slot0Start.unlocked, 'LOK'); require( zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, 'SPL' ); slot0.unlocked = false; SwapCache memory cache = SwapCache({ liquidityStart: liquidity, blockTimestamp: _blockTimestamp(), feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, computedLatestObservation: false }); bool exactInput = amountSpecified > 0; SwapState memory state = SwapState({ amountSpecifiedRemaining: amountSpecified, amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, protocolFee: 0, liquidity: cache.liquidityStart }); // 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) { StepComputations memory step; step.sqrtPriceStartX96 = state.sqrtPriceX96; (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( state.tick, tickSpacing, zeroForOne ); // 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) { step.tickNext = TickMath.MIN_TICK; } else if (step.tickNext > TickMath.MAX_TICK) { step.tickNext = TickMath.MAX_TICK; } // get the price for the next tick step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); // 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, (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96) ? sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, fee ); if (exactInput) { state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); } else { state.amountSpecifiedRemaining += step.amountOut.toInt256(); state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); } // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee if (cache.feeProtocol > 0) { uint256 delta = step.feeAmount / cache.feeProtocol; step.feeAmount -= delta; state.protocolFee += uint128(delta); } // update global fee tracker if (state.liquidity > 0) state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); // shift tick if we reached the next price if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { // if the tick is initialized, run the tick transition if (step.initialized) { // check for the placeholder value, which we replace with the actual value the first time the swap // crosses an initialized tick if (!cache.computedLatestObservation) { (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle( cache.blockTimestamp, 0, slot0Start.tick, slot0Start.observationIndex, cache.liquidityStart, slot0Start.observationCardinality ); cache.computedLatestObservation = true; } int128 liquidityNet = ticks.cross( step.tickNext, (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, cache.blockTimestamp ); // if we're moving leftward, we interpret liquidityNet as the opposite sign // safe because liquidityNet cannot be type(int128).min if (zeroForOne) liquidityNet = -liquidityNet; state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); } state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) { // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); } } // update tick and write an oracle entry if the tick change if (state.tick != slot0Start.tick) { (uint16 observationIndex, uint16 observationCardinality) = observations.write( slot0Start.observationIndex, cache.blockTimestamp, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, slot0Start.observationCardinalityNext ); (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = ( state.sqrtPriceX96, state.tick, observationIndex, observationCardinality ); } else { // otherwise just update the price slot0.sqrtPriceX96 = state.sqrtPriceX96; } // update liquidity if it changed if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; // update fee growth global and, if necessary, protocol fees // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees if (zeroForOne) { feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; } else { feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; } (amount0, amount1) = zeroForOne == exactInput ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); // do the transfers and collect payment if (zeroForOne) { if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); uint256 balance0Before = balance0(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); } else { if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); uint256 balance1Before = balance1(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); } emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); slot0.unlocked = true; } /// @inheritdoc IUniswapV3PoolActions function flash( address recipient, uint256 amount0, uint256 amount1, bytes calldata data ) external override lock noDelegateCall { uint128 _liquidity = liquidity; require(_liquidity > 0, 'L'); uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); uint256 balance0Before = balance0(); uint256 balance1Before = balance1(); if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data); uint256 balance0After = balance0(); uint256 balance1After = balance1(); require(balance0Before.add(fee0) <= balance0After, 'F0'); require(balance1Before.add(fee1) <= balance1After, 'F1'); // sub is safe because we know balanceAfter is gt balanceBefore by at least fee uint256 paid0 = balance0After - balance0Before; uint256 paid1 = balance1After - balance1Before; if (paid0 > 0) { uint8 feeProtocol0 = slot0.feeProtocol % 16; uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0); feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity); } if (paid1 > 0) { uint8 feeProtocol1 = slot0.feeProtocol >> 4; uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1); feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity); } emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); } /// @inheritdoc IUniswapV3PoolOwnerActions function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner { require( (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) ); uint8 feeProtocolOld = slot0.feeProtocol; slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); } /// @inheritdoc IUniswapV3PoolOwnerActions function collectProtocol( address recipient, uint128 amount0Requested, uint128 amount1Requested ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; if (amount0 > 0) { if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings protocolFees.token0 -= amount0; TransferHelper.safeTransfer(token0, recipient, amount0); } if (amount1 > 0) { if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings protocolFees.token1 -= amount1; TransferHelper.safeTransfer(token1, recipient, amount1); } emit CollectProtocol(msg.sender, recipient, amount0, amount1); } }
Texte modifié
Ouvrir un fichier
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { using LowGasSafeMath for uint256; using LowGasSafeMath for int256; using SafeCast for uint256; using SafeCast for int256; using Tick for mapping(int24 => Tick.Info); using TickBitmap for mapping(int16 => uint256); using Position for mapping(bytes32 => Position.Info); using Position for Position.Info; using Oracle for Oracle.Observation[65535]; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override factory; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override token0; /// @inheritdoc IUniswapV3PoolImmutables address public immutable override token1; /// @inheritdoc IUniswapV3PoolImmutables uint24 public immutable override fee; /// @inheritdoc IUniswapV3PoolImmutables int24 public immutable override tickSpacing; /// @inheritdoc IUniswapV3PoolImmutables uint128 public immutable override maxLiquidityPerTick; struct Slot0 { // the current price uint160 sqrtPriceX96; // the current tick int24 tick; // the most-recently updated index of the observations array uint16 observationIndex; // the current maximum number of observations that are being stored uint16 observationCardinality; // the next maximum number of observations to store, triggered in observations.write uint16 observationCardinalityNext; // the current protocol fee as a percentage of the swap fee taken on withdrawal // represented as an integer denominator (1/x)% uint8 feeProtocol; // whether the pool is locked bool unlocked; } /// @inheritdoc IUniswapV3PoolState Slot0 public override slot0; /// @inheritdoc IUniswapV3PoolState uint256 public override feeGrowthGlobal0X128; /// @inheritdoc IUniswapV3PoolState uint256 public override feeGrowthGlobal1X128; // accumulated protocol fees in token0/token1 units struct ProtocolFees { uint128 token0; uint128 token1; } /// @inheritdoc IUniswapV3PoolState ProtocolFees public override protocolFees; /// @inheritdoc IUniswapV3PoolState uint128 public override liquidity; /// @inheritdoc IUniswapV3PoolState mapping(int24 => Tick.Info) public override ticks; /// @inheritdoc IUniswapV3PoolState mapping(int16 => uint256) public override tickBitmap; /// @inheritdoc IUniswapV3PoolState mapping(bytes32 => Position.Info) public override positions; /// @inheritdoc IUniswapV3PoolState Oracle.Observation[65535] public override observations; /// @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 /// we use balance checks to determine the payment status of interactions such as mint, swap and flash. modifier lock() { require(slot0.unlocked, 'LOK'); slot0.unlocked = false; _; slot0.unlocked = true; } /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner() modifier onlyFactoryOwner() { require(msg.sender == IUniswapV3Factory(factory).owner()); _; } constructor() { int24 _tickSpacing; (factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters(); tickSpacing = _tickSpacing; maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); } /// @dev Common checks for valid tick inputs. function checkTicks(int24 tickLower, int24 tickUpper) private pure { require(tickLower < tickUpper, 'TLU'); require(tickLower >= TickMath.MIN_TICK, 'TLM'); 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. function _blockTimestamp() internal view virtual returns (uint32) { return uint32(block.timestamp); // truncation is desired } /// @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 /// check function balance0() private view returns (uint256) { (bool success, bytes memory data) = token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } /// @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 /// check function balance1() private view returns (uint256) { (bool success, bytes memory data) = token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } /// @inheritdoc IUniswapV3PoolDerivedState function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) external view override noDelegateCall returns ( int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside ) { checkTicks(tickLower, tickUpper); int56 tickCumulativeLower; int56 tickCumulativeUpper; uint160 secondsPerLiquidityOutsideLowerX128; uint160 secondsPerLiquidityOutsideUpperX128; uint32 secondsOutsideLower; uint32 secondsOutsideUpper; { Tick.Info storage lower = ticks[tickLower]; Tick.Info storage upper = ticks[tickUpper]; bool initializedLower; (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( lower.tickCumulativeOutside, lower.secondsPerLiquidityOutsideX128, lower.secondsOutside, lower.initialized ); require(initializedLower); bool initializedUpper; (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = ( upper.tickCumulativeOutside, upper.secondsPerLiquidityOutsideX128, upper.secondsOutside, upper.initialized ); require(initializedUpper); } Slot0 memory _slot0 = slot0; if (_slot0.tick < tickLower) { return ( tickCumulativeLower - tickCumulativeUpper, secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, secondsOutsideLower - secondsOutsideUpper ); } else if (_slot0.tick < tickUpper) { uint32 time = _blockTimestamp(); (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( time, 0, _slot0.tick, _slot0.observationIndex, liquidity, _slot0.observationCardinality ); return ( tickCumulative - tickCumulativeLower - tickCumulativeUpper, secondsPerLiquidityCumulativeX128 - secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, time - secondsOutsideLower - secondsOutsideUpper ); } else { return ( tickCumulativeUpper - tickCumulativeLower, secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, secondsOutsideUpper - secondsOutsideLower ); } } /// @inheritdoc IUniswapV3PoolDerivedState function observe(uint32[] calldata secondsAgos) external view override noDelegateCall returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { return observations.observe( _blockTimestamp(), secondsAgos, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality ); } /// @inheritdoc IUniswapV3PoolActions function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external override lock noDelegateCall { uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event uint16 observationCardinalityNextNew = observations.grow(observationCardinalityNextOld, observationCardinalityNext); slot0.observationCardinalityNext = observationCardinalityNextNew; if (observationCardinalityNextOld != observationCardinalityNextNew) emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); } /// @inheritdoc IUniswapV3PoolActions /// @dev not locked because it initializes unlocked function initialize(uint160 sqrtPriceX96) external override { require(slot0.sqrtPriceX96 == 0, 'AI'); int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); slot0 = Slot0({ sqrtPriceX96: sqrtPriceX96, tick: tick, observationIndex: 0, observationCardinality: cardinality, observationCardinalityNext: cardinalityNext, feeProtocol: 0, unlocked: true }); emit Initialize(sqrtPriceX96, tick); } struct ModifyPositionParams { // the address that owns the position address owner; // the lower and upper tick of the position int24 tickLower; int24 tickUpper; // any change in liquidity int128 liquidityDelta; } /// @dev Effect some changes to a position /// @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 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 function _modifyPosition(ModifyPositionParams memory params) private noDelegateCall returns ( Position.Info storage position, int256 amount0, int256 amount1 ) { checkTicks(params.tickLower, params.tickUpper); Slot0 memory _slot0 = slot0; // SLOAD for gas optimization position = _updatePosition( params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, _slot0.tick ); if (params.liquidityDelta != 0) { if (_slot0.tick < params.tickLower) { // 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 amount0 = SqrtPriceMath.getAmount0Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); } else if (_slot0.tick < params.tickUpper) { // current tick is inside the passed range uint128 liquidityBefore = liquidity; // SLOAD for gas optimization // write an oracle entry (slot0.observationIndex, slot0.observationCardinality) = observations.write( _slot0.observationIndex, _blockTimestamp(), _slot0.tick, liquidityBefore, _slot0.observationCardinality, _slot0.observationCardinalityNext ); amount0 = SqrtPriceMath.getAmount0Delta( _slot0.sqrtPriceX96, TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), _slot0.sqrtPriceX96, params.liquidityDelta ); liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta); } else { // 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 amount1 = SqrtPriceMath.getAmount1Delta( TickMath.getSqrtRatioAtTick(params.tickLower), TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta ); } } } /// @dev Gets and updates a position with the given liquidity delta /// @param owner the owner of the position /// @param tickLower the lower 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 function _updatePosition( address owner, int24 tickLower, int24 tickUpper, int128 liquidityDelta, int24 tick ) private returns (Position.Info storage position) { position = positions.get(owner, tickLower, tickUpper); uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization // if we need to update the ticks, do it bool flippedLower; bool flippedUpper; if (liquidityDelta != 0) { uint32 time = _blockTimestamp(); (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( time, 0, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality ); flippedLower = ticks.update( tickLower, tick, liquidityDelta, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, time, false, maxLiquidityPerTick ); flippedUpper = ticks.update( tickUpper, tick, liquidityDelta, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128, secondsPerLiquidityCumulativeX128, tickCumulative, time, true, maxLiquidityPerTick ); if (flippedLower) { tickBitmap.flipTick(tickLower, tickSpacing); } if (flippedUpper) { tickBitmap.flipTick(tickUpper, tickSpacing); } } (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128); position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); // clear any tick data that is no longer needed if (liquidityDelta < 0) { if (flippedLower) { ticks.clear(tickLower); } if (flippedUpper) { ticks.clear(tickUpper); } } } /// @inheritdoc IUniswapV3PoolActions /// @dev noDelegateCall is applied indirectly via _modifyPosition function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external override lock returns (uint256 amount0, uint256 amount1) { require(amount > 0); (, int256 amount0Int, int256 amount1Int) = _modifyPosition( ModifyPositionParams({ owner: recipient, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: int256(amount).toInt128() }) ); amount0 = uint256(amount0Int); amount1 = uint256(amount1Int); uint256 balance0Before; uint256 balance1Before; if (amount0 > 0) balance0Before = balance0(); if (amount1 > 0) balance1Before = balance1(); IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0'); if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1'); emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); } /// @inheritdoc IUniswapV3PoolActions function collect( address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested ) 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} Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper); amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested; amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested; if (amount0 > 0) { position.tokensOwed0 -= amount0; TransferHelper.safeTransfer(token0, recipient, amount0); } if (amount1 > 0) { position.tokensOwed1 -= amount1; TransferHelper.safeTransfer(token1, recipient, amount1); } emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); } /// @inheritdoc IUniswapV3PoolActions /// @dev noDelegateCall is applied indirectly via _modifyPosition function burn( int24 tickLower, int24 tickUpper, uint128 amount ) external override lock returns (uint256 amount0, uint256 amount1) { (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( ModifyPositionParams({ owner: msg.sender, tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: -int256(amount).toInt128() }) ); amount0 = uint256(-amount0Int); amount1 = uint256(-amount1Int); if (amount0 > 0 || amount1 > 0) { (position.tokensOwed0, position.tokensOwed1) = ( position.tokensOwed0 + uint128(amount0), position.tokensOwed1 + uint128(amount1) ); } emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); } struct SwapCache { // the protocol fee for the input token uint8 feeProtocol; // liquidity at the beginning of the swap uint128 liquidityStart; // the timestamp of the current block uint32 blockTimestamp; // the current value of the tick accumulator, computed only if we cross an initialized tick int56 tickCumulative; // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick uint160 secondsPerLiquidityCumulativeX128; // whether we've computed and cached the above two accumulators bool computedLatestObservation; } // the top level state of the swap, the results of which are recorded in storage at the end struct SwapState { // the amount remaining to be swapped in/out of the input/output asset int256 amountSpecifiedRemaining; // the amount already swapped out/in of the output/input asset int256 amountCalculated; // current sqrt(price) uint160 sqrtPriceX96; // the tick associated with the current price int24 tick; // the global fee growth of the input token uint256 feeGrowthGlobalX128; // amount of input token paid as protocol fee uint128 protocolFee; // the current liquidity in range uint128 liquidity; } struct StepComputations { // the price at the beginning of the step uint160 sqrtPriceStartX96; // the next tick to swap to from the current tick in the swap direction int24 tickNext; // whether tickNext is initialized or not bool initialized; // sqrt(price) for the next tick (1/0) uint160 sqrtPriceNextX96; // how much is being swapped in in this step uint256 amountIn; // how much is being swapped out uint256 amountOut; // how much fee is being paid in uint256 feeAmount; } /// @inheritdoc IUniswapV3PoolActions function swap( address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data ) external override noDelegateCall returns (int256 amount0, int256 amount1) { require(amountSpecified != 0, 'AS'); Slot0 memory slot0Start = slot0; require(slot0Start.unlocked, 'LOK'); require( zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, 'SPL' ); slot0.unlocked = false; SwapCache memory cache = SwapCache({ liquidityStart: liquidity, blockTimestamp: _blockTimestamp(), feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), secondsPerLiquidityCumulativeX128: 0, tickCumulative: 0, computedLatestObservation: false }); bool exactInput = amountSpecified > 0; SwapState memory state = SwapState({ amountSpecifiedRemaining: amountSpecified, amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, protocolFee: 0, liquidity: cache.liquidityStart }); // 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) { StepComputations memory step; step.sqrtPriceStartX96 = state.sqrtPriceX96; (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( state.tick, tickSpacing, zeroForOne ); // 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) { step.tickNext = TickMath.MIN_TICK; } else if (step.tickNext > TickMath.MAX_TICK) { step.tickNext = TickMath.MAX_TICK; } // get the price for the next tick step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); // 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, (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96) ? sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, fee ); if (exactInput) { state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); } else { state.amountSpecifiedRemaining += step.amountOut.toInt256(); state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); } // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee if (cache.feeProtocol > 0) { uint256 delta = (step.feeAmount * cache.feeProtocol) / 15; step.feeAmount -= delta; state.protocolFee += uint128(delta); } // update global fee tracker if (state.liquidity > 0) state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); // shift tick if we reached the next price if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { // if the tick is initialized, run the tick transition if (step.initialized) { // check for the placeholder value, which we replace with the actual value the first time the swap // crosses an initialized tick if (!cache.computedLatestObservation) { (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle( cache.blockTimestamp, 0, slot0Start.tick, slot0Start.observationIndex, cache.liquidityStart, slot0Start.observationCardinality ); cache.computedLatestObservation = true; } int128 liquidityNet = ticks.cross( step.tickNext, (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, cache.blockTimestamp ); // if we're moving leftward, we interpret liquidityNet as the opposite sign // safe because liquidityNet cannot be type(int128).min if (zeroForOne) liquidityNet = -liquidityNet; state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); } state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) { // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); } } // update tick and write an oracle entry if the tick change if (state.tick != slot0Start.tick) { (uint16 observationIndex, uint16 observationCardinality) = observations.write( slot0Start.observationIndex, cache.blockTimestamp, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, slot0Start.observationCardinalityNext ); (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = ( state.sqrtPriceX96, state.tick, observationIndex, observationCardinality ); } else { // otherwise just update the price slot0.sqrtPriceX96 = state.sqrtPriceX96; } // update liquidity if it changed if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; // update fee growth global and, if necessary, protocol fees // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees if (zeroForOne) { feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; } else { feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; } (amount0, amount1) = zeroForOne == exactInput ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); // do the transfers and collect payment if (zeroForOne) { if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); uint256 balance0Before = balance0(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); } else { if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); uint256 balance1Before = balance1(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); } emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); slot0.unlocked = true; } /// @inheritdoc IUniswapV3PoolActions function flash( address recipient, uint256 amount0, uint256 amount1, bytes calldata data ) external override lock noDelegateCall { uint128 _liquidity = liquidity; require(_liquidity > 0, 'L'); uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); uint256 balance0Before = balance0(); uint256 balance1Before = balance1(); if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data); uint256 balance0After = balance0(); uint256 balance1After = balance1(); require(balance0Before.add(fee0) <= balance0After, 'F0'); require(balance1Before.add(fee1) <= balance1After, 'F1'); // sub is safe because we know balanceAfter is gt balanceBefore by at least fee uint256 paid0 = balance0After - balance0Before; uint256 paid1 = balance1After - balance1Before; if (paid0 > 0) { uint8 feeProtocol0 = slot0.feeProtocol % 16; uint256 fees0 = (paid0 * feeProtocol0) / 15; if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0); feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity); } if (paid1 > 0) { uint8 feeProtocol1 = slot0.feeProtocol >> 4; uint256 fees1 = (paid1 * feeProtocol1) / 15; if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1); feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity); } emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); } /// @inheritdoc IUniswapV3PoolOwnerActions function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner { uint8 feeProtocolOld = slot0.feeProtocol; slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); } /// @inheritdoc IUniswapV3PoolOwnerActions function collectProtocol( address recipient, uint128 amount0Requested, uint128 amount1Requested ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; if (amount0 > 0) { if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings protocolFees.token0 -= amount0; TransferHelper.safeTransfer(token0, recipient, amount0); } if (amount1 > 0) { if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings protocolFees.token1 -= amount1; TransferHelper.safeTransfer(token1, recipient, amount1); } emit CollectProtocol(msg.sender, recipient, amount0, amount1); } }
Trouver la différence