Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
univ3diff
作成日
3 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
0 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
638 行
すべてコピー
0 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
638 行
すべてコピー
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
using LowGasSafeMath for uint256;
using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
using LowGasSafeMath for int256;
using SafeCast for uint256;
using SafeCast for uint256;
using SafeCast for int256;
using SafeCast for int256;
using Tick for mapping(int24 => Tick.Info);
using Tick for mapping(int24 => Tick.Info);
using TickBitmap for mapping(int16 => uint256);
using TickBitmap for mapping(int16 => uint256);
using Position for mapping(bytes32 => Position.Info);
using Position for mapping(bytes32 => Position.Info);
using Position for Position.Info;
using Position for Position.Info;
using Oracle for Oracle.Observation[65535];
using Oracle for Oracle.Observation[65535];
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override factory;
address public immutable override factory;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token0;
address public immutable override token0;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token1;
address public immutable override token1;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
uint24 public immutable override fee;
uint24 public immutable override fee;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
int24 public immutable override tickSpacing;
int24 public immutable override tickSpacing;
/// @inheritdoc IUniswapV3PoolImmutables
/// @inheritdoc IUniswapV3PoolImmutables
uint128 public immutable override maxLiquidityPerTick;
uint128 public immutable override maxLiquidityPerTick;
struct Slot0 {
struct Slot0 {
// the current price
// the current price
uint160 sqrtPriceX96;
uint160 sqrtPriceX96;
// the current tick
// the current tick
int24 tick;
int24 tick;
// the most-recently updated index of the observations array
// the most-recently updated index of the observations array
uint16 observationIndex;
uint16 observationIndex;
// the current maximum number of observations that are being stored
// the current maximum number of observations that are being stored
uint16 observationCardinality;
uint16 observationCardinality;
// the next maximum number of observations to store, triggered in observations.write
// the next maximum number of observations to store, triggered in observations.write
uint16 observationCardinalityNext;
uint16 observationCardinalityNext;
// the current protocol fee as a percentage of the swap fee taken on withdrawal
// the current protocol fee as a percentage of the swap fee taken on withdrawal
// represented as an integer denominator (1/x)%
// represented as an integer denominator (1/x)%
uint8 feeProtocol;
uint8 feeProtocol;
// whether the pool is locked
// whether the pool is locked
bool unlocked;
bool unlocked;
}
}
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
Slot0 public override slot0;
Slot0 public override slot0;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal0X128;
uint256 public override feeGrowthGlobal0X128;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal1X128;
uint256 public override feeGrowthGlobal1X128;
// accumulated protocol fees in token0/token1 units
// accumulated protocol fees in token0/token1 units
struct ProtocolFees {
struct ProtocolFees {
uint128 token0;
uint128 token0;
uint128 token1;
uint128 token1;
}
}
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
ProtocolFees public override protocolFees;
ProtocolFees public override protocolFees;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
uint128 public override liquidity;
uint128 public override liquidity;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(int24 => Tick.Info) public override ticks;
mapping(int24 => Tick.Info) public override ticks;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(int16 => uint256) public override tickBitmap;
mapping(int16 => uint256) public override tickBitmap;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
mapping(bytes32 => Position.Info) public override positions;
mapping(bytes32 => Position.Info) public override positions;
/// @inheritdoc IUniswapV3PoolState
/// @inheritdoc IUniswapV3PoolState
Oracle.Observation[65535] public override observations;
Oracle.Observation[65535] public override observations;
/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
modifier lock() {
modifier lock() {
require(slot0.unlocked, 'LOK');
require(slot0.unlocked, 'LOK');
slot0.unlocked = false;
slot0.unlocked = false;
_;
_;
slot0.unlocked = true;
slot0.unlocked = true;
}
}
/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
modifier onlyFactoryOwner() {
modifier onlyFactoryOwner() {
require(msg.sender == IUniswapV3Factory(factory).owner());
require(msg.sender == IUniswapV3Factory(factory).owner());
_;
_;
}
}
constructor() {
constructor() {
int24 _tickSpacing;
int24 _tickSpacing;
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
tickSpacing = _tickSpacing;
tickSpacing = _tickSpacing;
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
}
}
/// @dev Common checks for valid tick inputs.
/// @dev Common checks for valid tick inputs.
function checkTicks(int24 tickLower, int24 tickUpper) private pure {
function checkTicks(int24 tickLower, int24 tickUpper) private pure {
require(tickLower < tickUpper, 'TLU');
require(tickLower < tickUpper, 'TLU');
require(tickLower >= TickMath.MIN_TICK, 'TLM');
require(tickLower >= TickMath.MIN_TICK, 'TLM');
require(tickUpper <= TickMath.MAX_TICK, 'TUM');
require(tickUpper <= TickMath.MAX_TICK, 'TUM');
}
}
/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
function _blockTimestamp() internal view virtual returns (uint32) {
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp); // truncation is desired
return uint32(block.timestamp); // truncation is desired
}
}
/// @dev Get the pool's balance of token0
/// @dev Get the pool's balance of token0
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
/// check
function balance0() private view returns (uint256) {
function balance0() private view returns (uint256) {
(bool success, bytes memory data) =
(bool success, bytes memory data) =
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
require(success && data.length >= 32);
return abi.decode(data, (uint256));
return abi.decode(data, (uint256));
}
}
/// @dev Get the pool's balance of token1
/// @dev Get the pool's balance of token1
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
/// check
function balance1() private view returns (uint256) {
function balance1() private view returns (uint256) {
(bool success, bytes memory data) =
(bool success, bytes memory data) =
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
require(success && data.length >= 32);
return abi.decode(data, (uint256));
return abi.decode(data, (uint256));
}
}
/// @inheritdoc IUniswapV3PoolDerivedState
/// @inheritdoc IUniswapV3PoolDerivedState
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
external
view
view
override
override
noDelegateCall
noDelegateCall
returns (
returns (
int56 tickCumulativeInside,
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
uint32 secondsInside
)
)
{
{
checkTicks(tickLower, tickUpper);
checkTicks(tickLower, tickUpper);
int56 tickCumulativeLower;
int56 tickCumulativeLower;
int56 tickCumulativeUpper;
int56 tickCumulativeUpper;
uint160 secondsPerLiquidityOutsideLowerX128;
uint160 secondsPerLiquidityOutsideLowerX128;
uint160 secondsPerLiquidityOutsideUpperX128;
uint160 secondsPerLiquidityOutsideUpperX128;
uint32 secondsOutsideLower;
uint32 secondsOutsideLower;
uint32 secondsOutsideUpper;
uint32 secondsOutsideUpper;
{
{
Tick.Info storage lower = ticks[tickLower];
Tick.Info storage lower = ticks[tickLower];
Tick.Info storage upper = ticks[tickUpper];
Tick.Info storage upper = ticks[tickUpper];
bool initializedLower;
bool initializedLower;
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
lower.tickCumulativeOutside,
lower.tickCumulativeOutside,
lower.secondsPerLiquidityOutsideX128,
lower.secondsPerLiquidityOutsideX128,
lower.secondsOutside,
lower.secondsOutside,
lower.initialized
lower.initialized
);
);
require(initializedLower);
require(initializedLower);
bool initializedUpper;
bool initializedUpper;
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
upper.tickCumulativeOutside,
upper.tickCumulativeOutside,
upper.secondsPerLiquidityOutsideX128,
upper.secondsPerLiquidityOutsideX128,
upper.secondsOutside,
upper.secondsOutside,
upper.initialized
upper.initialized
);
);
require(initializedUpper);
require(initializedUpper);
}
}
Slot0 memory _slot0 = slot0;
Slot0 memory _slot0 = slot0;
if (_slot0.tick < tickLower) {
if (_slot0.tick < tickLower) {
return (
return (
tickCumulativeLower - tickCumulativeUpper,
tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
secondsOutsideLower - secondsOutsideUpper
secondsOutsideLower - secondsOutsideUpper
);
);
} else if (_slot0.tick < tickUpper) {
} else if (_slot0.tick < tickUpper) {
uint32 time = _blockTimestamp();
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
observations.observeSingle(
time,
time,
0,
0,
_slot0.tick,
_slot0.tick,
_slot0.observationIndex,
_slot0.observationIndex,
liquidity,
liquidity,
_slot0.observationCardinality
_slot0.observationCardinality
);
);
return (
return (
tickCumulative - tickCumulativeLower - tickCumulativeUpper,
tickCumulative - tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityCumulativeX128 -
secondsPerLiquidityCumulativeX128 -
secondsPerLiquidityOutsideLowerX128 -
secondsPerLiquidityOutsideLowerX128 -
secondsPerLiquidityOutsideUpperX128,
secondsPerLiquidityOutsideUpperX128,
time - secondsOutsideLower - secondsOutsideUpper
time - secondsOutsideLower - secondsOutsideUpper
);
);
} else {
} else {
return (
return (
tickCumulativeUpper - tickCumulativeLower,
tickCumulativeUpper - tickCumulativeLower,
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
secondsOutsideUpper - secondsOutsideLower
secondsOutsideUpper - secondsOutsideLower
);
);
}
}
}
}
/// @inheritdoc IUniswapV3PoolDerivedState
/// @inheritdoc IUniswapV3PoolDerivedState
function observe(uint32[] calldata secondsAgos)
function observe(uint32[] calldata secondsAgos)
external
external
view
view
override
override
noDelegateCall
noDelegateCall
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
{
{
return
return
observations.observe(
observations.observe(
_blockTimestamp(),
_blockTimestamp(),
secondsAgos,
secondsAgos,
slot0.tick,
slot0.tick,
slot0.observationIndex,
slot0.observationIndex,
liquidity,
liquidity,
slot0.observationCardinality
slot0.observationCardinality
);
);
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
external
external
override
override
lock
lock
noDelegateCall
noDelegateCall
{
{
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
uint16 observationCardinalityNextNew =
uint16 observationCardinalityNextNew =
observations.grow(observationCardinalityNextOld, observationCardinalityNext);
observations.grow(observationCardinalityNextOld, observationCardinalityNext);
slot0.observationCardinalityNext = observationCardinalityNextNew;
slot0.observationCardinalityNext = observationCardinalityNextNew;
if (observationCardinalityNextOld != observationCardinalityNextNew)
if (observationCardinalityNextOld != observationCardinalityNextNew)
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev not locked because it initializes unlocked
/// @dev not locked because it initializes unlocked
function initialize(uint160 sqrtPriceX96) external override {
function initialize(uint160 sqrtPriceX96) external override {
require(slot0.sqrtPriceX96 == 0, 'AI');
require(slot0.sqrtPriceX96 == 0, 'AI');
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
slot0 = Slot0({
slot0 = Slot0({
sqrtPriceX96: sqrtPriceX96,
sqrtPriceX96: sqrtPriceX96,
tick: tick,
tick: tick,
observationIndex: 0,
observationIndex: 0,
observationCardinality: cardinality,
observationCardinality: cardinality,
observationCardinalityNext: cardinalityNext,
observationCardinalityNext: cardinalityNext,
feeProtocol: 0,
feeProtocol: 0,
unlocked: true
unlocked: true
});
});
emit Initialize(sqrtPriceX96, tick);
emit Initialize(sqrtPriceX96, tick);
}
}
struct ModifyPositionParams {
struct ModifyPositionParams {
// the address that owns the position
// the address that owns the position
address owner;
address owner;
// the lower and upper tick of the position
// the lower and upper tick of the position
int24 tickLower;
int24 tickLower;
int24 tickUpper;
int24 tickUpper;
// any change in liquidity
// any change in liquidity
int128 liquidityDelta;
int128 liquidityDelta;
}
}
/// @dev Effect some changes to a position
/// @dev Effect some changes to a position
/// @param params the position details and the change to the position's liquidity to effect
/// @param params the position details and the change to the position's liquidity to effect
/// @return position a storage pointer referencing the position with the given owner and tick range
/// @return position a storage pointer referencing the position with the given owner and tick range
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
function _modifyPosition(ModifyPositionParams memory params)
function _modifyPosition(ModifyPositionParams memory params)
private
private
noDelegateCall
noDelegateCall
returns (
returns (
Position.Info storage position,
Position.Info storage position,
int256 amount0,
int256 amount0,
int256 amount1
int256 amount1
)
)
{
{
checkTicks(params.tickLower, params.tickUpper);
checkTicks(params.tickLower, params.tickUpper);
Slot0 memory _slot0 = slot0; // SLOAD for gas optimization
Slot0 memory _slot0 = slot0; // SLOAD for gas optimization
position = _updatePosition(
position = _updatePosition(
params.owner,
params.owner,
params.tickLower,
params.tickLower,
params.tickUpper,
params.tickUpper,
params.liquidityDelta,
params.liquidityDelta,
_slot0.tick
_slot0.tick
);
);
if (params.liquidityDelta != 0) {
if (params.liquidityDelta != 0) {
if (_slot0.tick < params.tickLower) {
if (_slot0.tick < params.tickLower) {
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
amount0 = SqrtPriceMath.getAmount0Delta(
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
} else if (_slot0.tick < params.tickUpper) {
} else if (_slot0.tick < params.tickUpper) {
// current tick is inside the passed range
// current tick is inside the passed range
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization
// write an oracle entry
// write an oracle entry
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
_slot0.observationIndex,
_slot0.observationIndex,
_blockTimestamp(),
_blockTimestamp(),
_slot0.tick,
_slot0.tick,
liquidityBefore,
liquidityBefore,
_slot0.observationCardinality,
_slot0.observationCardinality,
_slot0.observationCardinalityNext
_slot0.observationCardinalityNext
);
);
amount0 = SqrtPriceMath.getAmount0Delta(
amount0 = SqrtPriceMath.getAmount0Delta(
_slot0.sqrtPriceX96,
_slot0.sqrtPriceX96,
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
amount1 = SqrtPriceMath.getAmount1Delta(
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
_slot0.sqrtPriceX96,
_slot0.sqrtPriceX96,
params.liquidityDelta
params.liquidityDelta
);
);
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
} else {
} else {
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
amount1 = SqrtPriceMath.getAmount1Delta(
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
params.liquidityDelta
);
);
}
}
}
}
}
}
/// @dev Gets and updates a position with the given liquidity delta
/// @dev Gets and updates a position with the given liquidity delta
/// @param owner the owner of the position
/// @param owner the owner of the position
/// @param tickLower the lower tick of the position's tick range
/// @param tickLower the lower tick of the position's tick range
/// @param tickUpper the upper tick of the position's tick range
/// @param tickUpper the upper tick of the position's tick range
/// @param tick the current tick, passed to avoid sloads
/// @param tick the current tick, passed to avoid sloads
function _updatePosition(
function _updatePosition(
address owner,
address owner,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
int128 liquidityDelta,
int128 liquidityDelta,
int24 tick
int24 tick
) private returns (Position.Info storage position) {
) private returns (Position.Info storage position) {
position = positions.get(owner, tickLower, tickUpper);
position = positions.get(owner, tickLower, tickUpper);
uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization
// if we need to update the ticks, do it
// if we need to update the ticks, do it
bool flippedLower;
bool flippedLower;
bool flippedUpper;
bool flippedUpper;
if (liquidityDelta != 0) {
if (liquidityDelta != 0) {
uint32 time = _blockTimestamp();
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
observations.observeSingle(
time,
time,
0,
0,
slot0.tick,
slot0.tick,
slot0.observationIndex,
slot0.observationIndex,
liquidity,
liquidity,
slot0.observationCardinality
slot0.observationCardinality
);
);
flippedLower = ticks.update(
flippedLower = ticks.update(
tickLower,
tickLower,
tick,
tick,
liquidityDelta,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
tickCumulative,
time,
time,
false,
false,
maxLiquidityPerTick
maxLiquidityPerTick
);
);
flippedUpper = ticks.update(
flippedUpper = ticks.update(
tickUpper,
tickUpper,
tick,
tick,
liquidityDelta,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
tickCumulative,
time,
time,
true,
true,
maxLiquidityPerTick
maxLiquidityPerTick
);
);
if (flippedLower) {
if (flippedLower) {
tickBitmap.flipTick(tickLower, tickSpacing);
tickBitmap.flipTick(tickLower, tickSpacing);
}
}
if (flippedUpper) {
if (flippedUpper) {
tickBitmap.flipTick(tickUpper, tickSpacing);
tickBitmap.flipTick(tickUpper, tickSpacing);
}
}
}
}
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
// clear any tick data that is no longer needed
// clear any tick data that is no longer needed
if (liquidityDelta < 0) {
if (liquidityDelta < 0) {
if (flippedLower) {
if (flippedLower) {
ticks.clear(tickLower);
ticks.clear(tickLower);
}
}
if (flippedUpper) {
if (flippedUpper) {
ticks.clear(tickUpper);
ticks.clear(tickUpper);
}
}
}
}
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function mint(
function mint(
address recipient,
address recipient,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount,
uint128 amount,
bytes calldata data
bytes calldata data
) external override lock returns (uint256 amount0, uint256 amount1) {
) external override lock returns (uint256 amount0, uint256 amount1) {
require(amount > 0);
require(amount > 0);
(, int256 amount0Int, int256 amount1Int) =
(, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
_modifyPosition(
ModifyPositionParams({
ModifyPositionParams({
owner: recipient,
owner: recipient,
tickLower: tickLower,
tickLower: tickLower,
tickUpper: tickUpper,
tickUpper: tickUpper,
liquidityDelta: int256(amount).toInt128()
liquidityDelta: int256(amount).toInt128()
})
})
);
);
amount0 = uint256(amount0Int);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
amount1 = uint256(amount1Int);
uint256 balance0Before;
uint256 balance0Before;
uint256 balance1Before;
uint256 balance1Before;
if (amount0 > 0) balance0Before = balance0();
if (amount0 > 0) balance0Before = balance0();
if (amount1 > 0) balance1Before = balance1();
if (amount1 > 0) balance1Before = balance1();
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function collect(
function collect(
address recipient,
address recipient,
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount0Requested,
uint128 amount1Requested
uint128 amount1Requested
) external override lock returns (uint128 amount0, uint128 amount1) {
) external override lock returns (uint128 amount0, uint128 amount1) {
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
if (amount0 > 0) {
if (amount0 > 0) {
position.tokensOwed0 -= amount0;
position.tokensOwed0 -= amount0;
TransferHelper.safeTransfer(token0, recipient, amount0);
TransferHelper.safeTransfer(token0, recipient, amount0);
}
}
if (amount1 > 0) {
if (amount1 > 0) {
position.tokensOwed1 -= amount1;
position.tokensOwed1 -= amount1;
TransferHelper.safeTransfer(token1, recipient, amount1);
TransferHelper.safeTransfer(token1, recipient, amount1);
}
}
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function burn(
function burn(
int24 tickLower,
int24 tickLower,
int24 tickUpper,
int24 tickUpper,
uint128 amount
uint128 amount
) external override lock returns (uint256 amount0, uint256 amount1) {
) external override lock returns (uint256 amount0, uint256 amount1) {
(Position.Info storage position, int256 amount0Int, int256 amount1Int) =
(Position.Info storage position, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
_modifyPosition(
ModifyPositionParams({
ModifyPositionParams({
owner: msg.sender,
owner: msg.sender,
tickLower: tickLower,
tickLower: tickLower,
tickUpper: tickUpper,
tickUpper: tickUpper,
liquidityDelta: -int256(amount).toInt128()
liquidityDelta: -int256(amount).toInt128()
})
})
);
);
amount0 = uint256(-amount0Int);
amount0 = uint256(-amount0Int);
amount1 = uint256(-amount1Int);
amount1 = uint256(-amount1Int);
if (amount0 > 0 || amount1 > 0) {
if (amount0 > 0 || amount1 > 0) {
(position.tokensOwed0, position.tokensOwed1) = (
(position.tokensOwed0, position.tokensOwed1) = (
position.tokensOwed0 + uint128(amount0),
position.tokensOwed0 + uint128(amount0),
position.tokensOwed1 + uint128(amount1)
position.tokensOwed1 + uint128(amount1)
);
);
}
}
emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
}
}
struct SwapCache {
struct SwapCache {
// the protocol fee for the input token
// the protocol fee for the input token
uint8 feeProtocol;
uint8 feeProtocol;
// liquidity at the beginning of the swap
// liquidity at the beginning of the swap
uint128 liquidityStart;
uint128 liquidityStart;
// the timestamp of the current block
// the timestamp of the current block
uint32 blockTimestamp;
uint32 blockTimestamp;
// the current value of the tick accumulator, computed only if we cross an initialized tick
// the current value of the tick accumulator, computed only if we cross an initialized tick
int56 tickCumulative;
int56 tickCumulative;
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
uint160 secondsPerLiquidityCumulativeX128;
uint160 secondsPerLiquidityCumulativeX128;
// whether we've computed and cached the above two accumulators
// whether we've computed and cached the above two accumulators
bool computedLatestObservation;
bool computedLatestObservation;
}
}
// the top level state of the swap, the results of which are recorded in storage at the end
// the top level state of the swap, the results of which are recorded in storage at the end
struct SwapState {
struct SwapState {
// the amount remaining to be swapped in/out of the input/output asset
// the amount remaining to be swapped in/out of the input/output asset
int256 amountSpecifiedRemaining;
int256 amountSpecifiedRemaining;
// the amount already swapped out/in of the output/input asset
// the amount already swapped out/in of the output/input asset
int256 amountCalculated;
int256 amountCalculated;
// current sqrt(price)
// current sqrt(price)
uint160 sqrtPriceX96;
uint160 sqrtPriceX96;
// the tick associated with the current price
// the tick associated with the current price
int24 tick;
int24 tick;
// the global fee growth of the input token
// the global fee growth of the input token
uint256 feeGrowthGlobalX128;
uint256 feeGrowthGlobalX128;
// amount of input token paid as protocol fee
// amount of input token paid as protocol fee
uint128 protocolFee;
uint128 protocolFee;
// the current liquidity in range
// the current liquidity in range
uint128 liquidity;
uint128 liquidity;
}
}
struct StepComputations {
struct StepComputations {
// the price at the beginning of the step
// the price at the beginning of the step
uint160 sqrtPriceStartX96;
uint160 sqrtPriceStartX96;
// the next tick to swap to from the current tick in the swap direction
// the next tick to swap to from the current tick in the swap direction
int24 tickNext;
int24 tickNext;
// whether tickNext is initialized or not
// whether tickNext is initialized or not
bool initialized;
bool initialized;
// sqrt(price) for the next tick (1/0)
// sqrt(price) for the next tick (1/0)
uint160 sqrtPriceNextX96;
uint160 sqrtPriceNextX96;
// how much is being swapped in in this step
// how much is being swapped in in this step
uint256 amountIn;
uint256 amountIn;
// how much is being swapped out
// how much is being swapped out
uint256 amountOut;
uint256 amountOut;
// how much fee is being paid in
// how much fee is being paid in
uint256 feeAmount;
uint256 feeAmount;
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @inheritdoc IUniswapV3PoolActions
function swap(
function swap(
address recipient,
address recipient,
bool zeroForOne,
bool zeroForOne,
int256 amountSpecified,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
uint160 sqrtPriceLimitX96,
bytes calldata data
bytes calldata data
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
require(amountSpecified != 0, 'AS');
require(amountSpecified != 0, 'AS');
Slot0 memory slot0Start = slot0;
Slot0 memory slot0Start = slot0;
require(slot0Start.unlocked, 'LOK');
require(slot0Start.unlocked, 'LOK');
require(
require(
zeroForOne
zeroForOne
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
'SPL'
'SPL'
);
);
slot0.unlocked = false;
slot0.unlocked = false;
SwapCache memory cache =
SwapCache memory cache =
SwapCache({
SwapCache({
liquidityStart: liquidity,
liquidityStart: liquidity,
blockTimestamp: _blockTimestamp(),
blockTimestamp: _blockTimestamp(),
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
secondsPerLiquidityCumulativeX128: 0,
secondsPerLiquidityCumulativeX128: 0,
tickCumulative: 0,
tickCumulative: 0,
computedLatestObservation: false
computedLatestObservation: false
});
});
bool exactInput = amountSpecified > 0;
bool exactInput = amountSpecified > 0;
SwapState memory state =
SwapState memory state =
SwapState({
SwapState({
amountSpecifiedRemaining: amountSpecified,
amountSpecifiedRemaining: amountSpecified,
amountCalculated: 0,
amountCalculated: 0,
sqrtPriceX96: slot0Start.sqrtPriceX96,
sqrtPriceX96: slot0Start.sqrtPriceX96,
tick: slot0Start.tick,
tick: slot0Start.tick,
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
protocolFee: 0,
protocolFee: 0,
liquidity: cache.liquidityStart
liquidity: cache.liquidityStart
});
});
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
StepComputations memory step;
StepComputations memory step;
step.sqrtPriceStartX96 = state.sqrtPriceX96;
step.sqrtPriceStartX96 = state.sqrtPriceX96;
(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
state.tick,
state.tick,
tickSpacing,
tickSpacing,
zeroForOne
zeroForOne
);
);
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
if (step.tickNext < TickMath.MIN_TICK) {
if (step.tickNext < TickMath.MIN_TICK) {
step.tickNext = TickMath.MIN_TICK;
step.tickNext = TickMath.MIN_TICK;
} else if (step.tickNext > TickMath.MAX_TICK) {
} else if (step.tickNext > TickMath.MAX_TICK) {
step.tickNext = TickMath.MAX_TICK;
step.tickNext = TickMath.MAX_TICK;
}
}
// get the price for the next tick
// get the price for the next tick
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
state.sqrtPriceX96,
state.sqrtPriceX96,
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
? sqrtPriceLimitX96
? sqrtPriceLimitX96
: step.sqrtPriceNe
: step.sqrtPriceNe
保存された差分
原文
ファイルを開く
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); } }
変更されたテキスト
ファイルを開く
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); } }
違いを見つける