From db703d82f4876e30f1ca10fb78d85b4dd4c31ec1 Mon Sep 17 00:00:00 2001 From: Erick Date: Fri, 17 Jun 2022 11:42:46 -0400 Subject: [PATCH 01/23] fix typo in `--help` output for `--print` flag (#1149) --- slither/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/__main__.py b/slither/__main__.py index dd195b65b8..e9f688a8e6 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -315,7 +315,7 @@ def parse_args(detector_classes, printer_classes): # pylint: disable=too-many-s group_printer.add_argument( "--print", - help="Comma-separated list fo contract information printers, " + help="Comma-separated list of contract information printers, " f"available printers: {', '.join(d.ARGUMENT for d in printer_classes)}", action="store", dest="printers_to_run", From 38c0ae513d7fe3871daed218629c21471190d94e Mon Sep 17 00:00:00 2001 From: noxx Date: Thu, 14 Jul 2022 18:15:46 +0100 Subject: [PATCH 02/23] add flag for table layout view, table layout with value view and silent flag for suppressing other outputs --- .../contracts/NoDelegateCall.sol | 27 + .../contracts/UniswapV3Pool.sol | 869 ++++++++++++++++++ .../contracts/interfaces/IERC20Minimal.sol | 52 ++ .../interfaces/IUniswapV3Factory.sol | 78 ++ .../contracts/interfaces/IUniswapV3Pool.sol | 24 + .../interfaces/IUniswapV3PoolDeployer.sol | 26 + .../callback/IUniswapV3FlashCallback.sol | 18 + .../callback/IUniswapV3MintCallback.sol | 18 + .../callback/IUniswapV3SwapCallback.sol | 21 + .../interfaces/pool/IUniswapV3PoolActions.sol | 103 +++ .../pool/IUniswapV3PoolDerivedState.sol | 40 + .../interfaces/pool/IUniswapV3PoolEvents.sol | 121 +++ .../pool/IUniswapV3PoolImmutables.sol | 35 + .../pool/IUniswapV3PoolOwnerActions.sol | 23 + .../interfaces/pool/IUniswapV3PoolState.sol | 116 +++ .../contracts/libraries/BitMath.sol | 94 ++ .../contracts/libraries/FixedPoint128.sol | 8 + .../contracts/libraries/FixedPoint96.sol | 10 + .../contracts/libraries/FullMath.sol | 124 +++ .../contracts/libraries/LiquidityMath.sol | 17 + .../contracts/libraries/LowGasSafeMath.sol | 46 + .../contracts/libraries/Oracle.sol | 325 +++++++ .../contracts/libraries/Position.sol | 88 ++ .../contracts/libraries/SafeCast.sol | 28 + .../contracts/libraries/SqrtPriceMath.sol | 227 +++++ .../contracts/libraries/SwapMath.sol | 98 ++ .../contracts/libraries/Tick.sol | 183 ++++ .../contracts/libraries/TickBitmap.sol | 78 ++ .../contracts/libraries/TickMath.sol | 205 +++++ .../contracts/libraries/TransferHelper.sol | 23 + .../contracts/libraries/UnsafeMath.sol | 17 + slither/tools/read_storage/__main__.py | 38 + slither/tools/read_storage/read_storage.py | 119 ++- 33 files changed, 3289 insertions(+), 10 deletions(-) create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol new file mode 100644 index 0000000000..5411979dcb --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +/// @title Prevents delegatecall to a contract +/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract +abstract contract NoDelegateCall { + /// @dev The original address of this contract + address private immutable original; + + constructor() { + // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. + // In other words, this variable won't change when it's checked at runtime. + original = address(this); + } + + /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, + /// and the use of immutable means the address bytes are copied in every place the modifier is used. + function checkNotDelegateCall() private view { + require(address(this) == original); + } + + /// @notice Prevents delegatecall into the modified method + modifier noDelegateCall() { + checkNotDelegateCall(); + _; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol new file mode 100644 index 0000000000..9e09821272 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +import './interfaces/IUniswapV3Pool.sol'; + +import './NoDelegateCall.sol'; + +import './libraries/LowGasSafeMath.sol'; +import './libraries/SafeCast.sol'; +import './libraries/Tick.sol'; +import './libraries/TickBitmap.sol'; +import './libraries/Position.sol'; +import './libraries/Oracle.sol'; + +import './libraries/FullMath.sol'; +import './libraries/FixedPoint128.sol'; +import './libraries/TransferHelper.sol'; +import './libraries/TickMath.sol'; +import './libraries/LiquidityMath.sol'; +import './libraries/SqrtPriceMath.sol'; +import './libraries/SwapMath.sol'; + +import './interfaces/IUniswapV3PoolDeployer.sol'; +import './interfaces/IUniswapV3Factory.sol'; +import './interfaces/IERC20Minimal.sol'; +import './interfaces/callback/IUniswapV3MintCallback.sol'; +import './interfaces/callback/IUniswapV3SwapCallback.sol'; +import './interfaces/callback/IUniswapV3FlashCallback.sol'; + +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); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol new file mode 100644 index 0000000000..c303265a3b --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Minimal ERC20 interface for Uniswap +/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 +interface IERC20Minimal { + /// @notice Returns the balance of a token + /// @param account The account for which to look up the number of tokens it has, i.e. its balance + /// @return The number of tokens held by the account + function balanceOf(address account) external view returns (uint256); + + /// @notice Transfers the amount of token from the `msg.sender` to the recipient + /// @param recipient The account that will receive the amount transferred + /// @param amount The number of tokens to send from the sender to the recipient + /// @return Returns true for a successful transfer, false for an unsuccessful transfer + function transfer(address recipient, uint256 amount) external returns (bool); + + /// @notice Returns the current allowance given to a spender by an owner + /// @param owner The account of the token owner + /// @param spender The account of the token spender + /// @return The current allowance granted by `owner` to `spender` + function allowance(address owner, address spender) external view returns (uint256); + + /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` + /// @param spender The account which will be allowed to spend a given amount of the owners tokens + /// @param amount The amount of tokens allowed to be used by `spender` + /// @return Returns true for a successful approval, false for unsuccessful + function approve(address spender, uint256 amount) external returns (bool); + + /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` + /// @param sender The account from which the transfer will be initiated + /// @param recipient The recipient of the transfer + /// @param amount The amount of the transfer + /// @return Returns true for a successful transfer, false for unsuccessful + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. + /// @param from The account from which the tokens were sent, i.e. the balance decreased + /// @param to The account to which the tokens were sent, i.e. the balance increased + /// @param value The amount of tokens that were transferred + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. + /// @param owner The account that approved spending of its tokens + /// @param spender The account for which the spending allowance was modified + /// @param value The new allowance from the owner to the spender + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol new file mode 100644 index 0000000000..540cfdc681 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title The interface for the Uniswap V3 Factory +/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees +interface IUniswapV3Factory { + /// @notice Emitted when the owner of the factory is changed + /// @param oldOwner The owner before the owner was changed + /// @param newOwner The owner after the owner was changed + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + /// @notice Emitted when a pool is created + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks + /// @param pool The address of the created pool + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + + /// @notice Emitted when a new fee amount is enabled for pool creation via the factory + /// @param fee The enabled fee, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + + /// @notice Returns the current owner of the factory + /// @dev Can be changed by the current owner via setOwner + /// @return The address of the factory owner + function owner() external view returns (address); + + /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled + /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context + /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee + /// @return The tick spacing + function feeAmountTickSpacing(uint24 fee) external view returns (int24); + + /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The pool address + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) external view returns (address pool); + + /// @notice Creates a pool for the given two tokens and fee + /// @param tokenA One of the two tokens in the desired pool + /// @param tokenB The other of the two tokens in the desired pool + /// @param fee The desired fee for the pool + /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved + /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments + /// are invalid. + /// @return pool The address of the newly created pool + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); + + /// @notice Updates the owner of the factory + /// @dev Must be called by the current owner + /// @param _owner The new owner of the factory + function setOwner(address _owner) external; + + /// @notice Enables a fee amount with the given tickSpacing + /// @dev Fee amounts may never be removed once enabled + /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) + /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol new file mode 100644 index 0000000000..56df0500df --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import './pool/IUniswapV3PoolImmutables.sol'; +import './pool/IUniswapV3PoolState.sol'; +import './pool/IUniswapV3PoolDerivedState.sol'; +import './pool/IUniswapV3PoolActions.sol'; +import './pool/IUniswapV3PoolOwnerActions.sol'; +import './pool/IUniswapV3PoolEvents.sol'; + +/// @title The interface for a Uniswap V3 Pool +/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform +/// to the ERC20 specification +/// @dev The pool interface is broken up into many smaller pieces +interface IUniswapV3Pool is + IUniswapV3PoolImmutables, + IUniswapV3PoolState, + IUniswapV3PoolDerivedState, + IUniswapV3PoolActions, + IUniswapV3PoolOwnerActions, + IUniswapV3PoolEvents +{ + +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol new file mode 100644 index 0000000000..72096c1ff5 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools +/// @notice A contract that constructs a pool must implement this to pass arguments to the pool +/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash +/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain +interface IUniswapV3PoolDeployer { + /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. + /// @dev Called by the pool constructor to fetch the parameters of the pool + /// Returns factory The factory address + /// Returns token0 The first token of the pool by address sort order + /// Returns token1 The second token of the pool by address sort order + /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// Returns tickSpacing The minimum number of ticks between initialized ticks + function parameters() + external + view + returns ( + address factory, + address token0, + address token1, + uint24 fee, + int24 tickSpacing + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol new file mode 100644 index 0000000000..18e54c4e12 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#flash +/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface +interface IUniswapV3FlashCallback { + /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. + /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param fee0 The fee amount in token0 due to the pool by the end of the flash + /// @param fee1 The fee amount in token1 due to the pool by the end of the flash + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call + function uniswapV3FlashCallback( + uint256 fee0, + uint256 fee1, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol new file mode 100644 index 0000000000..85447e84f9 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#mint +/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface +interface IUniswapV3MintCallback { + /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. + /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity + /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol new file mode 100644 index 0000000000..9f183b22a7 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#swap +/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface +interface IUniswapV3SwapCallback { + /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + /// @dev In the implementation you must pay the pool tokens owed for the swap. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. + /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol new file mode 100644 index 0000000000..44fb61c24a --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissionless pool actions +/// @notice Contains pool methods that can be called by anyone +interface IUniswapV3PoolActions { + /// @notice Sets the initial price for the pool + /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value + /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 + function initialize(uint160 sqrtPriceX96) external; + + /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position + /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback + /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends + /// on tickLower, tickUpper, the amount of liquidity, and the current price. + /// @param recipient The address for which the liquidity will be created + /// @param tickLower The lower tick of the position in which to add liquidity + /// @param tickUpper The upper tick of the position in which to add liquidity + /// @param amount The amount of liquidity to mint + /// @param data Any data that should be passed through to the callback + /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback + /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Collects tokens owed to a position + /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. + /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or + /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the + /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. + /// @param recipient The address which should receive the fees collected + /// @param tickLower The lower tick of the position for which to collect fees + /// @param tickUpper The upper tick of the position for which to collect fees + /// @param amount0Requested How much token0 should be withdrawn from the fees owed + /// @param amount1Requested How much token1 should be withdrawn from the fees owed + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + + /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position + /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 + /// @dev Fees must be collected separately via a call to #collect + /// @param tickLower The lower tick of the position for which to burn liquidity + /// @param tickUpper The upper tick of the position for which to burn liquidity + /// @param amount How much liquidity to burn + /// @return amount0 The amount of token0 sent to the recipient + /// @return amount1 The amount of token1 sent to the recipient + function burn( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Swap token0 for token1, or token1 for token0 + /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback + /// @param recipient The address to receive the output of the swap + /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 + /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) + /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this + /// value after the swap. If one for zero, the price cannot be greater than this value after the swap + /// @param data Any data to be passed through to the callback + /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive + /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); + + /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback + /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback + /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling + /// with 0 amount{0,1} and sending the donation amount(s) from the callback + /// @param recipient The address which will receive the token0 and token1 amounts + /// @param amount0 The amount of token0 to send + /// @param amount1 The amount of token1 to send + /// @param data Any data to be passed through to the callback + function flash( + address recipient, + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external; + + /// @notice Increase the maximum number of price and liquidity observations that this pool will store + /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to + /// the input observationCardinalityNext. + /// @param observationCardinalityNext The desired minimum number of observations for the pool to store + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol new file mode 100644 index 0000000000..eda3a0089d --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that is not stored +/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the +/// blockchain. The functions here may have variable gas costs. +interface IUniswapV3PoolDerivedState { + /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp + /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing + /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, + /// you must call it with secondsAgos = [3600, 0]. + /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in + /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. + /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned + /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp + /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block + /// timestamp + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); + + /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range + /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. + /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first + /// snapshot is taken and the second snapshot is taken. + /// @param tickLower The lower tick of the range + /// @param tickUpper The upper tick of the range + /// @return tickCumulativeInside The snapshot of the tick accumulator for the range + /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range + /// @return secondsInside The snapshot of seconds per liquidity for the range + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol new file mode 100644 index 0000000000..9d915dde93 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Events emitted by a pool +/// @notice Contains all events emitted by the pool +interface IUniswapV3PoolEvents { + /// @notice Emitted exactly once by a pool when #initialize is first called on the pool + /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize + /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 + /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool + event Initialize(uint160 sqrtPriceX96, int24 tick); + + /// @notice Emitted when liquidity is minted for a given position + /// @param sender The address that minted the liquidity + /// @param owner The owner of the position and recipient of any minted liquidity + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity minted to the position range + /// @param amount0 How much token0 was required for the minted liquidity + /// @param amount1 How much token1 was required for the minted liquidity + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted when fees are collected by the owner of a position + /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees + /// @param owner The owner of the position for which fees are collected + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount0 The amount of token0 fees collected + /// @param amount1 The amount of token1 fees collected + event Collect( + address indexed owner, + address recipient, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount0, + uint128 amount1 + ); + + /// @notice Emitted when a position's liquidity is removed + /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect + /// @param owner The owner of the position for which liquidity is removed + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity to remove + /// @param amount0 The amount of token0 withdrawn + /// @param amount1 The amount of token1 withdrawn + event Burn( + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted by the pool for any swaps between token0 and token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the output of the swap + /// @param amount0 The delta of the token0 balance of the pool + /// @param amount1 The delta of the token1 balance of the pool + /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 + /// @param liquidity The liquidity of the pool after the swap + /// @param tick The log base 1.0001 of price of the pool after the swap + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + /// @notice Emitted by the pool for any flashes of token0/token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the tokens from flash + /// @param amount0 The amount of token0 that was flashed + /// @param amount1 The amount of token1 that was flashed + /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee + /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee + event Flash( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1, + uint256 paid0, + uint256 paid1 + ); + + /// @notice Emitted by the pool for increases to the number of observations that can be stored + /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index + /// just before a mint/swap/burn. + /// @param observationCardinalityNextOld The previous value of the next observation cardinality + /// @param observationCardinalityNextNew The updated value of the next observation cardinality + event IncreaseObservationCardinalityNext( + uint16 observationCardinalityNextOld, + uint16 observationCardinalityNextNew + ); + + /// @notice Emitted when the protocol fee is changed by the pool + /// @param feeProtocol0Old The previous value of the token0 protocol fee + /// @param feeProtocol1Old The previous value of the token1 protocol fee + /// @param feeProtocol0New The updated value of the token0 protocol fee + /// @param feeProtocol1New The updated value of the token1 protocol fee + event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); + + /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner + /// @param sender The address that collects the protocol fees + /// @param recipient The address that receives the collected protocol fees + /// @param amount0 The amount of token0 protocol fees that is withdrawn + /// @param amount0 The amount of token1 protocol fees that is withdrawn + event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol new file mode 100644 index 0000000000..c9beb151e3 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that never changes +/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values +interface IUniswapV3PoolImmutables { + /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface + /// @return The contract address + function factory() external view returns (address); + + /// @notice The first of the two tokens of the pool, sorted by address + /// @return The token contract address + function token0() external view returns (address); + + /// @notice The second of the two tokens of the pool, sorted by address + /// @return The token contract address + function token1() external view returns (address); + + /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 + /// @return The fee + function fee() external view returns (uint24); + + /// @notice The pool tick spacing + /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive + /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... + /// This value is an int24 to avoid casting even though it is always positive. + /// @return The tick spacing + function tickSpacing() external view returns (int24); + + /// @notice The maximum amount of position liquidity that can use any tick in the range + /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and + /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool + /// @return The max amount of liquidity per tick + function maxLiquidityPerTick() external view returns (uint128); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol new file mode 100644 index 0000000000..2395ed321d --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissioned pool actions +/// @notice Contains pool methods that may only be called by the factory owner +interface IUniswapV3PoolOwnerActions { + /// @notice Set the denominator of the protocol's % share of the fees + /// @param feeProtocol0 new protocol fee for token0 of the pool + /// @param feeProtocol1 new protocol fee for token1 of the pool + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; + + /// @notice Collect the protocol fee accrued to the pool + /// @param recipient The address to which collected protocol fees should be sent + /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 + /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 + /// @return amount0 The protocol fee collected in token0 + /// @return amount1 The protocol fee collected in token1 + function collectProtocol( + address recipient, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol new file mode 100644 index 0000000000..620256c311 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that can change +/// @notice These methods compose the pool's state, and can change with any frequency including multiple times +/// per transaction +interface IUniswapV3PoolState { + /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas + /// when accessed externally. + /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value + /// tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick + /// boundary. + /// observationIndex The index of the last oracle observation that was written, + /// observationCardinality The current maximum number of observations stored in the pool, + /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. + /// feeProtocol The protocol fee for both tokens of the pool. + /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 + /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. + /// unlocked Whether the pool is currently locked to reentrancy + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + + /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal0X128() external view returns (uint256); + + /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal1X128() external view returns (uint256); + + /// @notice The amounts of token0 and token1 that are owed to the protocol + /// @dev Protocol fees will never exceed uint128 max in either token + function protocolFees() external view returns (uint128 token0, uint128 token1); + + /// @notice The currently in range liquidity available to the pool + /// @dev This value has no relationship to the total liquidity across all ticks + function liquidity() external view returns (uint128); + + /// @notice Look up information about a specific tick in the pool + /// @param tick The tick to look up + /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or + /// tick upper, + /// liquidityNet how much liquidity changes when the pool price crosses the tick, + /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, + /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, + /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick + /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, + /// secondsOutside the seconds spent on the other side of the tick from the current tick, + /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. + /// In addition, these values are only relative and must be used only in comparison to previous snapshots for + /// a specific position. + function ticks(int24 tick) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + + /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information + function tickBitmap(int16 wordPosition) external view returns (uint256); + + /// @notice Returns the information about a position by the position's key + /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper + /// @return _liquidity The amount of liquidity in the position, + /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, + /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, + /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, + /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke + function positions(bytes32 key) + external + view + returns ( + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + /// @notice Returns data about a specific observation index + /// @param index The element of the observations array to fetch + /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time + /// ago, rather than at a specific index in the array. + /// @return blockTimestamp The timestamp of the observation, + /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, + /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, + /// Returns initialized whether the observation has been initialized and the values are safe to use + function observations(uint256 index) + external + view + returns ( + uint32 blockTimestamp, + int56 tickCumulative, + uint160 secondsPerLiquidityCumulativeX128, + bool initialized + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol new file mode 100644 index 0000000000..3a7216c7bd --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title BitMath +/// @dev This library provides functionality for computing bit properties of an unsigned integer +library BitMath { + /// @notice Returns the index of the most significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) + /// @param x the value for which to compute the most significant bit, must be greater than 0 + /// @return r the index of the most significant bit + function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + if (x >= 0x100000000000000000000000000000000) { + x >>= 128; + r += 128; + } + if (x >= 0x10000000000000000) { + x >>= 64; + r += 64; + } + if (x >= 0x100000000) { + x >>= 32; + r += 32; + } + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 0x4) { + x >>= 2; + r += 2; + } + if (x >= 0x2) r += 1; + } + + /// @notice Returns the index of the least significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) + /// @param x the value for which to compute the least significant bit, must be greater than 0 + /// @return r the index of the least significant bit + function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + r = 255; + if (x & type(uint128).max > 0) { + r -= 128; + } else { + x >>= 128; + } + if (x & type(uint64).max > 0) { + r -= 64; + } else { + x >>= 64; + } + if (x & type(uint32).max > 0) { + r -= 32; + } else { + x >>= 32; + } + if (x & type(uint16).max > 0) { + r -= 16; + } else { + x >>= 16; + } + if (x & type(uint8).max > 0) { + r -= 8; + } else { + x >>= 8; + } + if (x & 0xf > 0) { + r -= 4; + } else { + x >>= 4; + } + if (x & 0x3 > 0) { + r -= 2; + } else { + x >>= 2; + } + if (x & 0x1 > 0) r -= 1; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol new file mode 100644 index 0000000000..6d6948b106 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint128 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +library FixedPoint128 { + uint256 internal constant Q128 = 0x100000000000000000000000000000000; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol new file mode 100644 index 0000000000..63b42c294e --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint96 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +/// @dev Used in SqrtPriceMath.sol +library FixedPoint96 { + uint8 internal constant RESOLUTION = 96; + uint256 internal constant Q96 = 0x1000000000000000000000000; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol new file mode 100644 index 0000000000..8688a1773f --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.0; + +/// @title Contains 512-bit math functions +/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision +/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits +library FullMath { + /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + function mulDiv( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + // 512-bit multiply [prod1 prod0] = a * b + // Compute the product mod 2**256 and mod 2**256 - 1 + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0 + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0] + // Compute remainder using mulmod + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + // Subtract 256 bit number from 512 bit number + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator + // Compute largest power of two divisor of denominator. + // Always >= 1. + uint256 twos = -denominator & denominator; + // Divide denominator by power of two + assembly { + denominator := div(denominator, twos) + } + + // Divide [prod1 prod0] by the factors of two + assembly { + prod0 := div(prod0, twos) + } + // Shift in bits from prod1 into prod0. For this we need + // to flip `twos` such that it is 2**256 / twos. + // If twos is zero, then it becomes one + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + // Invert denominator mod 2**256 + // Now that denominator is an odd number, it has an inverse + // modulo 2**256 such that denominator * inv = 1 mod 2**256. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inv = (3 * denominator) ^ 2; + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv *= 2 - denominator * inv; // inverse mod 2**8 + inv *= 2 - denominator * inv; // inverse mod 2**16 + inv *= 2 - denominator * inv; // inverse mod 2**32 + inv *= 2 - denominator * inv; // inverse mod 2**64 + inv *= 2 - denominator * inv; // inverse mod 2**128 + inv *= 2 - denominator * inv; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying + // with the modular inverse of denominator. This will give us the + // correct result modulo 2**256. Since the precoditions guarantee + // that the outcome is less than 2**256, this is the final result. + // We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inv; + return result; + } + + /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + function mulDivRoundingUp( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max); + result++; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol new file mode 100644 index 0000000000..d5e23032e8 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math library for liquidity +library LiquidityMath { + /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows + /// @param x The liquidity before change + /// @param y The delta by which liquidity should be changed + /// @return z The liquidity delta + function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { + if (y < 0) { + require((z = x - uint128(-y)) < x, 'LS'); + } else { + require((z = x + uint128(y)) >= x, 'LA'); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol new file mode 100644 index 0000000000..dbc817c2ec --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.0; + +/// @title Optimized overflow and underflow safe math operations +/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost +library LowGasSafeMath { + /// @notice Returns x + y, reverts if sum overflows uint256 + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x); + } + + /// @notice Returns x - y, reverts if underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x); + } + + /// @notice Returns x * y, reverts if overflows + /// @param x The multiplicand + /// @param y The multiplier + /// @return z The product of x and y + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(x == 0 || (z = x * y) / x == y); + } + + /// @notice Returns x + y, reverts if overflows or underflows + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x + y) >= x == (y >= 0)); + } + + /// @notice Returns x - y, reverts if overflows or underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x - y) <= x == (y >= 0)); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol new file mode 100644 index 0000000000..3f6b3f32c9 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +/// @title Oracle +/// @notice Provides price and liquidity data useful for a wide variety of system designs +/// @dev Instances of stored oracle data, "observations", are collected in the oracle array +/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the +/// maximum length of the oracle array. New slots will be added when the array is fully populated. +/// Observations are overwritten when the full length of the oracle array is populated. +/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() +library Oracle { + struct Observation { + // the block timestamp of the observation + uint32 blockTimestamp; + // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized + int56 tickCumulative; + // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized + uint160 secondsPerLiquidityCumulativeX128; + // whether or not the observation is initialized + bool initialized; + } + + /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values + /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows + /// @param last The specified observation to be transformed + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @return Observation The newly populated observation + function transform( + Observation memory last, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity + ) private pure returns (Observation memory) { + uint32 delta = blockTimestamp - last.blockTimestamp; + return + Observation({ + blockTimestamp: blockTimestamp, + tickCumulative: last.tickCumulative + int56(tick) * delta, + secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + + ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), + initialized: true + }); + } + + /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array + /// @param self The stored oracle array + /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 + /// @return cardinality The number of populated elements in the oracle array + /// @return cardinalityNext The new length of the oracle array, independent of population + function initialize(Observation[65535] storage self, uint32 time) + internal + returns (uint16 cardinality, uint16 cardinalityNext) + { + self[0] = Observation({ + blockTimestamp: time, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + initialized: true + }); + return (1, 1); + } + + /// @notice Writes an oracle observation to the array + /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. + /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality + /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. + /// @param self The stored oracle array + /// @param index The index of the observation that was most recently written to the observations array + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @param cardinality The number of populated elements in the oracle array + /// @param cardinalityNext The new length of the oracle array, independent of population + /// @return indexUpdated The new index of the most recently written element in the oracle array + /// @return cardinalityUpdated The new cardinality of the oracle array + function write( + Observation[65535] storage self, + uint16 index, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity, + uint16 cardinality, + uint16 cardinalityNext + ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { + Observation memory last = self[index]; + + // early return if we've already written an observation this block + if (last.blockTimestamp == blockTimestamp) return (index, cardinality); + + // if the conditions are right, we can bump the cardinality + if (cardinalityNext > cardinality && index == (cardinality - 1)) { + cardinalityUpdated = cardinalityNext; + } else { + cardinalityUpdated = cardinality; + } + + indexUpdated = (index + 1) % cardinalityUpdated; + self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); + } + + /// @notice Prepares the oracle array to store up to `next` observations + /// @param self The stored oracle array + /// @param current The current next cardinality of the oracle array + /// @param next The proposed next cardinality which will be populated in the oracle array + /// @return next The next cardinality which will be populated in the oracle array + function grow( + Observation[65535] storage self, + uint16 current, + uint16 next + ) internal returns (uint16) { + require(current > 0, 'I'); + // no-op if the passed next value isn't greater than the current next value + if (next <= current) return current; + // store in each slot to prevent fresh SSTOREs in swaps + // this data will not be used because the initialized boolean is still false + for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; + return next; + } + + /// @notice comparator for 32-bit timestamps + /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time + /// @param time A timestamp truncated to 32 bits + /// @param a A comparison timestamp from which to determine the relative position of `time` + /// @param b From which to determine the relative position of `time` + /// @return bool Whether `a` is chronologically <= `b` + function lte( + uint32 time, + uint32 a, + uint32 b + ) private pure returns (bool) { + // if there hasn't been overflow, no need to adjust + if (a <= time && b <= time) return a <= b; + + uint256 aAdjusted = a > time ? a : a + 2**32; + uint256 bAdjusted = b > time ? b : b + 2**32; + + return aAdjusted <= bAdjusted; + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. + /// The result may be the same observation, or adjacent observations. + /// @dev The answer must be contained in the array, used when the target is located within the stored observation + /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param index The index of the observation that was most recently written to the observations array + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation recorded before, or at, the target + /// @return atOrAfter The observation recorded at, or after, the target + function binarySearch( + Observation[65535] storage self, + uint32 time, + uint32 target, + uint16 index, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + uint256 l = (index + 1) % cardinality; // oldest observation + uint256 r = l + cardinality - 1; // newest observation + uint256 i; + while (true) { + i = (l + r) / 2; + + beforeOrAt = self[i % cardinality]; + + // we've landed on an uninitialized tick, keep searching higher (more recently) + if (!beforeOrAt.initialized) { + l = i + 1; + continue; + } + + atOrAfter = self[(i + 1) % cardinality]; + + bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); + + // check if we've found the answer! + if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; + + if (!targetAtOrAfter) r = i - 1; + else l = i + 1; + } + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied + /// @dev Assumes there is at least 1 initialized observation. + /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param tick The active tick at the time of the returned or simulated observation + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The total pool liquidity at the time of the call + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation which occurred at, or before, the given timestamp + /// @return atOrAfter The observation which occurred at, or after, the given timestamp + function getSurroundingObservations( + Observation[65535] storage self, + uint32 time, + uint32 target, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + // optimistically set before to the newest observation + beforeOrAt = self[index]; + + // if the target is chronologically at or after the newest observation, we can early return + if (lte(time, beforeOrAt.blockTimestamp, target)) { + if (beforeOrAt.blockTimestamp == target) { + // if newest observation equals target, we're in the same block, so we can ignore atOrAfter + return (beforeOrAt, atOrAfter); + } else { + // otherwise, we need to transform + return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); + } + } + + // now, set before to the oldest observation + beforeOrAt = self[(index + 1) % cardinality]; + if (!beforeOrAt.initialized) beforeOrAt = self[0]; + + // ensure that the target is chronologically at or after the oldest observation + require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); + + // if we've reached this point, we have to binary search + return binarySearch(self, time, target, index, cardinality); + } + + /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. + /// 0 may be passed as `secondsAgo' to return the current cumulative values. + /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values + /// at exactly the timestamp between the two observations. + /// @param self The stored oracle array + /// @param time The current block timestamp + /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` + function observeSingle( + Observation[65535] storage self, + uint32 time, + uint32 secondsAgo, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { + if (secondsAgo == 0) { + Observation memory last = self[index]; + if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); + return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); + } + + uint32 target = time - secondsAgo; + + (Observation memory beforeOrAt, Observation memory atOrAfter) = + getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); + + if (target == beforeOrAt.blockTimestamp) { + // we're at the left boundary + return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); + } else if (target == atOrAfter.blockTimestamp) { + // we're at the right boundary + return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); + } else { + // we're in the middle + uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; + uint32 targetDelta = target - beforeOrAt.blockTimestamp; + return ( + beforeOrAt.tickCumulative + + ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * + targetDelta, + beforeOrAt.secondsPerLiquidityCumulativeX128 + + uint160( + (uint256( + atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 + ) * targetDelta) / observationTimeDelta + ) + ); + } + } + + /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` + /// @dev Reverts if `secondsAgos` > oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` + function observe( + Observation[65535] storage self, + uint32 time, + uint32[] memory secondsAgos, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { + require(cardinality > 0, 'I'); + + tickCumulatives = new int56[](secondsAgos.length); + secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); + for (uint256 i = 0; i < secondsAgos.length; i++) { + (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( + self, + time, + secondsAgos[i], + tick, + index, + liquidity, + cardinality + ); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol new file mode 100644 index 0000000000..1c67c7f279 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './FullMath.sol'; +import './FixedPoint128.sol'; +import './LiquidityMath.sol'; + +/// @title Position +/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary +/// @dev Positions store additional state for tracking fees owed to the position +library Position { + // info stored for each user's position + struct Info { + // the amount of liquidity owned by this position + uint128 liquidity; + // fee growth per unit of liquidity as of the last update to liquidity or fees owed + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + // the fees owed to the position owner in token0/token1 + uint128 tokensOwed0; + uint128 tokensOwed1; + } + + /// @notice Returns the Info struct of a position, given an owner and position boundaries + /// @param self The mapping containing all user positions + /// @param owner The address of the position owner + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @return position The position info struct of the given owners' position + function get( + mapping(bytes32 => Info) storage self, + address owner, + int24 tickLower, + int24 tickUpper + ) internal view returns (Position.Info storage position) { + position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; + } + + /// @notice Credits accumulated fees to a user's position + /// @param self The individual position to update + /// @param liquidityDelta The change in pool liquidity as a result of the position update + /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function update( + Info storage self, + int128 liquidityDelta, + uint256 feeGrowthInside0X128, + uint256 feeGrowthInside1X128 + ) internal { + Info memory _self = self; + + uint128 liquidityNext; + if (liquidityDelta == 0) { + require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions + liquidityNext = _self.liquidity; + } else { + liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); + } + + // calculate accumulated fees + uint128 tokensOwed0 = + uint128( + FullMath.mulDiv( + feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + uint128 tokensOwed1 = + uint128( + FullMath.mulDiv( + feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + + // update the position + if (liquidityDelta != 0) self.liquidity = liquidityNext; + self.feeGrowthInside0LastX128 = feeGrowthInside0X128; + self.feeGrowthInside1LastX128 = feeGrowthInside1X128; + if (tokensOwed0 > 0 || tokensOwed1 > 0) { + // overflow is acceptable, have to withdraw before you hit type(uint128).max fees + self.tokensOwed0 += tokensOwed0; + self.tokensOwed1 += tokensOwed1; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol new file mode 100644 index 0000000000..a8ea229878 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Safe casting methods +/// @notice Contains methods for safely casting between types +library SafeCast { + /// @notice Cast a uint256 to a uint160, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint160 + function toUint160(uint256 y) internal pure returns (uint160 z) { + require((z = uint160(y)) == y); + } + + /// @notice Cast a int256 to a int128, revert on overflow or underflow + /// @param y The int256 to be downcasted + /// @return z The downcasted integer, now type int128 + function toInt128(int256 y) internal pure returns (int128 z) { + require((z = int128(y)) == y); + } + + /// @notice Cast a uint256 to a int256, revert on overflow + /// @param y The uint256 to be casted + /// @return z The casted integer, now type int256 + function toInt256(uint256 y) internal pure returns (int256 z) { + require(y < 2**255); + z = int256(y); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol new file mode 100644 index 0000000000..685f485da4 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './FullMath.sol'; +import './UnsafeMath.sol'; +import './FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMath { + using LowGasSafeMath for uint256; + using SafeCast for uint256; + + /// @notice Gets the next sqrt price given a delta of token0 + /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the + /// price less in order to not send too much output. + /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), + /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). + /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token0 to add or remove from virtual reserves + /// @param add Whether to add or remove the amount of token0 + /// @return The price after adding or removing amount, depending on add + function getNextSqrtPriceFromAmount0RoundingUp( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price + if (amount == 0) return sqrtPX96; + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + + if (add) { + uint256 product; + if ((product = amount * sqrtPX96) / amount == sqrtPX96) { + uint256 denominator = numerator1 + product; + if (denominator >= numerator1) + // always fits in 160 bits + return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); + } + + return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); + } else { + uint256 product; + // if the product overflows, we know the denominator underflows + // in addition, we must check that the denominator does not underflow + require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); + uint256 denominator = numerator1 - product; + return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); + } + } + + /// @notice Gets the next sqrt price given a delta of token1 + /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the + /// price less in order to not send too much output. + /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity + /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token1 to add, or remove, from virtual reserves + /// @param add Whether to add, or remove, the amount of token1 + /// @return The price after adding or removing `amount` + function getNextSqrtPriceFromAmount1RoundingDown( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // if we're adding (subtracting), rounding down requires rounding the quotient down (up) + // in both cases, avoid a mulDiv for most inputs + if (add) { + uint256 quotient = + ( + amount <= type(uint160).max + ? (amount << FixedPoint96.RESOLUTION) / liquidity + : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) + ); + + return uint256(sqrtPX96).add(quotient).toUint160(); + } else { + uint256 quotient = + ( + amount <= type(uint160).max + ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) + : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) + ); + + require(sqrtPX96 > quotient); + // always fits 160 bits + return uint160(sqrtPX96 - quotient); + } + } + + /// @notice Gets the next sqrt price given an input amount of token0 or token1 + /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds + /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount + /// @param liquidity The amount of usable liquidity + /// @param amountIn How much of token0, or token1, is being swapped in + /// @param zeroForOne Whether the amount in is token0 or token1 + /// @return sqrtQX96 The price after adding the input amount to token0 or token1 + function getNextSqrtPriceFromInput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we don't pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) + : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); + } + + /// @notice Gets the next sqrt price given an output amount of token0 or token1 + /// @dev Throws if price or liquidity are 0 or the next price is out of bounds + /// @param sqrtPX96 The starting price before accounting for the output amount + /// @param liquidity The amount of usable liquidity + /// @param amountOut How much of token0, or token1, is being swapped out + /// @param zeroForOne Whether the amount out is token0 or token1 + /// @return sqrtQX96 The price after removing the output amount of token0 or token1 + function getNextSqrtPriceFromOutput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) + : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); + } + + /// @notice Gets the amount0 delta between two prices + /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), + /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up or down + /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + + require(sqrtRatioAX96 > 0); + + return + roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), + sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; + } + + /// @notice Gets the amount1 delta between two prices + /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up, or down + /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return + roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } + + /// @notice Helper that gets signed token0 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount0 delta + /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount0) { + return + liquidity < 0 + ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } + + /// @notice Helper that gets signed token1 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount1 delta + /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount1) { + return + liquidity < 0 + ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol new file mode 100644 index 0000000000..ee176fbee4 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './FullMath.sol'; +import './SqrtPriceMath.sol'; + +/// @title Computes the result of a swap within ticks +/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. +library SwapMath { + /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap + /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive + /// @param sqrtRatioCurrentX96 The current sqrt price of the pool + /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred + /// @param liquidity The usable liquidity + /// @param amountRemaining How much input or output amount is remaining to be swapped in/out + /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip + /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target + /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap + /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap + /// @return feeAmount The amount of input that will be taken as a fee + function computeSwapStep( + uint160 sqrtRatioCurrentX96, + uint160 sqrtRatioTargetX96, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) + internal + pure + returns ( + uint160 sqrtRatioNextX96, + uint256 amountIn, + uint256 amountOut, + uint256 feeAmount + ) + { + bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; + bool exactIn = amountRemaining >= 0; + + if (exactIn) { + uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); + if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( + sqrtRatioCurrentX96, + liquidity, + amountRemainingLessFee, + zeroForOne + ); + } else { + amountOut = zeroForOne + ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); + if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( + sqrtRatioCurrentX96, + liquidity, + uint256(-amountRemaining), + zeroForOne + ); + } + + bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; + + // get the input/output amounts + if (zeroForOne) { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); + } else { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); + } + + // cap the output amount to not exceed the remaining output amount + if (!exactIn && amountOut > uint256(-amountRemaining)) { + amountOut = uint256(-amountRemaining); + } + + if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { + // we didn't reach the target, so take the remainder of the maximum input as fee + feeAmount = uint256(amountRemaining) - amountIn; + } else { + feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol new file mode 100644 index 0000000000..13d3428494 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './TickMath.sol'; +import './LiquidityMath.sol'; + +/// @title Tick +/// @notice Contains functions for managing tick processes and relevant calculations +library Tick { + using LowGasSafeMath for int256; + using SafeCast for int256; + + // info stored for each initialized individual tick + struct Info { + // the total position liquidity that references this tick + uint128 liquidityGross; + // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), + int128 liquidityNet; + // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint256 feeGrowthOutside0X128; + uint256 feeGrowthOutside1X128; + // the cumulative tick value on the other side of the tick + int56 tickCumulativeOutside; + // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint160 secondsPerLiquidityOutsideX128; + // the seconds spent on the other side of the tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint32 secondsOutside; + // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 + // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks + bool initialized; + } + + /// @notice Derives max liquidity per tick from given tick spacing + /// @dev Executed within the pool constructor + /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` + /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... + /// @return The max liquidity per tick + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; + return type(uint128).max / numTicks; + } + + /// @notice Retrieves fee growth data + /// @param self The mapping containing all tick information for initialized ticks + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @param tickCurrent The current tick + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function getFeeGrowthInside( + mapping(int24 => Tick.Info) storage self, + int24 tickLower, + int24 tickUpper, + int24 tickCurrent, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128 + ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + Info storage lower = self[tickLower]; + Info storage upper = self[tickUpper]; + + // calculate fee growth below + uint256 feeGrowthBelow0X128; + uint256 feeGrowthBelow1X128; + if (tickCurrent >= tickLower) { + feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; + } else { + feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; + } + + // calculate fee growth above + uint256 feeGrowthAbove0X128; + uint256 feeGrowthAbove1X128; + if (tickCurrent < tickUpper) { + feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; + } else { + feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; + } + + feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; + } + + /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The tick that will be updated + /// @param tickCurrent The current tick + /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool + /// @param time The current block timestamp cast to a uint32 + /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick + /// @param maxLiquidity The maximum liquidity allocation for a single tick + /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa + function update( + mapping(int24 => Tick.Info) storage self, + int24 tick, + int24 tickCurrent, + int128 liquidityDelta, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time, + bool upper, + uint128 maxLiquidity + ) internal returns (bool flipped) { + Tick.Info storage info = self[tick]; + + uint128 liquidityGrossBefore = info.liquidityGross; + uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); + + require(liquidityGrossAfter <= maxLiquidity, 'LO'); + + flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); + + if (liquidityGrossBefore == 0) { + // by convention, we assume that all growth before a tick was initialized happened _below_ the tick + if (tick <= tickCurrent) { + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; + info.tickCumulativeOutside = tickCumulative; + info.secondsOutside = time; + } + info.initialized = true; + } + + info.liquidityGross = liquidityGrossAfter; + + // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) + info.liquidityNet = upper + ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() + : int256(info.liquidityNet).add(liquidityDelta).toInt128(); + } + + /// @notice Clears tick data + /// @param self The mapping containing all initialized tick information for initialized ticks + /// @param tick The tick that will be cleared + function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { + delete self[tick]; + } + + /// @notice Transitions to next tick as needed by price movement + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The destination tick of the transition + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity + /// @param time The current block.timestamp + /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) + function cross( + mapping(int24 => Tick.Info) storage self, + int24 tick, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time + ) internal returns (int128 liquidityNet) { + Tick.Info storage info = self[tick]; + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; + info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; + info.secondsOutside = time - info.secondsOutside; + liquidityNet = info.liquidityNet; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol new file mode 100644 index 0000000000..3c43585771 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './BitMath.sol'; + +/// @title Packed tick initialized state library +/// @notice Stores a packed mapping of tick index to its initialized state +/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. +library TickBitmap { + /// @notice Computes the position in the mapping where the initialized bit for a tick lives + /// @param tick The tick for which to compute the position + /// @return wordPos The key in the mapping containing the word in which the bit is stored + /// @return bitPos The bit position in the word where the flag is stored + function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { + wordPos = int16(tick >> 8); + bitPos = uint8(tick % 256); + } + + /// @notice Flips the initialized state for a given tick from false to true, or vice versa + /// @param self The mapping in which to flip the tick + /// @param tick The tick to flip + /// @param tickSpacing The spacing between usable ticks + function flipTick( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing + ) internal { + require(tick % tickSpacing == 0); // ensure that the tick is spaced + (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); + uint256 mask = 1 << bitPos; + self[wordPos] ^= mask; + } + + /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either + /// to the left (less than or equal to) or right (greater than) of the given tick + /// @param self The mapping in which to compute the next initialized tick + /// @param tick The starting tick + /// @param tickSpacing The spacing between usable ticks + /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) + /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick + /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks + function nextInitializedTickWithinOneWord( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing, + bool lte + ) internal view returns (int24 next, bool initialized) { + int24 compressed = tick / tickSpacing; + if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + + if (lte) { + (int16 wordPos, uint8 bitPos) = position(compressed); + // all the 1s at or to the right of the current bitPos + uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing + : (compressed - int24(bitPos)) * tickSpacing; + } else { + // start from the word of the next tick, since the current tick state doesn't matter + (int16 wordPos, uint8 bitPos) = position(compressed + 1); + // all the 1s at or to the left of the bitPos + uint256 mask = ~((1 << bitPos) - 1); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the left of the current tick, return leftmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing + : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol new file mode 100644 index 0000000000..378e44528c --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(MAX_TICK), 'T'); + + uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol new file mode 100644 index 0000000000..25d6309028 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import '../interfaces/IERC20Minimal.sol'; + +/// @title TransferHelper +/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false +library TransferHelper { + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Calls transfer on token contract, errors with TF if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer( + address token, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol new file mode 100644 index 0000000000..f62f84676f --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math functions that do not check inputs or outputs +/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks +library UnsafeMath { + /// @notice Returns ceil(x / y) + /// @dev division by 0 has unspecified behavior, and must be checked externally + /// @param x The dividend + /// @param y The divisor + /// @return z The quotient, ceil(x / y) + function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := add(div(x, y), gt(mod(x, y), 0)) + } + } +} diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 6bf5788db7..3ec47d1768 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -3,6 +3,7 @@ """ import json import argparse +from os import environ from crytic_compile import cryticparser @@ -84,6 +85,24 @@ def parse_args() -> argparse.Namespace: help="Toggle used to include values in output.", ) + parser.add_argument( + "--table-storage-layout", + action="store_true", + help="Print table view of storage layout", + ) + + parser.add_argument( + "--table-storage-value", + action="store_true", + help="Print table view of storage layout & values", + ) + + parser.add_argument( + "--silent", + action="store_true", + help="Silence log outputs", + ) + parser.add_argument("--max-depth", help="Max depth to search in data structure.", default=20) cryticparser.init(parser) @@ -120,6 +139,25 @@ def main() -> None: srs.rpc = args.rpc_url + if args.silent: + environ["SILENT"] = "1" + + if args.table_storage_layout: + environ["TABLE"] = "1" + srs.get_all_storage_variables() + srs.get_storage_layout() + srs.print_table() + return + + if args.table_storage_value: + assert args.rpc_url + environ["TABLE"] = "1" + environ["TABLE_VALUE"] = "1" + srs.get_all_storage_variables() + srs.get_storage_layout() + srs.print_table() + return + if args.layout: srs.get_all_storage_variables() srs.get_storage_layout() diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index dbdda7183d..9b0fe5a744 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -1,6 +1,7 @@ import sys import logging from math import floor +from os import environ from typing import Callable, Optional, Tuple, Union, List, Dict @@ -31,6 +32,13 @@ print("$ pip3 install web3 --user\n") sys.exit(-1) +try: + from tabulate import tabulate +except ImportError: + print("ERROR: in order to use slither-read-storage --table, you need to install tabulate") + print("$ pip3 install tabulate --user\n") + sys.exit(-1) + from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType from slither.core.declarations import Contract, StructureContract @@ -182,14 +190,24 @@ def get_storage_slot( int_slot = int.from_bytes(slot, byteorder="big") self.log += f"\nName: {var_log_name}\nType: {type_to}\nSlot: {int_slot}\n" - logger.info(self.log) + if environ.get("SILENT") is None: + logger.info(self.log) self.log = "" - return { - "type_string": type_to, - "slot": int_slot, - "size": size, - "offset": offset, - } + if environ.get("TABLE") is None: + return { + "type_string": type_to, + "slot": int_slot, + "size": size, + "offset": offset, + } + else: + return { + "type_string": type_to, + "slot": int_slot, + "size": size, + "offset": offset, + "struct_var": struct_var, + } def get_target_variables(self, **kwargs) -> None: """ @@ -241,6 +259,86 @@ def get_all_storage_variables(self, func: Callable = None) -> None: ) ) + def print_table(self) -> None: + + if environ.get("TABLE_VALUE") is None: + tabulate_headers = ['slot', 'offset', 'size', 'type', 'name'] + else: + tabulate_headers = ['slot', 'offset', 'size', 'type', 'name', 'value'] + print("Processing, grabbing values from rpc endpoint...") + + tabulate_data = [] + + for contract, state_var in self.target_variables: + type_ = state_var.type + + var = state_var.name + info = self.slot_info[var] + + slot = info.get('slot') + offset = info.get('offset') + size = info.get('size') + type_string = info.get('type_string') + struct_var = info.get('struct_var') + + + if environ.get("TABLE_VALUE") is None: + tabulate_data.append([slot, offset, size, type_string, var]) + else: + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + value = self.convert_value_to_type( + hex_bytes, size, offset, type_string + ) + tabulate_data.append([slot, offset, size, type_string, var, value]) + + + if is_user_defined_type(type_) and is_struct(type_.type): + tabulate_data.pop() + for item in info["elems"]: + slot = info["elems"][item].get('slot') + offset = info["elems"][item].get('offset') + size = info["elems"][item].get('size') + type_string = info["elems"][item].get('type_string') + struct_var = info["elems"][item].get('struct_var') + + # doesn't handle deep keys currently + var_name_struct_or_array_var = "{} -> {}".format(var, struct_var) + + + if environ.get("TABLE_VALUE") is None: + tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) + else: + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + value = self.convert_value_to_type( + hex_bytes, size, offset, type_string + ) + tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var, value]) + + if is_array(type_): + tabulate_data.pop() + for item in info["elems"]: + for key in info["elems"][item]: + slot = info["elems"][item][key].get('slot') + offset = info["elems"][item][key].get('offset') + size = info["elems"][item][key].get('size') + type_string = info["elems"][item][key].get('type_string') + struct_var = info["elems"][item][key].get('struct_var') + + # doesn't handle deep keys currently + var_name_struct_or_array_var = "{} -> {}".format(var, struct_var) + + if environ.get("TABLE_VALUE") is None: + tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) + else: + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + value = self.convert_value_to_type( + hex_bytes, size, offset, type_string + ) + tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var, value]) + + + print(tabulate(tabulate_data, headers=tabulate_headers, tablefmt='grid')) + @staticmethod def _find_struct_var_slot( elems: List[StructureVariable], slot: bytes, struct_var: str @@ -456,9 +554,10 @@ def get_variable_info( size = byte_size * 8 # bits (int_slot, offset) = contract.compilation_unit.storage_layout_of(contract, target_variable) offset *= 8 # bits - logger.info( - f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" - ) + if environ.get("SILENT") is None: + logger.info( + f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" + ) return int_slot, size, offset, type_to From 2fbc8d2a9a0159b057f2b588712f323c33baf2cf Mon Sep 17 00:00:00 2001 From: noxx Date: Thu, 14 Jul 2022 18:17:25 +0100 Subject: [PATCH 03/23] remove crytic-export --- .../contracts/NoDelegateCall.sol | 27 - .../contracts/UniswapV3Pool.sol | 869 ------------------ .../contracts/interfaces/IERC20Minimal.sol | 52 -- .../interfaces/IUniswapV3Factory.sol | 78 -- .../contracts/interfaces/IUniswapV3Pool.sol | 24 - .../interfaces/IUniswapV3PoolDeployer.sol | 26 - .../callback/IUniswapV3FlashCallback.sol | 18 - .../callback/IUniswapV3MintCallback.sol | 18 - .../callback/IUniswapV3SwapCallback.sol | 21 - .../interfaces/pool/IUniswapV3PoolActions.sol | 103 --- .../pool/IUniswapV3PoolDerivedState.sol | 40 - .../interfaces/pool/IUniswapV3PoolEvents.sol | 121 --- .../pool/IUniswapV3PoolImmutables.sol | 35 - .../pool/IUniswapV3PoolOwnerActions.sol | 23 - .../interfaces/pool/IUniswapV3PoolState.sol | 116 --- .../contracts/libraries/BitMath.sol | 94 -- .../contracts/libraries/FixedPoint128.sol | 8 - .../contracts/libraries/FixedPoint96.sol | 10 - .../contracts/libraries/FullMath.sol | 124 --- .../contracts/libraries/LiquidityMath.sol | 17 - .../contracts/libraries/LowGasSafeMath.sol | 46 - .../contracts/libraries/Oracle.sol | 325 ------- .../contracts/libraries/Position.sol | 88 -- .../contracts/libraries/SafeCast.sol | 28 - .../contracts/libraries/SqrtPriceMath.sol | 227 ----- .../contracts/libraries/SwapMath.sol | 98 -- .../contracts/libraries/Tick.sol | 183 ---- .../contracts/libraries/TickBitmap.sol | 78 -- .../contracts/libraries/TickMath.sol | 205 ----- .../contracts/libraries/TransferHelper.sol | 23 - .../contracts/libraries/UnsafeMath.sol | 17 - 31 files changed, 3142 deletions(-) delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol deleted file mode 100644 index 5411979dcb..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.7.6; - -/// @title Prevents delegatecall to a contract -/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract -abstract contract NoDelegateCall { - /// @dev The original address of this contract - address private immutable original; - - constructor() { - // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. - // In other words, this variable won't change when it's checked at runtime. - original = address(this); - } - - /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, - /// and the use of immutable means the address bytes are copied in every place the modifier is used. - function checkNotDelegateCall() private view { - require(address(this) == original); - } - - /// @notice Prevents delegatecall into the modified method - modifier noDelegateCall() { - checkNotDelegateCall(); - _; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol deleted file mode 100644 index 9e09821272..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol +++ /dev/null @@ -1,869 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.7.6; - -import './interfaces/IUniswapV3Pool.sol'; - -import './NoDelegateCall.sol'; - -import './libraries/LowGasSafeMath.sol'; -import './libraries/SafeCast.sol'; -import './libraries/Tick.sol'; -import './libraries/TickBitmap.sol'; -import './libraries/Position.sol'; -import './libraries/Oracle.sol'; - -import './libraries/FullMath.sol'; -import './libraries/FixedPoint128.sol'; -import './libraries/TransferHelper.sol'; -import './libraries/TickMath.sol'; -import './libraries/LiquidityMath.sol'; -import './libraries/SqrtPriceMath.sol'; -import './libraries/SwapMath.sol'; - -import './interfaces/IUniswapV3PoolDeployer.sol'; -import './interfaces/IUniswapV3Factory.sol'; -import './interfaces/IERC20Minimal.sol'; -import './interfaces/callback/IUniswapV3MintCallback.sol'; -import './interfaces/callback/IUniswapV3SwapCallback.sol'; -import './interfaces/callback/IUniswapV3FlashCallback.sol'; - -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); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol deleted file mode 100644 index c303265a3b..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Minimal ERC20 interface for Uniswap -/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 -interface IERC20Minimal { - /// @notice Returns the balance of a token - /// @param account The account for which to look up the number of tokens it has, i.e. its balance - /// @return The number of tokens held by the account - function balanceOf(address account) external view returns (uint256); - - /// @notice Transfers the amount of token from the `msg.sender` to the recipient - /// @param recipient The account that will receive the amount transferred - /// @param amount The number of tokens to send from the sender to the recipient - /// @return Returns true for a successful transfer, false for an unsuccessful transfer - function transfer(address recipient, uint256 amount) external returns (bool); - - /// @notice Returns the current allowance given to a spender by an owner - /// @param owner The account of the token owner - /// @param spender The account of the token spender - /// @return The current allowance granted by `owner` to `spender` - function allowance(address owner, address spender) external view returns (uint256); - - /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` - /// @param spender The account which will be allowed to spend a given amount of the owners tokens - /// @param amount The amount of tokens allowed to be used by `spender` - /// @return Returns true for a successful approval, false for unsuccessful - function approve(address spender, uint256 amount) external returns (bool); - - /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` - /// @param sender The account from which the transfer will be initiated - /// @param recipient The recipient of the transfer - /// @param amount The amount of the transfer - /// @return Returns true for a successful transfer, false for unsuccessful - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); - - /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. - /// @param from The account from which the tokens were sent, i.e. the balance decreased - /// @param to The account to which the tokens were sent, i.e. the balance increased - /// @param value The amount of tokens that were transferred - event Transfer(address indexed from, address indexed to, uint256 value); - - /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. - /// @param owner The account that approved spending of its tokens - /// @param spender The account for which the spending allowance was modified - /// @param value The new allowance from the owner to the spender - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol deleted file mode 100644 index 540cfdc681..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title The interface for the Uniswap V3 Factory -/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees -interface IUniswapV3Factory { - /// @notice Emitted when the owner of the factory is changed - /// @param oldOwner The owner before the owner was changed - /// @param newOwner The owner after the owner was changed - event OwnerChanged(address indexed oldOwner, address indexed newOwner); - - /// @notice Emitted when a pool is created - /// @param token0 The first token of the pool by address sort order - /// @param token1 The second token of the pool by address sort order - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @param tickSpacing The minimum number of ticks between initialized ticks - /// @param pool The address of the created pool - event PoolCreated( - address indexed token0, - address indexed token1, - uint24 indexed fee, - int24 tickSpacing, - address pool - ); - - /// @notice Emitted when a new fee amount is enabled for pool creation via the factory - /// @param fee The enabled fee, denominated in hundredths of a bip - /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee - event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); - - /// @notice Returns the current owner of the factory - /// @dev Can be changed by the current owner via setOwner - /// @return The address of the factory owner - function owner() external view returns (address); - - /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled - /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context - /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee - /// @return The tick spacing - function feeAmountTickSpacing(uint24 fee) external view returns (int24); - - /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist - /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order - /// @param tokenA The contract address of either token0 or token1 - /// @param tokenB The contract address of the other token - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @return pool The pool address - function getPool( - address tokenA, - address tokenB, - uint24 fee - ) external view returns (address pool); - - /// @notice Creates a pool for the given two tokens and fee - /// @param tokenA One of the two tokens in the desired pool - /// @param tokenB The other of the two tokens in the desired pool - /// @param fee The desired fee for the pool - /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved - /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments - /// are invalid. - /// @return pool The address of the newly created pool - function createPool( - address tokenA, - address tokenB, - uint24 fee - ) external returns (address pool); - - /// @notice Updates the owner of the factory - /// @dev Must be called by the current owner - /// @param _owner The new owner of the factory - function setOwner(address _owner) external; - - /// @notice Enables a fee amount with the given tickSpacing - /// @dev Fee amounts may never be removed once enabled - /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) - /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount - function enableFeeAmount(uint24 fee, int24 tickSpacing) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol deleted file mode 100644 index 56df0500df..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -import './pool/IUniswapV3PoolImmutables.sol'; -import './pool/IUniswapV3PoolState.sol'; -import './pool/IUniswapV3PoolDerivedState.sol'; -import './pool/IUniswapV3PoolActions.sol'; -import './pool/IUniswapV3PoolOwnerActions.sol'; -import './pool/IUniswapV3PoolEvents.sol'; - -/// @title The interface for a Uniswap V3 Pool -/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform -/// to the ERC20 specification -/// @dev The pool interface is broken up into many smaller pieces -interface IUniswapV3Pool is - IUniswapV3PoolImmutables, - IUniswapV3PoolState, - IUniswapV3PoolDerivedState, - IUniswapV3PoolActions, - IUniswapV3PoolOwnerActions, - IUniswapV3PoolEvents -{ - -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol deleted file mode 100644 index 72096c1ff5..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools -/// @notice A contract that constructs a pool must implement this to pass arguments to the pool -/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash -/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain -interface IUniswapV3PoolDeployer { - /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. - /// @dev Called by the pool constructor to fetch the parameters of the pool - /// Returns factory The factory address - /// Returns token0 The first token of the pool by address sort order - /// Returns token1 The second token of the pool by address sort order - /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// Returns tickSpacing The minimum number of ticks between initialized ticks - function parameters() - external - view - returns ( - address factory, - address token0, - address token1, - uint24 fee, - int24 tickSpacing - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol deleted file mode 100644 index 18e54c4e12..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#flash -/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface -interface IUniswapV3FlashCallback { - /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. - /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// @param fee0 The fee amount in token0 due to the pool by the end of the flash - /// @param fee1 The fee amount in token1 due to the pool by the end of the flash - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call - function uniswapV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol deleted file mode 100644 index 85447e84f9..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#mint -/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface -interface IUniswapV3MintCallback { - /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. - /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity - /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call - function uniswapV3MintCallback( - uint256 amount0Owed, - uint256 amount1Owed, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol deleted file mode 100644 index 9f183b22a7..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#swap -/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface -interface IUniswapV3SwapCallback { - /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. - /// @dev In the implementation you must pay the pool tokens owed for the swap. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. - /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by - /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. - /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by - /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call - function uniswapV3SwapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol deleted file mode 100644 index 44fb61c24a..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Permissionless pool actions -/// @notice Contains pool methods that can be called by anyone -interface IUniswapV3PoolActions { - /// @notice Sets the initial price for the pool - /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value - /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 - function initialize(uint160 sqrtPriceX96) external; - - /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position - /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback - /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends - /// on tickLower, tickUpper, the amount of liquidity, and the current price. - /// @param recipient The address for which the liquidity will be created - /// @param tickLower The lower tick of the position in which to add liquidity - /// @param tickUpper The upper tick of the position in which to add liquidity - /// @param amount The amount of liquidity to mint - /// @param data Any data that should be passed through to the callback - /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback - /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback - function mint( - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount, - bytes calldata data - ) external returns (uint256 amount0, uint256 amount1); - - /// @notice Collects tokens owed to a position - /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. - /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or - /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the - /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. - /// @param recipient The address which should receive the fees collected - /// @param tickLower The lower tick of the position for which to collect fees - /// @param tickUpper The upper tick of the position for which to collect fees - /// @param amount0Requested How much token0 should be withdrawn from the fees owed - /// @param amount1Requested How much token1 should be withdrawn from the fees owed - /// @return amount0 The amount of fees collected in token0 - /// @return amount1 The amount of fees collected in token1 - function collect( - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount0Requested, - uint128 amount1Requested - ) external returns (uint128 amount0, uint128 amount1); - - /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position - /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 - /// @dev Fees must be collected separately via a call to #collect - /// @param tickLower The lower tick of the position for which to burn liquidity - /// @param tickUpper The upper tick of the position for which to burn liquidity - /// @param amount How much liquidity to burn - /// @return amount0 The amount of token0 sent to the recipient - /// @return amount1 The amount of token1 sent to the recipient - function burn( - int24 tickLower, - int24 tickUpper, - uint128 amount - ) external returns (uint256 amount0, uint256 amount1); - - /// @notice Swap token0 for token1, or token1 for token0 - /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback - /// @param recipient The address to receive the output of the swap - /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 - /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) - /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this - /// value after the swap. If one for zero, the price cannot be greater than this value after the swap - /// @param data Any data to be passed through to the callback - /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive - /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive - function swap( - address recipient, - bool zeroForOne, - int256 amountSpecified, - uint160 sqrtPriceLimitX96, - bytes calldata data - ) external returns (int256 amount0, int256 amount1); - - /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback - /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback - /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling - /// with 0 amount{0,1} and sending the donation amount(s) from the callback - /// @param recipient The address which will receive the token0 and token1 amounts - /// @param amount0 The amount of token0 to send - /// @param amount1 The amount of token1 to send - /// @param data Any data to be passed through to the callback - function flash( - address recipient, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) external; - - /// @notice Increase the maximum number of price and liquidity observations that this pool will store - /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to - /// the input observationCardinalityNext. - /// @param observationCardinalityNext The desired minimum number of observations for the pool to store - function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol deleted file mode 100644 index eda3a0089d..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that is not stored -/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the -/// blockchain. The functions here may have variable gas costs. -interface IUniswapV3PoolDerivedState { - /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp - /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing - /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, - /// you must call it with secondsAgos = [3600, 0]. - /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in - /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. - /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned - /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp - /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block - /// timestamp - function observe(uint32[] calldata secondsAgos) - external - view - returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); - - /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range - /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. - /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first - /// snapshot is taken and the second snapshot is taken. - /// @param tickLower The lower tick of the range - /// @param tickUpper The upper tick of the range - /// @return tickCumulativeInside The snapshot of the tick accumulator for the range - /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range - /// @return secondsInside The snapshot of seconds per liquidity for the range - function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) - external - view - returns ( - int56 tickCumulativeInside, - uint160 secondsPerLiquidityInsideX128, - uint32 secondsInside - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol deleted file mode 100644 index 9d915dde93..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Events emitted by a pool -/// @notice Contains all events emitted by the pool -interface IUniswapV3PoolEvents { - /// @notice Emitted exactly once by a pool when #initialize is first called on the pool - /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize - /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 - /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool - event Initialize(uint160 sqrtPriceX96, int24 tick); - - /// @notice Emitted when liquidity is minted for a given position - /// @param sender The address that minted the liquidity - /// @param owner The owner of the position and recipient of any minted liquidity - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount The amount of liquidity minted to the position range - /// @param amount0 How much token0 was required for the minted liquidity - /// @param amount1 How much token1 was required for the minted liquidity - event Mint( - address sender, - address indexed owner, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount, - uint256 amount0, - uint256 amount1 - ); - - /// @notice Emitted when fees are collected by the owner of a position - /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees - /// @param owner The owner of the position for which fees are collected - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount0 The amount of token0 fees collected - /// @param amount1 The amount of token1 fees collected - event Collect( - address indexed owner, - address recipient, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount0, - uint128 amount1 - ); - - /// @notice Emitted when a position's liquidity is removed - /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect - /// @param owner The owner of the position for which liquidity is removed - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount The amount of liquidity to remove - /// @param amount0 The amount of token0 withdrawn - /// @param amount1 The amount of token1 withdrawn - event Burn( - address indexed owner, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount, - uint256 amount0, - uint256 amount1 - ); - - /// @notice Emitted by the pool for any swaps between token0 and token1 - /// @param sender The address that initiated the swap call, and that received the callback - /// @param recipient The address that received the output of the swap - /// @param amount0 The delta of the token0 balance of the pool - /// @param amount1 The delta of the token1 balance of the pool - /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 - /// @param liquidity The liquidity of the pool after the swap - /// @param tick The log base 1.0001 of price of the pool after the swap - event Swap( - address indexed sender, - address indexed recipient, - int256 amount0, - int256 amount1, - uint160 sqrtPriceX96, - uint128 liquidity, - int24 tick - ); - - /// @notice Emitted by the pool for any flashes of token0/token1 - /// @param sender The address that initiated the swap call, and that received the callback - /// @param recipient The address that received the tokens from flash - /// @param amount0 The amount of token0 that was flashed - /// @param amount1 The amount of token1 that was flashed - /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee - /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee - event Flash( - address indexed sender, - address indexed recipient, - uint256 amount0, - uint256 amount1, - uint256 paid0, - uint256 paid1 - ); - - /// @notice Emitted by the pool for increases to the number of observations that can be stored - /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index - /// just before a mint/swap/burn. - /// @param observationCardinalityNextOld The previous value of the next observation cardinality - /// @param observationCardinalityNextNew The updated value of the next observation cardinality - event IncreaseObservationCardinalityNext( - uint16 observationCardinalityNextOld, - uint16 observationCardinalityNextNew - ); - - /// @notice Emitted when the protocol fee is changed by the pool - /// @param feeProtocol0Old The previous value of the token0 protocol fee - /// @param feeProtocol1Old The previous value of the token1 protocol fee - /// @param feeProtocol0New The updated value of the token0 protocol fee - /// @param feeProtocol1New The updated value of the token1 protocol fee - event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); - - /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner - /// @param sender The address that collects the protocol fees - /// @param recipient The address that receives the collected protocol fees - /// @param amount0 The amount of token0 protocol fees that is withdrawn - /// @param amount0 The amount of token1 protocol fees that is withdrawn - event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol deleted file mode 100644 index c9beb151e3..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that never changes -/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values -interface IUniswapV3PoolImmutables { - /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface - /// @return The contract address - function factory() external view returns (address); - - /// @notice The first of the two tokens of the pool, sorted by address - /// @return The token contract address - function token0() external view returns (address); - - /// @notice The second of the two tokens of the pool, sorted by address - /// @return The token contract address - function token1() external view returns (address); - - /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 - /// @return The fee - function fee() external view returns (uint24); - - /// @notice The pool tick spacing - /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive - /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... - /// This value is an int24 to avoid casting even though it is always positive. - /// @return The tick spacing - function tickSpacing() external view returns (int24); - - /// @notice The maximum amount of position liquidity that can use any tick in the range - /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and - /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool - /// @return The max amount of liquidity per tick - function maxLiquidityPerTick() external view returns (uint128); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol deleted file mode 100644 index 2395ed321d..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Permissioned pool actions -/// @notice Contains pool methods that may only be called by the factory owner -interface IUniswapV3PoolOwnerActions { - /// @notice Set the denominator of the protocol's % share of the fees - /// @param feeProtocol0 new protocol fee for token0 of the pool - /// @param feeProtocol1 new protocol fee for token1 of the pool - function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; - - /// @notice Collect the protocol fee accrued to the pool - /// @param recipient The address to which collected protocol fees should be sent - /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 - /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 - /// @return amount0 The protocol fee collected in token0 - /// @return amount1 The protocol fee collected in token1 - function collectProtocol( - address recipient, - uint128 amount0Requested, - uint128 amount1Requested - ) external returns (uint128 amount0, uint128 amount1); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol deleted file mode 100644 index 620256c311..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that can change -/// @notice These methods compose the pool's state, and can change with any frequency including multiple times -/// per transaction -interface IUniswapV3PoolState { - /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas - /// when accessed externally. - /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value - /// tick The current tick of the pool, i.e. according to the last tick transition that was run. - /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick - /// boundary. - /// observationIndex The index of the last oracle observation that was written, - /// observationCardinality The current maximum number of observations stored in the pool, - /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. - /// feeProtocol The protocol fee for both tokens of the pool. - /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 - /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. - /// unlocked Whether the pool is currently locked to reentrancy - function slot0() - external - view - returns ( - uint160 sqrtPriceX96, - int24 tick, - uint16 observationIndex, - uint16 observationCardinality, - uint16 observationCardinalityNext, - uint8 feeProtocol, - bool unlocked - ); - - /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool - /// @dev This value can overflow the uint256 - function feeGrowthGlobal0X128() external view returns (uint256); - - /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool - /// @dev This value can overflow the uint256 - function feeGrowthGlobal1X128() external view returns (uint256); - - /// @notice The amounts of token0 and token1 that are owed to the protocol - /// @dev Protocol fees will never exceed uint128 max in either token - function protocolFees() external view returns (uint128 token0, uint128 token1); - - /// @notice The currently in range liquidity available to the pool - /// @dev This value has no relationship to the total liquidity across all ticks - function liquidity() external view returns (uint128); - - /// @notice Look up information about a specific tick in the pool - /// @param tick The tick to look up - /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or - /// tick upper, - /// liquidityNet how much liquidity changes when the pool price crosses the tick, - /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, - /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, - /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick - /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, - /// secondsOutside the seconds spent on the other side of the tick from the current tick, - /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. - /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. - /// In addition, these values are only relative and must be used only in comparison to previous snapshots for - /// a specific position. - function ticks(int24 tick) - external - view - returns ( - uint128 liquidityGross, - int128 liquidityNet, - uint256 feeGrowthOutside0X128, - uint256 feeGrowthOutside1X128, - int56 tickCumulativeOutside, - uint160 secondsPerLiquidityOutsideX128, - uint32 secondsOutside, - bool initialized - ); - - /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information - function tickBitmap(int16 wordPosition) external view returns (uint256); - - /// @notice Returns the information about a position by the position's key - /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper - /// @return _liquidity The amount of liquidity in the position, - /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, - /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, - /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, - /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke - function positions(bytes32 key) - external - view - returns ( - uint128 _liquidity, - uint256 feeGrowthInside0LastX128, - uint256 feeGrowthInside1LastX128, - uint128 tokensOwed0, - uint128 tokensOwed1 - ); - - /// @notice Returns data about a specific observation index - /// @param index The element of the observations array to fetch - /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time - /// ago, rather than at a specific index in the array. - /// @return blockTimestamp The timestamp of the observation, - /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, - /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, - /// Returns initialized whether the observation has been initialized and the values are safe to use - function observations(uint256 index) - external - view - returns ( - uint32 blockTimestamp, - int56 tickCumulative, - uint160 secondsPerLiquidityCumulativeX128, - bool initialized - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol deleted file mode 100644 index 3a7216c7bd..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title BitMath -/// @dev This library provides functionality for computing bit properties of an unsigned integer -library BitMath { - /// @notice Returns the index of the most significant bit of the number, - /// where the least significant bit is at index 0 and the most significant bit is at index 255 - /// @dev The function satisfies the property: - /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) - /// @param x the value for which to compute the most significant bit, must be greater than 0 - /// @return r the index of the most significant bit - function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { - require(x > 0); - - if (x >= 0x100000000000000000000000000000000) { - x >>= 128; - r += 128; - } - if (x >= 0x10000000000000000) { - x >>= 64; - r += 64; - } - if (x >= 0x100000000) { - x >>= 32; - r += 32; - } - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 0x4) { - x >>= 2; - r += 2; - } - if (x >= 0x2) r += 1; - } - - /// @notice Returns the index of the least significant bit of the number, - /// where the least significant bit is at index 0 and the most significant bit is at index 255 - /// @dev The function satisfies the property: - /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) - /// @param x the value for which to compute the least significant bit, must be greater than 0 - /// @return r the index of the least significant bit - function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { - require(x > 0); - - r = 255; - if (x & type(uint128).max > 0) { - r -= 128; - } else { - x >>= 128; - } - if (x & type(uint64).max > 0) { - r -= 64; - } else { - x >>= 64; - } - if (x & type(uint32).max > 0) { - r -= 32; - } else { - x >>= 32; - } - if (x & type(uint16).max > 0) { - r -= 16; - } else { - x >>= 16; - } - if (x & type(uint8).max > 0) { - r -= 8; - } else { - x >>= 8; - } - if (x & 0xf > 0) { - r -= 4; - } else { - x >>= 4; - } - if (x & 0x3 > 0) { - r -= 2; - } else { - x >>= 2; - } - if (x & 0x1 > 0) r -= 1; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol deleted file mode 100644 index 6d6948b106..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.4.0; - -/// @title FixedPoint128 -/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) -library FixedPoint128 { - uint256 internal constant Q128 = 0x100000000000000000000000000000000; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol deleted file mode 100644 index 63b42c294e..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.4.0; - -/// @title FixedPoint96 -/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) -/// @dev Used in SqrtPriceMath.sol -library FixedPoint96 { - uint8 internal constant RESOLUTION = 96; - uint256 internal constant Q96 = 0x1000000000000000000000000; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol deleted file mode 100644 index 8688a1773f..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.0; - -/// @title Contains 512-bit math functions -/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision -/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits -library FullMath { - /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - /// @param a The multiplicand - /// @param b The multiplier - /// @param denominator The divisor - /// @return result The 256-bit result - /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv - function mulDiv( - uint256 a, - uint256 b, - uint256 denominator - ) internal pure returns (uint256 result) { - // 512-bit multiply [prod1 prod0] = a * b - // Compute the product mod 2**256 and mod 2**256 - 1 - // then use the Chinese Remainder Theorem to reconstruct - // the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2**256 + prod0 - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(a, b, not(0)) - prod0 := mul(a, b) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - - // Handle non-overflow cases, 256 by 256 division - if (prod1 == 0) { - require(denominator > 0); - assembly { - result := div(prod0, denominator) - } - return result; - } - - // Make sure the result is less than 2**256. - // Also prevents denominator == 0 - require(denominator > prod1); - - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// - - // Make division exact by subtracting the remainder from [prod1 prod0] - // Compute remainder using mulmod - uint256 remainder; - assembly { - remainder := mulmod(a, b, denominator) - } - // Subtract 256 bit number from 512 bit number - assembly { - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) - } - - // Factor powers of two out of denominator - // Compute largest power of two divisor of denominator. - // Always >= 1. - uint256 twos = -denominator & denominator; - // Divide denominator by power of two - assembly { - denominator := div(denominator, twos) - } - - // Divide [prod1 prod0] by the factors of two - assembly { - prod0 := div(prod0, twos) - } - // Shift in bits from prod1 into prod0. For this we need - // to flip `twos` such that it is 2**256 / twos. - // If twos is zero, then it becomes one - assembly { - twos := add(div(sub(0, twos), twos), 1) - } - prod0 |= prod1 * twos; - - // Invert denominator mod 2**256 - // Now that denominator is an odd number, it has an inverse - // modulo 2**256 such that denominator * inv = 1 mod 2**256. - // Compute the inverse by starting with a seed that is correct - // correct for four bits. That is, denominator * inv = 1 mod 2**4 - uint256 inv = (3 * denominator) ^ 2; - // Now use Newton-Raphson iteration to improve the precision. - // Thanks to Hensel's lifting lemma, this also works in modular - // arithmetic, doubling the correct bits in each step. - inv *= 2 - denominator * inv; // inverse mod 2**8 - inv *= 2 - denominator * inv; // inverse mod 2**16 - inv *= 2 - denominator * inv; // inverse mod 2**32 - inv *= 2 - denominator * inv; // inverse mod 2**64 - inv *= 2 - denominator * inv; // inverse mod 2**128 - inv *= 2 - denominator * inv; // inverse mod 2**256 - - // Because the division is now exact we can divide by multiplying - // with the modular inverse of denominator. This will give us the - // correct result modulo 2**256. Since the precoditions guarantee - // that the outcome is less than 2**256, this is the final result. - // We don't need to compute the high bits of the result and prod1 - // is no longer required. - result = prod0 * inv; - return result; - } - - /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - /// @param a The multiplicand - /// @param b The multiplier - /// @param denominator The divisor - /// @return result The 256-bit result - function mulDivRoundingUp( - uint256 a, - uint256 b, - uint256 denominator - ) internal pure returns (uint256 result) { - result = mulDiv(a, b, denominator); - if (mulmod(a, b, denominator) > 0) { - require(result < type(uint256).max); - result++; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol deleted file mode 100644 index d5e23032e8..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math library for liquidity -library LiquidityMath { - /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows - /// @param x The liquidity before change - /// @param y The delta by which liquidity should be changed - /// @return z The liquidity delta - function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { - if (y < 0) { - require((z = x - uint128(-y)) < x, 'LS'); - } else { - require((z = x + uint128(y)) >= x, 'LA'); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol deleted file mode 100644 index dbc817c2ec..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.7.0; - -/// @title Optimized overflow and underflow safe math operations -/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost -library LowGasSafeMath { - /// @notice Returns x + y, reverts if sum overflows uint256 - /// @param x The augend - /// @param y The addend - /// @return z The sum of x and y - function add(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x + y) >= x); - } - - /// @notice Returns x - y, reverts if underflows - /// @param x The minuend - /// @param y The subtrahend - /// @return z The difference of x and y - function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x - y) <= x); - } - - /// @notice Returns x * y, reverts if overflows - /// @param x The multiplicand - /// @param y The multiplier - /// @return z The product of x and y - function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { - require(x == 0 || (z = x * y) / x == y); - } - - /// @notice Returns x + y, reverts if overflows or underflows - /// @param x The augend - /// @param y The addend - /// @return z The sum of x and y - function add(int256 x, int256 y) internal pure returns (int256 z) { - require((z = x + y) >= x == (y >= 0)); - } - - /// @notice Returns x - y, reverts if overflows or underflows - /// @param x The minuend - /// @param y The subtrahend - /// @return z The difference of x and y - function sub(int256 x, int256 y) internal pure returns (int256 z) { - require((z = x - y) <= x == (y >= 0)); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol deleted file mode 100644 index 3f6b3f32c9..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol +++ /dev/null @@ -1,325 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -/// @title Oracle -/// @notice Provides price and liquidity data useful for a wide variety of system designs -/// @dev Instances of stored oracle data, "observations", are collected in the oracle array -/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the -/// maximum length of the oracle array. New slots will be added when the array is fully populated. -/// Observations are overwritten when the full length of the oracle array is populated. -/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() -library Oracle { - struct Observation { - // the block timestamp of the observation - uint32 blockTimestamp; - // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized - int56 tickCumulative; - // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized - uint160 secondsPerLiquidityCumulativeX128; - // whether or not the observation is initialized - bool initialized; - } - - /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values - /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows - /// @param last The specified observation to be transformed - /// @param blockTimestamp The timestamp of the new observation - /// @param tick The active tick at the time of the new observation - /// @param liquidity The total in-range liquidity at the time of the new observation - /// @return Observation The newly populated observation - function transform( - Observation memory last, - uint32 blockTimestamp, - int24 tick, - uint128 liquidity - ) private pure returns (Observation memory) { - uint32 delta = blockTimestamp - last.blockTimestamp; - return - Observation({ - blockTimestamp: blockTimestamp, - tickCumulative: last.tickCumulative + int56(tick) * delta, - secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + - ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), - initialized: true - }); - } - - /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array - /// @param self The stored oracle array - /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 - /// @return cardinality The number of populated elements in the oracle array - /// @return cardinalityNext The new length of the oracle array, independent of population - function initialize(Observation[65535] storage self, uint32 time) - internal - returns (uint16 cardinality, uint16 cardinalityNext) - { - self[0] = Observation({ - blockTimestamp: time, - tickCumulative: 0, - secondsPerLiquidityCumulativeX128: 0, - initialized: true - }); - return (1, 1); - } - - /// @notice Writes an oracle observation to the array - /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. - /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality - /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. - /// @param self The stored oracle array - /// @param index The index of the observation that was most recently written to the observations array - /// @param blockTimestamp The timestamp of the new observation - /// @param tick The active tick at the time of the new observation - /// @param liquidity The total in-range liquidity at the time of the new observation - /// @param cardinality The number of populated elements in the oracle array - /// @param cardinalityNext The new length of the oracle array, independent of population - /// @return indexUpdated The new index of the most recently written element in the oracle array - /// @return cardinalityUpdated The new cardinality of the oracle array - function write( - Observation[65535] storage self, - uint16 index, - uint32 blockTimestamp, - int24 tick, - uint128 liquidity, - uint16 cardinality, - uint16 cardinalityNext - ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { - Observation memory last = self[index]; - - // early return if we've already written an observation this block - if (last.blockTimestamp == blockTimestamp) return (index, cardinality); - - // if the conditions are right, we can bump the cardinality - if (cardinalityNext > cardinality && index == (cardinality - 1)) { - cardinalityUpdated = cardinalityNext; - } else { - cardinalityUpdated = cardinality; - } - - indexUpdated = (index + 1) % cardinalityUpdated; - self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); - } - - /// @notice Prepares the oracle array to store up to `next` observations - /// @param self The stored oracle array - /// @param current The current next cardinality of the oracle array - /// @param next The proposed next cardinality which will be populated in the oracle array - /// @return next The next cardinality which will be populated in the oracle array - function grow( - Observation[65535] storage self, - uint16 current, - uint16 next - ) internal returns (uint16) { - require(current > 0, 'I'); - // no-op if the passed next value isn't greater than the current next value - if (next <= current) return current; - // store in each slot to prevent fresh SSTOREs in swaps - // this data will not be used because the initialized boolean is still false - for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; - return next; - } - - /// @notice comparator for 32-bit timestamps - /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time - /// @param time A timestamp truncated to 32 bits - /// @param a A comparison timestamp from which to determine the relative position of `time` - /// @param b From which to determine the relative position of `time` - /// @return bool Whether `a` is chronologically <= `b` - function lte( - uint32 time, - uint32 a, - uint32 b - ) private pure returns (bool) { - // if there hasn't been overflow, no need to adjust - if (a <= time && b <= time) return a <= b; - - uint256 aAdjusted = a > time ? a : a + 2**32; - uint256 bAdjusted = b > time ? b : b + 2**32; - - return aAdjusted <= bAdjusted; - } - - /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. - /// The result may be the same observation, or adjacent observations. - /// @dev The answer must be contained in the array, used when the target is located within the stored observation - /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param target The timestamp at which the reserved observation should be for - /// @param index The index of the observation that was most recently written to the observations array - /// @param cardinality The number of populated elements in the oracle array - /// @return beforeOrAt The observation recorded before, or at, the target - /// @return atOrAfter The observation recorded at, or after, the target - function binarySearch( - Observation[65535] storage self, - uint32 time, - uint32 target, - uint16 index, - uint16 cardinality - ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - uint256 l = (index + 1) % cardinality; // oldest observation - uint256 r = l + cardinality - 1; // newest observation - uint256 i; - while (true) { - i = (l + r) / 2; - - beforeOrAt = self[i % cardinality]; - - // we've landed on an uninitialized tick, keep searching higher (more recently) - if (!beforeOrAt.initialized) { - l = i + 1; - continue; - } - - atOrAfter = self[(i + 1) % cardinality]; - - bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); - - // check if we've found the answer! - if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; - - if (!targetAtOrAfter) r = i - 1; - else l = i + 1; - } - } - - /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied - /// @dev Assumes there is at least 1 initialized observation. - /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param target The timestamp at which the reserved observation should be for - /// @param tick The active tick at the time of the returned or simulated observation - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The total pool liquidity at the time of the call - /// @param cardinality The number of populated elements in the oracle array - /// @return beforeOrAt The observation which occurred at, or before, the given timestamp - /// @return atOrAfter The observation which occurred at, or after, the given timestamp - function getSurroundingObservations( - Observation[65535] storage self, - uint32 time, - uint32 target, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - // optimistically set before to the newest observation - beforeOrAt = self[index]; - - // if the target is chronologically at or after the newest observation, we can early return - if (lte(time, beforeOrAt.blockTimestamp, target)) { - if (beforeOrAt.blockTimestamp == target) { - // if newest observation equals target, we're in the same block, so we can ignore atOrAfter - return (beforeOrAt, atOrAfter); - } else { - // otherwise, we need to transform - return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); - } - } - - // now, set before to the oldest observation - beforeOrAt = self[(index + 1) % cardinality]; - if (!beforeOrAt.initialized) beforeOrAt = self[0]; - - // ensure that the target is chronologically at or after the oldest observation - require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); - - // if we've reached this point, we have to binary search - return binarySearch(self, time, target, index, cardinality); - } - - /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. - /// 0 may be passed as `secondsAgo' to return the current cumulative values. - /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values - /// at exactly the timestamp between the two observations. - /// @param self The stored oracle array - /// @param time The current block timestamp - /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation - /// @param tick The current tick - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The current in-range pool liquidity - /// @param cardinality The number of populated elements in the oracle array - /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` - /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` - function observeSingle( - Observation[65535] storage self, - uint32 time, - uint32 secondsAgo, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { - if (secondsAgo == 0) { - Observation memory last = self[index]; - if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); - return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); - } - - uint32 target = time - secondsAgo; - - (Observation memory beforeOrAt, Observation memory atOrAfter) = - getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); - - if (target == beforeOrAt.blockTimestamp) { - // we're at the left boundary - return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); - } else if (target == atOrAfter.blockTimestamp) { - // we're at the right boundary - return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); - } else { - // we're in the middle - uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; - uint32 targetDelta = target - beforeOrAt.blockTimestamp; - return ( - beforeOrAt.tickCumulative + - ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * - targetDelta, - beforeOrAt.secondsPerLiquidityCumulativeX128 + - uint160( - (uint256( - atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 - ) * targetDelta) / observationTimeDelta - ) - ); - } - } - - /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` - /// @dev Reverts if `secondsAgos` > oldest observation - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation - /// @param tick The current tick - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The current in-range pool liquidity - /// @param cardinality The number of populated elements in the oracle array - /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` - /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` - function observe( - Observation[65535] storage self, - uint32 time, - uint32[] memory secondsAgos, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { - require(cardinality > 0, 'I'); - - tickCumulatives = new int56[](secondsAgos.length); - secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); - for (uint256 i = 0; i < secondsAgos.length; i++) { - (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( - self, - time, - secondsAgos[i], - tick, - index, - liquidity, - cardinality - ); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol deleted file mode 100644 index 1c67c7f279..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './FullMath.sol'; -import './FixedPoint128.sol'; -import './LiquidityMath.sol'; - -/// @title Position -/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary -/// @dev Positions store additional state for tracking fees owed to the position -library Position { - // info stored for each user's position - struct Info { - // the amount of liquidity owned by this position - uint128 liquidity; - // fee growth per unit of liquidity as of the last update to liquidity or fees owed - uint256 feeGrowthInside0LastX128; - uint256 feeGrowthInside1LastX128; - // the fees owed to the position owner in token0/token1 - uint128 tokensOwed0; - uint128 tokensOwed1; - } - - /// @notice Returns the Info struct of a position, given an owner and position boundaries - /// @param self The mapping containing all user positions - /// @param owner The address of the position owner - /// @param tickLower The lower tick boundary of the position - /// @param tickUpper The upper tick boundary of the position - /// @return position The position info struct of the given owners' position - function get( - mapping(bytes32 => Info) storage self, - address owner, - int24 tickLower, - int24 tickUpper - ) internal view returns (Position.Info storage position) { - position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; - } - - /// @notice Credits accumulated fees to a user's position - /// @param self The individual position to update - /// @param liquidityDelta The change in pool liquidity as a result of the position update - /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries - /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries - function update( - Info storage self, - int128 liquidityDelta, - uint256 feeGrowthInside0X128, - uint256 feeGrowthInside1X128 - ) internal { - Info memory _self = self; - - uint128 liquidityNext; - if (liquidityDelta == 0) { - require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions - liquidityNext = _self.liquidity; - } else { - liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); - } - - // calculate accumulated fees - uint128 tokensOwed0 = - uint128( - FullMath.mulDiv( - feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) - ); - uint128 tokensOwed1 = - uint128( - FullMath.mulDiv( - feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) - ); - - // update the position - if (liquidityDelta != 0) self.liquidity = liquidityNext; - self.feeGrowthInside0LastX128 = feeGrowthInside0X128; - self.feeGrowthInside1LastX128 = feeGrowthInside1X128; - if (tokensOwed0 > 0 || tokensOwed1 > 0) { - // overflow is acceptable, have to withdraw before you hit type(uint128).max fees - self.tokensOwed0 += tokensOwed0; - self.tokensOwed1 += tokensOwed1; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol deleted file mode 100644 index a8ea229878..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Safe casting methods -/// @notice Contains methods for safely casting between types -library SafeCast { - /// @notice Cast a uint256 to a uint160, revert on overflow - /// @param y The uint256 to be downcasted - /// @return z The downcasted integer, now type uint160 - function toUint160(uint256 y) internal pure returns (uint160 z) { - require((z = uint160(y)) == y); - } - - /// @notice Cast a int256 to a int128, revert on overflow or underflow - /// @param y The int256 to be downcasted - /// @return z The downcasted integer, now type int128 - function toInt128(int256 y) internal pure returns (int128 z) { - require((z = int128(y)) == y); - } - - /// @notice Cast a uint256 to a int256, revert on overflow - /// @param y The uint256 to be casted - /// @return z The casted integer, now type int256 - function toInt256(uint256 y) internal pure returns (int256 z) { - require(y < 2**255); - z = int256(y); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol deleted file mode 100644 index 685f485da4..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './LowGasSafeMath.sol'; -import './SafeCast.sol'; - -import './FullMath.sol'; -import './UnsafeMath.sol'; -import './FixedPoint96.sol'; - -/// @title Functions based on Q64.96 sqrt price and liquidity -/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas -library SqrtPriceMath { - using LowGasSafeMath for uint256; - using SafeCast for uint256; - - /// @notice Gets the next sqrt price given a delta of token0 - /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least - /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the - /// price less in order to not send too much output. - /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), - /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). - /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta - /// @param liquidity The amount of usable liquidity - /// @param amount How much of token0 to add or remove from virtual reserves - /// @param add Whether to add or remove the amount of token0 - /// @return The price after adding or removing amount, depending on add - function getNextSqrtPriceFromAmount0RoundingUp( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amount, - bool add - ) internal pure returns (uint160) { - // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price - if (amount == 0) return sqrtPX96; - uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; - - if (add) { - uint256 product; - if ((product = amount * sqrtPX96) / amount == sqrtPX96) { - uint256 denominator = numerator1 + product; - if (denominator >= numerator1) - // always fits in 160 bits - return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); - } - - return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); - } else { - uint256 product; - // if the product overflows, we know the denominator underflows - // in addition, we must check that the denominator does not underflow - require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); - uint256 denominator = numerator1 - product; - return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); - } - } - - /// @notice Gets the next sqrt price given a delta of token1 - /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least - /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the - /// price less in order to not send too much output. - /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity - /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta - /// @param liquidity The amount of usable liquidity - /// @param amount How much of token1 to add, or remove, from virtual reserves - /// @param add Whether to add, or remove, the amount of token1 - /// @return The price after adding or removing `amount` - function getNextSqrtPriceFromAmount1RoundingDown( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amount, - bool add - ) internal pure returns (uint160) { - // if we're adding (subtracting), rounding down requires rounding the quotient down (up) - // in both cases, avoid a mulDiv for most inputs - if (add) { - uint256 quotient = - ( - amount <= type(uint160).max - ? (amount << FixedPoint96.RESOLUTION) / liquidity - : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) - ); - - return uint256(sqrtPX96).add(quotient).toUint160(); - } else { - uint256 quotient = - ( - amount <= type(uint160).max - ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) - : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) - ); - - require(sqrtPX96 > quotient); - // always fits 160 bits - return uint160(sqrtPX96 - quotient); - } - } - - /// @notice Gets the next sqrt price given an input amount of token0 or token1 - /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds - /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount - /// @param liquidity The amount of usable liquidity - /// @param amountIn How much of token0, or token1, is being swapped in - /// @param zeroForOne Whether the amount in is token0 or token1 - /// @return sqrtQX96 The price after adding the input amount to token0 or token1 - function getNextSqrtPriceFromInput( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amountIn, - bool zeroForOne - ) internal pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); - - // round to make sure that we don't pass the target price - return - zeroForOne - ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) - : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); - } - - /// @notice Gets the next sqrt price given an output amount of token0 or token1 - /// @dev Throws if price or liquidity are 0 or the next price is out of bounds - /// @param sqrtPX96 The starting price before accounting for the output amount - /// @param liquidity The amount of usable liquidity - /// @param amountOut How much of token0, or token1, is being swapped out - /// @param zeroForOne Whether the amount out is token0 or token1 - /// @return sqrtQX96 The price after removing the output amount of token0 or token1 - function getNextSqrtPriceFromOutput( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amountOut, - bool zeroForOne - ) internal pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); - - // round to make sure that we pass the target price - return - zeroForOne - ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) - : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); - } - - /// @notice Gets the amount0 delta between two prices - /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), - /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The amount of usable liquidity - /// @param roundUp Whether to round the amount up or down - /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices - function getAmount0Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - uint128 liquidity, - bool roundUp - ) internal pure returns (uint256 amount0) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - - uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; - uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; - - require(sqrtRatioAX96 > 0); - - return - roundUp - ? UnsafeMath.divRoundingUp( - FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), - sqrtRatioAX96 - ) - : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; - } - - /// @notice Gets the amount1 delta between two prices - /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The amount of usable liquidity - /// @param roundUp Whether to round the amount up, or down - /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices - function getAmount1Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - uint128 liquidity, - bool roundUp - ) internal pure returns (uint256 amount1) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - - return - roundUp - ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) - : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); - } - - /// @notice Helper that gets signed token0 delta - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The change in liquidity for which to compute the amount0 delta - /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices - function getAmount0Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - int128 liquidity - ) internal pure returns (int256 amount0) { - return - liquidity < 0 - ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } - - /// @notice Helper that gets signed token1 delta - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The change in liquidity for which to compute the amount1 delta - /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices - function getAmount1Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - int128 liquidity - ) internal pure returns (int256 amount1) { - return - liquidity < 0 - ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol deleted file mode 100644 index ee176fbee4..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './FullMath.sol'; -import './SqrtPriceMath.sol'; - -/// @title Computes the result of a swap within ticks -/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. -library SwapMath { - /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap - /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive - /// @param sqrtRatioCurrentX96 The current sqrt price of the pool - /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred - /// @param liquidity The usable liquidity - /// @param amountRemaining How much input or output amount is remaining to be swapped in/out - /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip - /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target - /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap - /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap - /// @return feeAmount The amount of input that will be taken as a fee - function computeSwapStep( - uint160 sqrtRatioCurrentX96, - uint160 sqrtRatioTargetX96, - uint128 liquidity, - int256 amountRemaining, - uint24 feePips - ) - internal - pure - returns ( - uint160 sqrtRatioNextX96, - uint256 amountIn, - uint256 amountOut, - uint256 feeAmount - ) - { - bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; - bool exactIn = amountRemaining >= 0; - - if (exactIn) { - uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); - amountIn = zeroForOne - ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) - : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); - if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; - else - sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( - sqrtRatioCurrentX96, - liquidity, - amountRemainingLessFee, - zeroForOne - ); - } else { - amountOut = zeroForOne - ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) - : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); - if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; - else - sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( - sqrtRatioCurrentX96, - liquidity, - uint256(-amountRemaining), - zeroForOne - ); - } - - bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; - - // get the input/output amounts - if (zeroForOne) { - amountIn = max && exactIn - ? amountIn - : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); - } else { - amountIn = max && exactIn - ? amountIn - : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); - } - - // cap the output amount to not exceed the remaining output amount - if (!exactIn && amountOut > uint256(-amountRemaining)) { - amountOut = uint256(-amountRemaining); - } - - if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { - // we didn't reach the target, so take the remainder of the maximum input as fee - feeAmount = uint256(amountRemaining) - amountIn; - } else { - feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol deleted file mode 100644 index 13d3428494..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './LowGasSafeMath.sol'; -import './SafeCast.sol'; - -import './TickMath.sol'; -import './LiquidityMath.sol'; - -/// @title Tick -/// @notice Contains functions for managing tick processes and relevant calculations -library Tick { - using LowGasSafeMath for int256; - using SafeCast for int256; - - // info stored for each initialized individual tick - struct Info { - // the total position liquidity that references this tick - uint128 liquidityGross; - // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), - int128 liquidityNet; - // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint256 feeGrowthOutside0X128; - uint256 feeGrowthOutside1X128; - // the cumulative tick value on the other side of the tick - int56 tickCumulativeOutside; - // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint160 secondsPerLiquidityOutsideX128; - // the seconds spent on the other side of the tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint32 secondsOutside; - // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 - // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks - bool initialized; - } - - /// @notice Derives max liquidity per tick from given tick spacing - /// @dev Executed within the pool constructor - /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` - /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... - /// @return The max liquidity per tick - function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { - int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; - int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; - uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; - return type(uint128).max / numTicks; - } - - /// @notice Retrieves fee growth data - /// @param self The mapping containing all tick information for initialized ticks - /// @param tickLower The lower tick boundary of the position - /// @param tickUpper The upper tick boundary of the position - /// @param tickCurrent The current tick - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries - /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries - function getFeeGrowthInside( - mapping(int24 => Tick.Info) storage self, - int24 tickLower, - int24 tickUpper, - int24 tickCurrent, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128 - ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { - Info storage lower = self[tickLower]; - Info storage upper = self[tickUpper]; - - // calculate fee growth below - uint256 feeGrowthBelow0X128; - uint256 feeGrowthBelow1X128; - if (tickCurrent >= tickLower) { - feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; - feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; - } else { - feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; - feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; - } - - // calculate fee growth above - uint256 feeGrowthAbove0X128; - uint256 feeGrowthAbove1X128; - if (tickCurrent < tickUpper) { - feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; - feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; - } else { - feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; - feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; - } - - feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; - feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; - } - - /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa - /// @param self The mapping containing all tick information for initialized ticks - /// @param tick The tick that will be updated - /// @param tickCurrent The current tick - /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool - /// @param time The current block timestamp cast to a uint32 - /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick - /// @param maxLiquidity The maximum liquidity allocation for a single tick - /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa - function update( - mapping(int24 => Tick.Info) storage self, - int24 tick, - int24 tickCurrent, - int128 liquidityDelta, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128, - uint160 secondsPerLiquidityCumulativeX128, - int56 tickCumulative, - uint32 time, - bool upper, - uint128 maxLiquidity - ) internal returns (bool flipped) { - Tick.Info storage info = self[tick]; - - uint128 liquidityGrossBefore = info.liquidityGross; - uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); - - require(liquidityGrossAfter <= maxLiquidity, 'LO'); - - flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); - - if (liquidityGrossBefore == 0) { - // by convention, we assume that all growth before a tick was initialized happened _below_ the tick - if (tick <= tickCurrent) { - info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; - info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; - info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; - info.tickCumulativeOutside = tickCumulative; - info.secondsOutside = time; - } - info.initialized = true; - } - - info.liquidityGross = liquidityGrossAfter; - - // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) - info.liquidityNet = upper - ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() - : int256(info.liquidityNet).add(liquidityDelta).toInt128(); - } - - /// @notice Clears tick data - /// @param self The mapping containing all initialized tick information for initialized ticks - /// @param tick The tick that will be cleared - function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { - delete self[tick]; - } - - /// @notice Transitions to next tick as needed by price movement - /// @param self The mapping containing all tick information for initialized ticks - /// @param tick The destination tick of the transition - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity - /// @param time The current block.timestamp - /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) - function cross( - mapping(int24 => Tick.Info) storage self, - int24 tick, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128, - uint160 secondsPerLiquidityCumulativeX128, - int56 tickCumulative, - uint32 time - ) internal returns (int128 liquidityNet) { - Tick.Info storage info = self[tick]; - info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; - info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; - info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; - info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; - info.secondsOutside = time - info.secondsOutside; - liquidityNet = info.liquidityNet; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol deleted file mode 100644 index 3c43585771..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './BitMath.sol'; - -/// @title Packed tick initialized state library -/// @notice Stores a packed mapping of tick index to its initialized state -/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. -library TickBitmap { - /// @notice Computes the position in the mapping where the initialized bit for a tick lives - /// @param tick The tick for which to compute the position - /// @return wordPos The key in the mapping containing the word in which the bit is stored - /// @return bitPos The bit position in the word where the flag is stored - function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { - wordPos = int16(tick >> 8); - bitPos = uint8(tick % 256); - } - - /// @notice Flips the initialized state for a given tick from false to true, or vice versa - /// @param self The mapping in which to flip the tick - /// @param tick The tick to flip - /// @param tickSpacing The spacing between usable ticks - function flipTick( - mapping(int16 => uint256) storage self, - int24 tick, - int24 tickSpacing - ) internal { - require(tick % tickSpacing == 0); // ensure that the tick is spaced - (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); - uint256 mask = 1 << bitPos; - self[wordPos] ^= mask; - } - - /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either - /// to the left (less than or equal to) or right (greater than) of the given tick - /// @param self The mapping in which to compute the next initialized tick - /// @param tick The starting tick - /// @param tickSpacing The spacing between usable ticks - /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) - /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick - /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks - function nextInitializedTickWithinOneWord( - mapping(int16 => uint256) storage self, - int24 tick, - int24 tickSpacing, - bool lte - ) internal view returns (int24 next, bool initialized) { - int24 compressed = tick / tickSpacing; - if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity - - if (lte) { - (int16 wordPos, uint8 bitPos) = position(compressed); - // all the 1s at or to the right of the current bitPos - uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); - uint256 masked = self[wordPos] & mask; - - // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word - initialized = masked != 0; - // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick - next = initialized - ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing - : (compressed - int24(bitPos)) * tickSpacing; - } else { - // start from the word of the next tick, since the current tick state doesn't matter - (int16 wordPos, uint8 bitPos) = position(compressed + 1); - // all the 1s at or to the left of the bitPos - uint256 mask = ~((1 << bitPos) - 1); - uint256 masked = self[wordPos] & mask; - - // if there are no initialized ticks to the left of the current tick, return leftmost in the word - initialized = masked != 0; - // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick - next = initialized - ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing - : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol deleted file mode 100644 index 378e44528c..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math library for computing sqrt prices from ticks and vice versa -/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports -/// prices between 2**-128 and 2**128 -library TickMath { - /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 - int24 internal constant MIN_TICK = -887272; - /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 - int24 internal constant MAX_TICK = -MIN_TICK; - - /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) - uint160 internal constant MIN_SQRT_RATIO = 4295128739; - /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) - uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; - - /// @notice Calculates sqrt(1.0001^tick) * 2^96 - /// @dev Throws if |tick| > max tick - /// @param tick The input tick for the above formula - /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) - /// at the given tick - function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { - uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); - require(absTick <= uint256(MAX_TICK), 'T'); - - uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; - if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; - if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; - if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; - if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; - if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; - if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; - if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; - if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; - if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; - if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; - if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; - if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; - if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; - if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; - if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; - if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; - if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; - if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; - if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; - - if (tick > 0) ratio = type(uint256).max / ratio; - - // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. - // we then downcast because we know the result always fits within 160 bits due to our tick input constraint - // we round up in the division so getTickAtSqrtRatio of the output price is always consistent - sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); - } - - /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio - /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may - /// ever return. - /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 - /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio - function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { - // second inequality must be < because the price can never reach the price at the max tick - require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); - uint256 ratio = uint256(sqrtPriceX96) << 32; - - uint256 r = ratio; - uint256 msb = 0; - - assembly { - let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(5, gt(r, 0xFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(4, gt(r, 0xFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(3, gt(r, 0xFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(2, gt(r, 0xF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(1, gt(r, 0x3)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := gt(r, 0x1) - msb := or(msb, f) - } - - if (msb >= 128) r = ratio >> (msb - 127); - else r = ratio << (127 - msb); - - int256 log_2 = (int256(msb) - 128) << 64; - - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(63, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(62, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(61, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(60, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(59, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(58, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(57, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(56, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(55, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(54, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(53, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(52, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(51, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(50, f)) - } - - int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number - - int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); - int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); - - tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol deleted file mode 100644 index 25d6309028..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.6.0; - -import '../interfaces/IERC20Minimal.sol'; - -/// @title TransferHelper -/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false -library TransferHelper { - /// @notice Transfers tokens from msg.sender to a recipient - /// @dev Calls transfer on token contract, errors with TF if transfer fails - /// @param token The contract address of the token which will be transferred - /// @param to The recipient of the transfer - /// @param value The value of the transfer - function safeTransfer( - address token, - address to, - uint256 value - ) internal { - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol deleted file mode 100644 index f62f84676f..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math functions that do not check inputs or outputs -/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks -library UnsafeMath { - /// @notice Returns ceil(x / y) - /// @dev division by 0 has unspecified behavior, and must be checked externally - /// @param x The dividend - /// @param y The divisor - /// @return z The quotient, ceil(x / y) - function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - assembly { - z := add(div(x, y), gt(mod(x, y), 0)) - } - } -} From d4b8ff011e71ecbc1388130f16075e45fa20dbd8 Mon Sep 17 00:00:00 2001 From: noxx Date: Sun, 17 Jul 2022 21:05:55 +0100 Subject: [PATCH 04/23] update the array name output in the table to include index --- .../contracts/NoDelegateCall.sol | 27 + .../contracts/UniswapV3Pool.sol | 869 ++++++++++++++++++ .../contracts/interfaces/IERC20Minimal.sol | 52 ++ .../interfaces/IUniswapV3Factory.sol | 78 ++ .../contracts/interfaces/IUniswapV3Pool.sol | 24 + .../interfaces/IUniswapV3PoolDeployer.sol | 26 + .../callback/IUniswapV3FlashCallback.sol | 18 + .../callback/IUniswapV3MintCallback.sol | 18 + .../callback/IUniswapV3SwapCallback.sol | 21 + .../interfaces/pool/IUniswapV3PoolActions.sol | 103 +++ .../pool/IUniswapV3PoolDerivedState.sol | 40 + .../interfaces/pool/IUniswapV3PoolEvents.sol | 121 +++ .../pool/IUniswapV3PoolImmutables.sol | 35 + .../pool/IUniswapV3PoolOwnerActions.sol | 23 + .../interfaces/pool/IUniswapV3PoolState.sol | 116 +++ .../contracts/libraries/BitMath.sol | 94 ++ .../contracts/libraries/FixedPoint128.sol | 8 + .../contracts/libraries/FixedPoint96.sol | 10 + .../contracts/libraries/FullMath.sol | 124 +++ .../contracts/libraries/LiquidityMath.sol | 17 + .../contracts/libraries/LowGasSafeMath.sol | 46 + .../contracts/libraries/Oracle.sol | 325 +++++++ .../contracts/libraries/Position.sol | 88 ++ .../contracts/libraries/SafeCast.sol | 28 + .../contracts/libraries/SqrtPriceMath.sol | 227 +++++ .../contracts/libraries/SwapMath.sol | 98 ++ .../contracts/libraries/Tick.sol | 183 ++++ .../contracts/libraries/TickBitmap.sol | 78 ++ .../contracts/libraries/TickMath.sol | 205 +++++ .../contracts/libraries/TransferHelper.sol | 23 + .../contracts/libraries/UnsafeMath.sol | 17 + slither/tools/read_storage/read_storage.py | 2 +- 32 files changed, 3143 insertions(+), 1 deletion(-) create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol create mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol new file mode 100644 index 0000000000..5411979dcb --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +/// @title Prevents delegatecall to a contract +/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract +abstract contract NoDelegateCall { + /// @dev The original address of this contract + address private immutable original; + + constructor() { + // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. + // In other words, this variable won't change when it's checked at runtime. + original = address(this); + } + + /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, + /// and the use of immutable means the address bytes are copied in every place the modifier is used. + function checkNotDelegateCall() private view { + require(address(this) == original); + } + + /// @notice Prevents delegatecall into the modified method + modifier noDelegateCall() { + checkNotDelegateCall(); + _; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol new file mode 100644 index 0000000000..9e09821272 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +import './interfaces/IUniswapV3Pool.sol'; + +import './NoDelegateCall.sol'; + +import './libraries/LowGasSafeMath.sol'; +import './libraries/SafeCast.sol'; +import './libraries/Tick.sol'; +import './libraries/TickBitmap.sol'; +import './libraries/Position.sol'; +import './libraries/Oracle.sol'; + +import './libraries/FullMath.sol'; +import './libraries/FixedPoint128.sol'; +import './libraries/TransferHelper.sol'; +import './libraries/TickMath.sol'; +import './libraries/LiquidityMath.sol'; +import './libraries/SqrtPriceMath.sol'; +import './libraries/SwapMath.sol'; + +import './interfaces/IUniswapV3PoolDeployer.sol'; +import './interfaces/IUniswapV3Factory.sol'; +import './interfaces/IERC20Minimal.sol'; +import './interfaces/callback/IUniswapV3MintCallback.sol'; +import './interfaces/callback/IUniswapV3SwapCallback.sol'; +import './interfaces/callback/IUniswapV3FlashCallback.sol'; + +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); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol new file mode 100644 index 0000000000..c303265a3b --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Minimal ERC20 interface for Uniswap +/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 +interface IERC20Minimal { + /// @notice Returns the balance of a token + /// @param account The account for which to look up the number of tokens it has, i.e. its balance + /// @return The number of tokens held by the account + function balanceOf(address account) external view returns (uint256); + + /// @notice Transfers the amount of token from the `msg.sender` to the recipient + /// @param recipient The account that will receive the amount transferred + /// @param amount The number of tokens to send from the sender to the recipient + /// @return Returns true for a successful transfer, false for an unsuccessful transfer + function transfer(address recipient, uint256 amount) external returns (bool); + + /// @notice Returns the current allowance given to a spender by an owner + /// @param owner The account of the token owner + /// @param spender The account of the token spender + /// @return The current allowance granted by `owner` to `spender` + function allowance(address owner, address spender) external view returns (uint256); + + /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` + /// @param spender The account which will be allowed to spend a given amount of the owners tokens + /// @param amount The amount of tokens allowed to be used by `spender` + /// @return Returns true for a successful approval, false for unsuccessful + function approve(address spender, uint256 amount) external returns (bool); + + /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` + /// @param sender The account from which the transfer will be initiated + /// @param recipient The recipient of the transfer + /// @param amount The amount of the transfer + /// @return Returns true for a successful transfer, false for unsuccessful + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. + /// @param from The account from which the tokens were sent, i.e. the balance decreased + /// @param to The account to which the tokens were sent, i.e. the balance increased + /// @param value The amount of tokens that were transferred + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. + /// @param owner The account that approved spending of its tokens + /// @param spender The account for which the spending allowance was modified + /// @param value The new allowance from the owner to the spender + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol new file mode 100644 index 0000000000..540cfdc681 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title The interface for the Uniswap V3 Factory +/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees +interface IUniswapV3Factory { + /// @notice Emitted when the owner of the factory is changed + /// @param oldOwner The owner before the owner was changed + /// @param newOwner The owner after the owner was changed + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + /// @notice Emitted when a pool is created + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks + /// @param pool The address of the created pool + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + + /// @notice Emitted when a new fee amount is enabled for pool creation via the factory + /// @param fee The enabled fee, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + + /// @notice Returns the current owner of the factory + /// @dev Can be changed by the current owner via setOwner + /// @return The address of the factory owner + function owner() external view returns (address); + + /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled + /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context + /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee + /// @return The tick spacing + function feeAmountTickSpacing(uint24 fee) external view returns (int24); + + /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The pool address + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) external view returns (address pool); + + /// @notice Creates a pool for the given two tokens and fee + /// @param tokenA One of the two tokens in the desired pool + /// @param tokenB The other of the two tokens in the desired pool + /// @param fee The desired fee for the pool + /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved + /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments + /// are invalid. + /// @return pool The address of the newly created pool + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); + + /// @notice Updates the owner of the factory + /// @dev Must be called by the current owner + /// @param _owner The new owner of the factory + function setOwner(address _owner) external; + + /// @notice Enables a fee amount with the given tickSpacing + /// @dev Fee amounts may never be removed once enabled + /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) + /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol new file mode 100644 index 0000000000..56df0500df --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import './pool/IUniswapV3PoolImmutables.sol'; +import './pool/IUniswapV3PoolState.sol'; +import './pool/IUniswapV3PoolDerivedState.sol'; +import './pool/IUniswapV3PoolActions.sol'; +import './pool/IUniswapV3PoolOwnerActions.sol'; +import './pool/IUniswapV3PoolEvents.sol'; + +/// @title The interface for a Uniswap V3 Pool +/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform +/// to the ERC20 specification +/// @dev The pool interface is broken up into many smaller pieces +interface IUniswapV3Pool is + IUniswapV3PoolImmutables, + IUniswapV3PoolState, + IUniswapV3PoolDerivedState, + IUniswapV3PoolActions, + IUniswapV3PoolOwnerActions, + IUniswapV3PoolEvents +{ + +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol new file mode 100644 index 0000000000..72096c1ff5 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools +/// @notice A contract that constructs a pool must implement this to pass arguments to the pool +/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash +/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain +interface IUniswapV3PoolDeployer { + /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. + /// @dev Called by the pool constructor to fetch the parameters of the pool + /// Returns factory The factory address + /// Returns token0 The first token of the pool by address sort order + /// Returns token1 The second token of the pool by address sort order + /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// Returns tickSpacing The minimum number of ticks between initialized ticks + function parameters() + external + view + returns ( + address factory, + address token0, + address token1, + uint24 fee, + int24 tickSpacing + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol new file mode 100644 index 0000000000..18e54c4e12 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#flash +/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface +interface IUniswapV3FlashCallback { + /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. + /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param fee0 The fee amount in token0 due to the pool by the end of the flash + /// @param fee1 The fee amount in token1 due to the pool by the end of the flash + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call + function uniswapV3FlashCallback( + uint256 fee0, + uint256 fee1, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol new file mode 100644 index 0000000000..85447e84f9 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#mint +/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface +interface IUniswapV3MintCallback { + /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. + /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity + /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol new file mode 100644 index 0000000000..9f183b22a7 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#swap +/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface +interface IUniswapV3SwapCallback { + /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + /// @dev In the implementation you must pay the pool tokens owed for the swap. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. + /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol new file mode 100644 index 0000000000..44fb61c24a --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissionless pool actions +/// @notice Contains pool methods that can be called by anyone +interface IUniswapV3PoolActions { + /// @notice Sets the initial price for the pool + /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value + /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 + function initialize(uint160 sqrtPriceX96) external; + + /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position + /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback + /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends + /// on tickLower, tickUpper, the amount of liquidity, and the current price. + /// @param recipient The address for which the liquidity will be created + /// @param tickLower The lower tick of the position in which to add liquidity + /// @param tickUpper The upper tick of the position in which to add liquidity + /// @param amount The amount of liquidity to mint + /// @param data Any data that should be passed through to the callback + /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback + /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Collects tokens owed to a position + /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. + /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or + /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the + /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. + /// @param recipient The address which should receive the fees collected + /// @param tickLower The lower tick of the position for which to collect fees + /// @param tickUpper The upper tick of the position for which to collect fees + /// @param amount0Requested How much token0 should be withdrawn from the fees owed + /// @param amount1Requested How much token1 should be withdrawn from the fees owed + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + + /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position + /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 + /// @dev Fees must be collected separately via a call to #collect + /// @param tickLower The lower tick of the position for which to burn liquidity + /// @param tickUpper The upper tick of the position for which to burn liquidity + /// @param amount How much liquidity to burn + /// @return amount0 The amount of token0 sent to the recipient + /// @return amount1 The amount of token1 sent to the recipient + function burn( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Swap token0 for token1, or token1 for token0 + /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback + /// @param recipient The address to receive the output of the swap + /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 + /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) + /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this + /// value after the swap. If one for zero, the price cannot be greater than this value after the swap + /// @param data Any data to be passed through to the callback + /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive + /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); + + /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback + /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback + /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling + /// with 0 amount{0,1} and sending the donation amount(s) from the callback + /// @param recipient The address which will receive the token0 and token1 amounts + /// @param amount0 The amount of token0 to send + /// @param amount1 The amount of token1 to send + /// @param data Any data to be passed through to the callback + function flash( + address recipient, + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external; + + /// @notice Increase the maximum number of price and liquidity observations that this pool will store + /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to + /// the input observationCardinalityNext. + /// @param observationCardinalityNext The desired minimum number of observations for the pool to store + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol new file mode 100644 index 0000000000..eda3a0089d --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that is not stored +/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the +/// blockchain. The functions here may have variable gas costs. +interface IUniswapV3PoolDerivedState { + /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp + /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing + /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, + /// you must call it with secondsAgos = [3600, 0]. + /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in + /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. + /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned + /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp + /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block + /// timestamp + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); + + /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range + /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. + /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first + /// snapshot is taken and the second snapshot is taken. + /// @param tickLower The lower tick of the range + /// @param tickUpper The upper tick of the range + /// @return tickCumulativeInside The snapshot of the tick accumulator for the range + /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range + /// @return secondsInside The snapshot of seconds per liquidity for the range + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol new file mode 100644 index 0000000000..9d915dde93 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Events emitted by a pool +/// @notice Contains all events emitted by the pool +interface IUniswapV3PoolEvents { + /// @notice Emitted exactly once by a pool when #initialize is first called on the pool + /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize + /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 + /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool + event Initialize(uint160 sqrtPriceX96, int24 tick); + + /// @notice Emitted when liquidity is minted for a given position + /// @param sender The address that minted the liquidity + /// @param owner The owner of the position and recipient of any minted liquidity + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity minted to the position range + /// @param amount0 How much token0 was required for the minted liquidity + /// @param amount1 How much token1 was required for the minted liquidity + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted when fees are collected by the owner of a position + /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees + /// @param owner The owner of the position for which fees are collected + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount0 The amount of token0 fees collected + /// @param amount1 The amount of token1 fees collected + event Collect( + address indexed owner, + address recipient, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount0, + uint128 amount1 + ); + + /// @notice Emitted when a position's liquidity is removed + /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect + /// @param owner The owner of the position for which liquidity is removed + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity to remove + /// @param amount0 The amount of token0 withdrawn + /// @param amount1 The amount of token1 withdrawn + event Burn( + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted by the pool for any swaps between token0 and token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the output of the swap + /// @param amount0 The delta of the token0 balance of the pool + /// @param amount1 The delta of the token1 balance of the pool + /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 + /// @param liquidity The liquidity of the pool after the swap + /// @param tick The log base 1.0001 of price of the pool after the swap + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + /// @notice Emitted by the pool for any flashes of token0/token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the tokens from flash + /// @param amount0 The amount of token0 that was flashed + /// @param amount1 The amount of token1 that was flashed + /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee + /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee + event Flash( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1, + uint256 paid0, + uint256 paid1 + ); + + /// @notice Emitted by the pool for increases to the number of observations that can be stored + /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index + /// just before a mint/swap/burn. + /// @param observationCardinalityNextOld The previous value of the next observation cardinality + /// @param observationCardinalityNextNew The updated value of the next observation cardinality + event IncreaseObservationCardinalityNext( + uint16 observationCardinalityNextOld, + uint16 observationCardinalityNextNew + ); + + /// @notice Emitted when the protocol fee is changed by the pool + /// @param feeProtocol0Old The previous value of the token0 protocol fee + /// @param feeProtocol1Old The previous value of the token1 protocol fee + /// @param feeProtocol0New The updated value of the token0 protocol fee + /// @param feeProtocol1New The updated value of the token1 protocol fee + event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); + + /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner + /// @param sender The address that collects the protocol fees + /// @param recipient The address that receives the collected protocol fees + /// @param amount0 The amount of token0 protocol fees that is withdrawn + /// @param amount0 The amount of token1 protocol fees that is withdrawn + event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol new file mode 100644 index 0000000000..c9beb151e3 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that never changes +/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values +interface IUniswapV3PoolImmutables { + /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface + /// @return The contract address + function factory() external view returns (address); + + /// @notice The first of the two tokens of the pool, sorted by address + /// @return The token contract address + function token0() external view returns (address); + + /// @notice The second of the two tokens of the pool, sorted by address + /// @return The token contract address + function token1() external view returns (address); + + /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 + /// @return The fee + function fee() external view returns (uint24); + + /// @notice The pool tick spacing + /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive + /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... + /// This value is an int24 to avoid casting even though it is always positive. + /// @return The tick spacing + function tickSpacing() external view returns (int24); + + /// @notice The maximum amount of position liquidity that can use any tick in the range + /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and + /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool + /// @return The max amount of liquidity per tick + function maxLiquidityPerTick() external view returns (uint128); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol new file mode 100644 index 0000000000..2395ed321d --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissioned pool actions +/// @notice Contains pool methods that may only be called by the factory owner +interface IUniswapV3PoolOwnerActions { + /// @notice Set the denominator of the protocol's % share of the fees + /// @param feeProtocol0 new protocol fee for token0 of the pool + /// @param feeProtocol1 new protocol fee for token1 of the pool + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; + + /// @notice Collect the protocol fee accrued to the pool + /// @param recipient The address to which collected protocol fees should be sent + /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 + /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 + /// @return amount0 The protocol fee collected in token0 + /// @return amount1 The protocol fee collected in token1 + function collectProtocol( + address recipient, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol new file mode 100644 index 0000000000..620256c311 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that can change +/// @notice These methods compose the pool's state, and can change with any frequency including multiple times +/// per transaction +interface IUniswapV3PoolState { + /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas + /// when accessed externally. + /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value + /// tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick + /// boundary. + /// observationIndex The index of the last oracle observation that was written, + /// observationCardinality The current maximum number of observations stored in the pool, + /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. + /// feeProtocol The protocol fee for both tokens of the pool. + /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 + /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. + /// unlocked Whether the pool is currently locked to reentrancy + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + + /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal0X128() external view returns (uint256); + + /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal1X128() external view returns (uint256); + + /// @notice The amounts of token0 and token1 that are owed to the protocol + /// @dev Protocol fees will never exceed uint128 max in either token + function protocolFees() external view returns (uint128 token0, uint128 token1); + + /// @notice The currently in range liquidity available to the pool + /// @dev This value has no relationship to the total liquidity across all ticks + function liquidity() external view returns (uint128); + + /// @notice Look up information about a specific tick in the pool + /// @param tick The tick to look up + /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or + /// tick upper, + /// liquidityNet how much liquidity changes when the pool price crosses the tick, + /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, + /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, + /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick + /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, + /// secondsOutside the seconds spent on the other side of the tick from the current tick, + /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. + /// In addition, these values are only relative and must be used only in comparison to previous snapshots for + /// a specific position. + function ticks(int24 tick) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + + /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information + function tickBitmap(int16 wordPosition) external view returns (uint256); + + /// @notice Returns the information about a position by the position's key + /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper + /// @return _liquidity The amount of liquidity in the position, + /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, + /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, + /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, + /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke + function positions(bytes32 key) + external + view + returns ( + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + /// @notice Returns data about a specific observation index + /// @param index The element of the observations array to fetch + /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time + /// ago, rather than at a specific index in the array. + /// @return blockTimestamp The timestamp of the observation, + /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, + /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, + /// Returns initialized whether the observation has been initialized and the values are safe to use + function observations(uint256 index) + external + view + returns ( + uint32 blockTimestamp, + int56 tickCumulative, + uint160 secondsPerLiquidityCumulativeX128, + bool initialized + ); +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol new file mode 100644 index 0000000000..3a7216c7bd --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title BitMath +/// @dev This library provides functionality for computing bit properties of an unsigned integer +library BitMath { + /// @notice Returns the index of the most significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) + /// @param x the value for which to compute the most significant bit, must be greater than 0 + /// @return r the index of the most significant bit + function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + if (x >= 0x100000000000000000000000000000000) { + x >>= 128; + r += 128; + } + if (x >= 0x10000000000000000) { + x >>= 64; + r += 64; + } + if (x >= 0x100000000) { + x >>= 32; + r += 32; + } + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 0x4) { + x >>= 2; + r += 2; + } + if (x >= 0x2) r += 1; + } + + /// @notice Returns the index of the least significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) + /// @param x the value for which to compute the least significant bit, must be greater than 0 + /// @return r the index of the least significant bit + function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + r = 255; + if (x & type(uint128).max > 0) { + r -= 128; + } else { + x >>= 128; + } + if (x & type(uint64).max > 0) { + r -= 64; + } else { + x >>= 64; + } + if (x & type(uint32).max > 0) { + r -= 32; + } else { + x >>= 32; + } + if (x & type(uint16).max > 0) { + r -= 16; + } else { + x >>= 16; + } + if (x & type(uint8).max > 0) { + r -= 8; + } else { + x >>= 8; + } + if (x & 0xf > 0) { + r -= 4; + } else { + x >>= 4; + } + if (x & 0x3 > 0) { + r -= 2; + } else { + x >>= 2; + } + if (x & 0x1 > 0) r -= 1; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol new file mode 100644 index 0000000000..6d6948b106 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint128 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +library FixedPoint128 { + uint256 internal constant Q128 = 0x100000000000000000000000000000000; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol new file mode 100644 index 0000000000..63b42c294e --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint96 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +/// @dev Used in SqrtPriceMath.sol +library FixedPoint96 { + uint8 internal constant RESOLUTION = 96; + uint256 internal constant Q96 = 0x1000000000000000000000000; +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol new file mode 100644 index 0000000000..8688a1773f --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.0; + +/// @title Contains 512-bit math functions +/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision +/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits +library FullMath { + /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + function mulDiv( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + // 512-bit multiply [prod1 prod0] = a * b + // Compute the product mod 2**256 and mod 2**256 - 1 + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0 + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0] + // Compute remainder using mulmod + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + // Subtract 256 bit number from 512 bit number + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator + // Compute largest power of two divisor of denominator. + // Always >= 1. + uint256 twos = -denominator & denominator; + // Divide denominator by power of two + assembly { + denominator := div(denominator, twos) + } + + // Divide [prod1 prod0] by the factors of two + assembly { + prod0 := div(prod0, twos) + } + // Shift in bits from prod1 into prod0. For this we need + // to flip `twos` such that it is 2**256 / twos. + // If twos is zero, then it becomes one + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + // Invert denominator mod 2**256 + // Now that denominator is an odd number, it has an inverse + // modulo 2**256 such that denominator * inv = 1 mod 2**256. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inv = (3 * denominator) ^ 2; + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv *= 2 - denominator * inv; // inverse mod 2**8 + inv *= 2 - denominator * inv; // inverse mod 2**16 + inv *= 2 - denominator * inv; // inverse mod 2**32 + inv *= 2 - denominator * inv; // inverse mod 2**64 + inv *= 2 - denominator * inv; // inverse mod 2**128 + inv *= 2 - denominator * inv; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying + // with the modular inverse of denominator. This will give us the + // correct result modulo 2**256. Since the precoditions guarantee + // that the outcome is less than 2**256, this is the final result. + // We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inv; + return result; + } + + /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + function mulDivRoundingUp( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max); + result++; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol new file mode 100644 index 0000000000..d5e23032e8 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math library for liquidity +library LiquidityMath { + /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows + /// @param x The liquidity before change + /// @param y The delta by which liquidity should be changed + /// @return z The liquidity delta + function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { + if (y < 0) { + require((z = x - uint128(-y)) < x, 'LS'); + } else { + require((z = x + uint128(y)) >= x, 'LA'); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol new file mode 100644 index 0000000000..dbc817c2ec --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.0; + +/// @title Optimized overflow and underflow safe math operations +/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost +library LowGasSafeMath { + /// @notice Returns x + y, reverts if sum overflows uint256 + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x); + } + + /// @notice Returns x - y, reverts if underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x); + } + + /// @notice Returns x * y, reverts if overflows + /// @param x The multiplicand + /// @param y The multiplier + /// @return z The product of x and y + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(x == 0 || (z = x * y) / x == y); + } + + /// @notice Returns x + y, reverts if overflows or underflows + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x + y) >= x == (y >= 0)); + } + + /// @notice Returns x - y, reverts if overflows or underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x - y) <= x == (y >= 0)); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol new file mode 100644 index 0000000000..3f6b3f32c9 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +/// @title Oracle +/// @notice Provides price and liquidity data useful for a wide variety of system designs +/// @dev Instances of stored oracle data, "observations", are collected in the oracle array +/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the +/// maximum length of the oracle array. New slots will be added when the array is fully populated. +/// Observations are overwritten when the full length of the oracle array is populated. +/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() +library Oracle { + struct Observation { + // the block timestamp of the observation + uint32 blockTimestamp; + // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized + int56 tickCumulative; + // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized + uint160 secondsPerLiquidityCumulativeX128; + // whether or not the observation is initialized + bool initialized; + } + + /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values + /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows + /// @param last The specified observation to be transformed + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @return Observation The newly populated observation + function transform( + Observation memory last, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity + ) private pure returns (Observation memory) { + uint32 delta = blockTimestamp - last.blockTimestamp; + return + Observation({ + blockTimestamp: blockTimestamp, + tickCumulative: last.tickCumulative + int56(tick) * delta, + secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + + ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), + initialized: true + }); + } + + /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array + /// @param self The stored oracle array + /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 + /// @return cardinality The number of populated elements in the oracle array + /// @return cardinalityNext The new length of the oracle array, independent of population + function initialize(Observation[65535] storage self, uint32 time) + internal + returns (uint16 cardinality, uint16 cardinalityNext) + { + self[0] = Observation({ + blockTimestamp: time, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + initialized: true + }); + return (1, 1); + } + + /// @notice Writes an oracle observation to the array + /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. + /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality + /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. + /// @param self The stored oracle array + /// @param index The index of the observation that was most recently written to the observations array + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @param cardinality The number of populated elements in the oracle array + /// @param cardinalityNext The new length of the oracle array, independent of population + /// @return indexUpdated The new index of the most recently written element in the oracle array + /// @return cardinalityUpdated The new cardinality of the oracle array + function write( + Observation[65535] storage self, + uint16 index, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity, + uint16 cardinality, + uint16 cardinalityNext + ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { + Observation memory last = self[index]; + + // early return if we've already written an observation this block + if (last.blockTimestamp == blockTimestamp) return (index, cardinality); + + // if the conditions are right, we can bump the cardinality + if (cardinalityNext > cardinality && index == (cardinality - 1)) { + cardinalityUpdated = cardinalityNext; + } else { + cardinalityUpdated = cardinality; + } + + indexUpdated = (index + 1) % cardinalityUpdated; + self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); + } + + /// @notice Prepares the oracle array to store up to `next` observations + /// @param self The stored oracle array + /// @param current The current next cardinality of the oracle array + /// @param next The proposed next cardinality which will be populated in the oracle array + /// @return next The next cardinality which will be populated in the oracle array + function grow( + Observation[65535] storage self, + uint16 current, + uint16 next + ) internal returns (uint16) { + require(current > 0, 'I'); + // no-op if the passed next value isn't greater than the current next value + if (next <= current) return current; + // store in each slot to prevent fresh SSTOREs in swaps + // this data will not be used because the initialized boolean is still false + for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; + return next; + } + + /// @notice comparator for 32-bit timestamps + /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time + /// @param time A timestamp truncated to 32 bits + /// @param a A comparison timestamp from which to determine the relative position of `time` + /// @param b From which to determine the relative position of `time` + /// @return bool Whether `a` is chronologically <= `b` + function lte( + uint32 time, + uint32 a, + uint32 b + ) private pure returns (bool) { + // if there hasn't been overflow, no need to adjust + if (a <= time && b <= time) return a <= b; + + uint256 aAdjusted = a > time ? a : a + 2**32; + uint256 bAdjusted = b > time ? b : b + 2**32; + + return aAdjusted <= bAdjusted; + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. + /// The result may be the same observation, or adjacent observations. + /// @dev The answer must be contained in the array, used when the target is located within the stored observation + /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param index The index of the observation that was most recently written to the observations array + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation recorded before, or at, the target + /// @return atOrAfter The observation recorded at, or after, the target + function binarySearch( + Observation[65535] storage self, + uint32 time, + uint32 target, + uint16 index, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + uint256 l = (index + 1) % cardinality; // oldest observation + uint256 r = l + cardinality - 1; // newest observation + uint256 i; + while (true) { + i = (l + r) / 2; + + beforeOrAt = self[i % cardinality]; + + // we've landed on an uninitialized tick, keep searching higher (more recently) + if (!beforeOrAt.initialized) { + l = i + 1; + continue; + } + + atOrAfter = self[(i + 1) % cardinality]; + + bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); + + // check if we've found the answer! + if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; + + if (!targetAtOrAfter) r = i - 1; + else l = i + 1; + } + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied + /// @dev Assumes there is at least 1 initialized observation. + /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param tick The active tick at the time of the returned or simulated observation + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The total pool liquidity at the time of the call + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation which occurred at, or before, the given timestamp + /// @return atOrAfter The observation which occurred at, or after, the given timestamp + function getSurroundingObservations( + Observation[65535] storage self, + uint32 time, + uint32 target, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + // optimistically set before to the newest observation + beforeOrAt = self[index]; + + // if the target is chronologically at or after the newest observation, we can early return + if (lte(time, beforeOrAt.blockTimestamp, target)) { + if (beforeOrAt.blockTimestamp == target) { + // if newest observation equals target, we're in the same block, so we can ignore atOrAfter + return (beforeOrAt, atOrAfter); + } else { + // otherwise, we need to transform + return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); + } + } + + // now, set before to the oldest observation + beforeOrAt = self[(index + 1) % cardinality]; + if (!beforeOrAt.initialized) beforeOrAt = self[0]; + + // ensure that the target is chronologically at or after the oldest observation + require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); + + // if we've reached this point, we have to binary search + return binarySearch(self, time, target, index, cardinality); + } + + /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. + /// 0 may be passed as `secondsAgo' to return the current cumulative values. + /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values + /// at exactly the timestamp between the two observations. + /// @param self The stored oracle array + /// @param time The current block timestamp + /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` + function observeSingle( + Observation[65535] storage self, + uint32 time, + uint32 secondsAgo, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { + if (secondsAgo == 0) { + Observation memory last = self[index]; + if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); + return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); + } + + uint32 target = time - secondsAgo; + + (Observation memory beforeOrAt, Observation memory atOrAfter) = + getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); + + if (target == beforeOrAt.blockTimestamp) { + // we're at the left boundary + return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); + } else if (target == atOrAfter.blockTimestamp) { + // we're at the right boundary + return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); + } else { + // we're in the middle + uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; + uint32 targetDelta = target - beforeOrAt.blockTimestamp; + return ( + beforeOrAt.tickCumulative + + ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * + targetDelta, + beforeOrAt.secondsPerLiquidityCumulativeX128 + + uint160( + (uint256( + atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 + ) * targetDelta) / observationTimeDelta + ) + ); + } + } + + /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` + /// @dev Reverts if `secondsAgos` > oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` + function observe( + Observation[65535] storage self, + uint32 time, + uint32[] memory secondsAgos, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { + require(cardinality > 0, 'I'); + + tickCumulatives = new int56[](secondsAgos.length); + secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); + for (uint256 i = 0; i < secondsAgos.length; i++) { + (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( + self, + time, + secondsAgos[i], + tick, + index, + liquidity, + cardinality + ); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol new file mode 100644 index 0000000000..1c67c7f279 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './FullMath.sol'; +import './FixedPoint128.sol'; +import './LiquidityMath.sol'; + +/// @title Position +/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary +/// @dev Positions store additional state for tracking fees owed to the position +library Position { + // info stored for each user's position + struct Info { + // the amount of liquidity owned by this position + uint128 liquidity; + // fee growth per unit of liquidity as of the last update to liquidity or fees owed + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + // the fees owed to the position owner in token0/token1 + uint128 tokensOwed0; + uint128 tokensOwed1; + } + + /// @notice Returns the Info struct of a position, given an owner and position boundaries + /// @param self The mapping containing all user positions + /// @param owner The address of the position owner + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @return position The position info struct of the given owners' position + function get( + mapping(bytes32 => Info) storage self, + address owner, + int24 tickLower, + int24 tickUpper + ) internal view returns (Position.Info storage position) { + position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; + } + + /// @notice Credits accumulated fees to a user's position + /// @param self The individual position to update + /// @param liquidityDelta The change in pool liquidity as a result of the position update + /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function update( + Info storage self, + int128 liquidityDelta, + uint256 feeGrowthInside0X128, + uint256 feeGrowthInside1X128 + ) internal { + Info memory _self = self; + + uint128 liquidityNext; + if (liquidityDelta == 0) { + require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions + liquidityNext = _self.liquidity; + } else { + liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); + } + + // calculate accumulated fees + uint128 tokensOwed0 = + uint128( + FullMath.mulDiv( + feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + uint128 tokensOwed1 = + uint128( + FullMath.mulDiv( + feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + + // update the position + if (liquidityDelta != 0) self.liquidity = liquidityNext; + self.feeGrowthInside0LastX128 = feeGrowthInside0X128; + self.feeGrowthInside1LastX128 = feeGrowthInside1X128; + if (tokensOwed0 > 0 || tokensOwed1 > 0) { + // overflow is acceptable, have to withdraw before you hit type(uint128).max fees + self.tokensOwed0 += tokensOwed0; + self.tokensOwed1 += tokensOwed1; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol new file mode 100644 index 0000000000..a8ea229878 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Safe casting methods +/// @notice Contains methods for safely casting between types +library SafeCast { + /// @notice Cast a uint256 to a uint160, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint160 + function toUint160(uint256 y) internal pure returns (uint160 z) { + require((z = uint160(y)) == y); + } + + /// @notice Cast a int256 to a int128, revert on overflow or underflow + /// @param y The int256 to be downcasted + /// @return z The downcasted integer, now type int128 + function toInt128(int256 y) internal pure returns (int128 z) { + require((z = int128(y)) == y); + } + + /// @notice Cast a uint256 to a int256, revert on overflow + /// @param y The uint256 to be casted + /// @return z The casted integer, now type int256 + function toInt256(uint256 y) internal pure returns (int256 z) { + require(y < 2**255); + z = int256(y); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol new file mode 100644 index 0000000000..685f485da4 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './FullMath.sol'; +import './UnsafeMath.sol'; +import './FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMath { + using LowGasSafeMath for uint256; + using SafeCast for uint256; + + /// @notice Gets the next sqrt price given a delta of token0 + /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the + /// price less in order to not send too much output. + /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), + /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). + /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token0 to add or remove from virtual reserves + /// @param add Whether to add or remove the amount of token0 + /// @return The price after adding or removing amount, depending on add + function getNextSqrtPriceFromAmount0RoundingUp( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price + if (amount == 0) return sqrtPX96; + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + + if (add) { + uint256 product; + if ((product = amount * sqrtPX96) / amount == sqrtPX96) { + uint256 denominator = numerator1 + product; + if (denominator >= numerator1) + // always fits in 160 bits + return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); + } + + return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); + } else { + uint256 product; + // if the product overflows, we know the denominator underflows + // in addition, we must check that the denominator does not underflow + require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); + uint256 denominator = numerator1 - product; + return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); + } + } + + /// @notice Gets the next sqrt price given a delta of token1 + /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the + /// price less in order to not send too much output. + /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity + /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token1 to add, or remove, from virtual reserves + /// @param add Whether to add, or remove, the amount of token1 + /// @return The price after adding or removing `amount` + function getNextSqrtPriceFromAmount1RoundingDown( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // if we're adding (subtracting), rounding down requires rounding the quotient down (up) + // in both cases, avoid a mulDiv for most inputs + if (add) { + uint256 quotient = + ( + amount <= type(uint160).max + ? (amount << FixedPoint96.RESOLUTION) / liquidity + : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) + ); + + return uint256(sqrtPX96).add(quotient).toUint160(); + } else { + uint256 quotient = + ( + amount <= type(uint160).max + ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) + : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) + ); + + require(sqrtPX96 > quotient); + // always fits 160 bits + return uint160(sqrtPX96 - quotient); + } + } + + /// @notice Gets the next sqrt price given an input amount of token0 or token1 + /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds + /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount + /// @param liquidity The amount of usable liquidity + /// @param amountIn How much of token0, or token1, is being swapped in + /// @param zeroForOne Whether the amount in is token0 or token1 + /// @return sqrtQX96 The price after adding the input amount to token0 or token1 + function getNextSqrtPriceFromInput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we don't pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) + : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); + } + + /// @notice Gets the next sqrt price given an output amount of token0 or token1 + /// @dev Throws if price or liquidity are 0 or the next price is out of bounds + /// @param sqrtPX96 The starting price before accounting for the output amount + /// @param liquidity The amount of usable liquidity + /// @param amountOut How much of token0, or token1, is being swapped out + /// @param zeroForOne Whether the amount out is token0 or token1 + /// @return sqrtQX96 The price after removing the output amount of token0 or token1 + function getNextSqrtPriceFromOutput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) + : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); + } + + /// @notice Gets the amount0 delta between two prices + /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), + /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up or down + /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + + require(sqrtRatioAX96 > 0); + + return + roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), + sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; + } + + /// @notice Gets the amount1 delta between two prices + /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up, or down + /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return + roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } + + /// @notice Helper that gets signed token0 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount0 delta + /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount0) { + return + liquidity < 0 + ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } + + /// @notice Helper that gets signed token1 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount1 delta + /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount1) { + return + liquidity < 0 + ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol new file mode 100644 index 0000000000..ee176fbee4 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './FullMath.sol'; +import './SqrtPriceMath.sol'; + +/// @title Computes the result of a swap within ticks +/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. +library SwapMath { + /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap + /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive + /// @param sqrtRatioCurrentX96 The current sqrt price of the pool + /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred + /// @param liquidity The usable liquidity + /// @param amountRemaining How much input or output amount is remaining to be swapped in/out + /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip + /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target + /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap + /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap + /// @return feeAmount The amount of input that will be taken as a fee + function computeSwapStep( + uint160 sqrtRatioCurrentX96, + uint160 sqrtRatioTargetX96, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) + internal + pure + returns ( + uint160 sqrtRatioNextX96, + uint256 amountIn, + uint256 amountOut, + uint256 feeAmount + ) + { + bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; + bool exactIn = amountRemaining >= 0; + + if (exactIn) { + uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); + if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( + sqrtRatioCurrentX96, + liquidity, + amountRemainingLessFee, + zeroForOne + ); + } else { + amountOut = zeroForOne + ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); + if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( + sqrtRatioCurrentX96, + liquidity, + uint256(-amountRemaining), + zeroForOne + ); + } + + bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; + + // get the input/output amounts + if (zeroForOne) { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); + } else { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); + } + + // cap the output amount to not exceed the remaining output amount + if (!exactIn && amountOut > uint256(-amountRemaining)) { + amountOut = uint256(-amountRemaining); + } + + if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { + // we didn't reach the target, so take the remainder of the maximum input as fee + feeAmount = uint256(amountRemaining) - amountIn; + } else { + feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol new file mode 100644 index 0000000000..13d3428494 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './TickMath.sol'; +import './LiquidityMath.sol'; + +/// @title Tick +/// @notice Contains functions for managing tick processes and relevant calculations +library Tick { + using LowGasSafeMath for int256; + using SafeCast for int256; + + // info stored for each initialized individual tick + struct Info { + // the total position liquidity that references this tick + uint128 liquidityGross; + // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), + int128 liquidityNet; + // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint256 feeGrowthOutside0X128; + uint256 feeGrowthOutside1X128; + // the cumulative tick value on the other side of the tick + int56 tickCumulativeOutside; + // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint160 secondsPerLiquidityOutsideX128; + // the seconds spent on the other side of the tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint32 secondsOutside; + // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 + // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks + bool initialized; + } + + /// @notice Derives max liquidity per tick from given tick spacing + /// @dev Executed within the pool constructor + /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` + /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... + /// @return The max liquidity per tick + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; + return type(uint128).max / numTicks; + } + + /// @notice Retrieves fee growth data + /// @param self The mapping containing all tick information for initialized ticks + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @param tickCurrent The current tick + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function getFeeGrowthInside( + mapping(int24 => Tick.Info) storage self, + int24 tickLower, + int24 tickUpper, + int24 tickCurrent, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128 + ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + Info storage lower = self[tickLower]; + Info storage upper = self[tickUpper]; + + // calculate fee growth below + uint256 feeGrowthBelow0X128; + uint256 feeGrowthBelow1X128; + if (tickCurrent >= tickLower) { + feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; + } else { + feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; + } + + // calculate fee growth above + uint256 feeGrowthAbove0X128; + uint256 feeGrowthAbove1X128; + if (tickCurrent < tickUpper) { + feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; + } else { + feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; + } + + feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; + } + + /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The tick that will be updated + /// @param tickCurrent The current tick + /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool + /// @param time The current block timestamp cast to a uint32 + /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick + /// @param maxLiquidity The maximum liquidity allocation for a single tick + /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa + function update( + mapping(int24 => Tick.Info) storage self, + int24 tick, + int24 tickCurrent, + int128 liquidityDelta, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time, + bool upper, + uint128 maxLiquidity + ) internal returns (bool flipped) { + Tick.Info storage info = self[tick]; + + uint128 liquidityGrossBefore = info.liquidityGross; + uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); + + require(liquidityGrossAfter <= maxLiquidity, 'LO'); + + flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); + + if (liquidityGrossBefore == 0) { + // by convention, we assume that all growth before a tick was initialized happened _below_ the tick + if (tick <= tickCurrent) { + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; + info.tickCumulativeOutside = tickCumulative; + info.secondsOutside = time; + } + info.initialized = true; + } + + info.liquidityGross = liquidityGrossAfter; + + // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) + info.liquidityNet = upper + ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() + : int256(info.liquidityNet).add(liquidityDelta).toInt128(); + } + + /// @notice Clears tick data + /// @param self The mapping containing all initialized tick information for initialized ticks + /// @param tick The tick that will be cleared + function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { + delete self[tick]; + } + + /// @notice Transitions to next tick as needed by price movement + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The destination tick of the transition + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity + /// @param time The current block.timestamp + /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) + function cross( + mapping(int24 => Tick.Info) storage self, + int24 tick, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time + ) internal returns (int128 liquidityNet) { + Tick.Info storage info = self[tick]; + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; + info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; + info.secondsOutside = time - info.secondsOutside; + liquidityNet = info.liquidityNet; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol new file mode 100644 index 0000000000..3c43585771 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './BitMath.sol'; + +/// @title Packed tick initialized state library +/// @notice Stores a packed mapping of tick index to its initialized state +/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. +library TickBitmap { + /// @notice Computes the position in the mapping where the initialized bit for a tick lives + /// @param tick The tick for which to compute the position + /// @return wordPos The key in the mapping containing the word in which the bit is stored + /// @return bitPos The bit position in the word where the flag is stored + function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { + wordPos = int16(tick >> 8); + bitPos = uint8(tick % 256); + } + + /// @notice Flips the initialized state for a given tick from false to true, or vice versa + /// @param self The mapping in which to flip the tick + /// @param tick The tick to flip + /// @param tickSpacing The spacing between usable ticks + function flipTick( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing + ) internal { + require(tick % tickSpacing == 0); // ensure that the tick is spaced + (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); + uint256 mask = 1 << bitPos; + self[wordPos] ^= mask; + } + + /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either + /// to the left (less than or equal to) or right (greater than) of the given tick + /// @param self The mapping in which to compute the next initialized tick + /// @param tick The starting tick + /// @param tickSpacing The spacing between usable ticks + /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) + /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick + /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks + function nextInitializedTickWithinOneWord( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing, + bool lte + ) internal view returns (int24 next, bool initialized) { + int24 compressed = tick / tickSpacing; + if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + + if (lte) { + (int16 wordPos, uint8 bitPos) = position(compressed); + // all the 1s at or to the right of the current bitPos + uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing + : (compressed - int24(bitPos)) * tickSpacing; + } else { + // start from the word of the next tick, since the current tick state doesn't matter + (int16 wordPos, uint8 bitPos) = position(compressed + 1); + // all the 1s at or to the left of the bitPos + uint256 mask = ~((1 << bitPos) - 1); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the left of the current tick, return leftmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing + : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; + } + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol new file mode 100644 index 0000000000..378e44528c --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(MAX_TICK), 'T'); + + uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol new file mode 100644 index 0000000000..25d6309028 --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import '../interfaces/IERC20Minimal.sol'; + +/// @title TransferHelper +/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false +library TransferHelper { + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Calls transfer on token contract, errors with TF if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer( + address token, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); + } +} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol new file mode 100644 index 0000000000..f62f84676f --- /dev/null +++ b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math functions that do not check inputs or outputs +/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks +library UnsafeMath { + /// @notice Returns ceil(x / y) + /// @dev division by 0 has unspecified behavior, and must be checked externally + /// @param x The dividend + /// @param y The divisor + /// @return z The quotient, ceil(x / y) + function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := add(div(x, y), gt(mod(x, y), 0)) + } + } +} diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 9b0fe5a744..c2507ce114 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -325,7 +325,7 @@ def print_table(self) -> None: struct_var = info["elems"][item][key].get('struct_var') # doesn't handle deep keys currently - var_name_struct_or_array_var = "{} -> {}".format(var, struct_var) + var_name_struct_or_array_var = "{} -> {} -> {}".format(var, item, struct_var) if environ.get("TABLE_VALUE") is None: tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) From e178b37fc9cd6bb052672b1027e8fafb2cbeb747 Mon Sep 17 00:00:00 2001 From: noxx Date: Sun, 17 Jul 2022 21:06:27 +0100 Subject: [PATCH 05/23] remove crytic-export --- .../contracts/NoDelegateCall.sol | 27 - .../contracts/UniswapV3Pool.sol | 869 ------------------ .../contracts/interfaces/IERC20Minimal.sol | 52 -- .../interfaces/IUniswapV3Factory.sol | 78 -- .../contracts/interfaces/IUniswapV3Pool.sol | 24 - .../interfaces/IUniswapV3PoolDeployer.sol | 26 - .../callback/IUniswapV3FlashCallback.sol | 18 - .../callback/IUniswapV3MintCallback.sol | 18 - .../callback/IUniswapV3SwapCallback.sol | 21 - .../interfaces/pool/IUniswapV3PoolActions.sol | 103 --- .../pool/IUniswapV3PoolDerivedState.sol | 40 - .../interfaces/pool/IUniswapV3PoolEvents.sol | 121 --- .../pool/IUniswapV3PoolImmutables.sol | 35 - .../pool/IUniswapV3PoolOwnerActions.sol | 23 - .../interfaces/pool/IUniswapV3PoolState.sol | 116 --- .../contracts/libraries/BitMath.sol | 94 -- .../contracts/libraries/FixedPoint128.sol | 8 - .../contracts/libraries/FixedPoint96.sol | 10 - .../contracts/libraries/FullMath.sol | 124 --- .../contracts/libraries/LiquidityMath.sol | 17 - .../contracts/libraries/LowGasSafeMath.sol | 46 - .../contracts/libraries/Oracle.sol | 325 ------- .../contracts/libraries/Position.sol | 88 -- .../contracts/libraries/SafeCast.sol | 28 - .../contracts/libraries/SqrtPriceMath.sol | 227 ----- .../contracts/libraries/SwapMath.sol | 98 -- .../contracts/libraries/Tick.sol | 183 ---- .../contracts/libraries/TickBitmap.sol | 78 -- .../contracts/libraries/TickMath.sol | 205 ----- .../contracts/libraries/TransferHelper.sol | 23 - .../contracts/libraries/UnsafeMath.sol | 17 - 31 files changed, 3142 deletions(-) delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol delete mode 100644 crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol deleted file mode 100644 index 5411979dcb..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/NoDelegateCall.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.7.6; - -/// @title Prevents delegatecall to a contract -/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract -abstract contract NoDelegateCall { - /// @dev The original address of this contract - address private immutable original; - - constructor() { - // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. - // In other words, this variable won't change when it's checked at runtime. - original = address(this); - } - - /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, - /// and the use of immutable means the address bytes are copied in every place the modifier is used. - function checkNotDelegateCall() private view { - require(address(this) == original); - } - - /// @notice Prevents delegatecall into the modified method - modifier noDelegateCall() { - checkNotDelegateCall(); - _; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol deleted file mode 100644 index 9e09821272..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/UniswapV3Pool.sol +++ /dev/null @@ -1,869 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.7.6; - -import './interfaces/IUniswapV3Pool.sol'; - -import './NoDelegateCall.sol'; - -import './libraries/LowGasSafeMath.sol'; -import './libraries/SafeCast.sol'; -import './libraries/Tick.sol'; -import './libraries/TickBitmap.sol'; -import './libraries/Position.sol'; -import './libraries/Oracle.sol'; - -import './libraries/FullMath.sol'; -import './libraries/FixedPoint128.sol'; -import './libraries/TransferHelper.sol'; -import './libraries/TickMath.sol'; -import './libraries/LiquidityMath.sol'; -import './libraries/SqrtPriceMath.sol'; -import './libraries/SwapMath.sol'; - -import './interfaces/IUniswapV3PoolDeployer.sol'; -import './interfaces/IUniswapV3Factory.sol'; -import './interfaces/IERC20Minimal.sol'; -import './interfaces/callback/IUniswapV3MintCallback.sol'; -import './interfaces/callback/IUniswapV3SwapCallback.sol'; -import './interfaces/callback/IUniswapV3FlashCallback.sol'; - -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); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol deleted file mode 100644 index c303265a3b..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IERC20Minimal.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Minimal ERC20 interface for Uniswap -/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 -interface IERC20Minimal { - /// @notice Returns the balance of a token - /// @param account The account for which to look up the number of tokens it has, i.e. its balance - /// @return The number of tokens held by the account - function balanceOf(address account) external view returns (uint256); - - /// @notice Transfers the amount of token from the `msg.sender` to the recipient - /// @param recipient The account that will receive the amount transferred - /// @param amount The number of tokens to send from the sender to the recipient - /// @return Returns true for a successful transfer, false for an unsuccessful transfer - function transfer(address recipient, uint256 amount) external returns (bool); - - /// @notice Returns the current allowance given to a spender by an owner - /// @param owner The account of the token owner - /// @param spender The account of the token spender - /// @return The current allowance granted by `owner` to `spender` - function allowance(address owner, address spender) external view returns (uint256); - - /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` - /// @param spender The account which will be allowed to spend a given amount of the owners tokens - /// @param amount The amount of tokens allowed to be used by `spender` - /// @return Returns true for a successful approval, false for unsuccessful - function approve(address spender, uint256 amount) external returns (bool); - - /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` - /// @param sender The account from which the transfer will be initiated - /// @param recipient The recipient of the transfer - /// @param amount The amount of the transfer - /// @return Returns true for a successful transfer, false for unsuccessful - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); - - /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. - /// @param from The account from which the tokens were sent, i.e. the balance decreased - /// @param to The account to which the tokens were sent, i.e. the balance increased - /// @param value The amount of tokens that were transferred - event Transfer(address indexed from, address indexed to, uint256 value); - - /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. - /// @param owner The account that approved spending of its tokens - /// @param spender The account for which the spending allowance was modified - /// @param value The new allowance from the owner to the spender - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol deleted file mode 100644 index 540cfdc681..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Factory.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title The interface for the Uniswap V3 Factory -/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees -interface IUniswapV3Factory { - /// @notice Emitted when the owner of the factory is changed - /// @param oldOwner The owner before the owner was changed - /// @param newOwner The owner after the owner was changed - event OwnerChanged(address indexed oldOwner, address indexed newOwner); - - /// @notice Emitted when a pool is created - /// @param token0 The first token of the pool by address sort order - /// @param token1 The second token of the pool by address sort order - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @param tickSpacing The minimum number of ticks between initialized ticks - /// @param pool The address of the created pool - event PoolCreated( - address indexed token0, - address indexed token1, - uint24 indexed fee, - int24 tickSpacing, - address pool - ); - - /// @notice Emitted when a new fee amount is enabled for pool creation via the factory - /// @param fee The enabled fee, denominated in hundredths of a bip - /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee - event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); - - /// @notice Returns the current owner of the factory - /// @dev Can be changed by the current owner via setOwner - /// @return The address of the factory owner - function owner() external view returns (address); - - /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled - /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context - /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee - /// @return The tick spacing - function feeAmountTickSpacing(uint24 fee) external view returns (int24); - - /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist - /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order - /// @param tokenA The contract address of either token0 or token1 - /// @param tokenB The contract address of the other token - /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// @return pool The pool address - function getPool( - address tokenA, - address tokenB, - uint24 fee - ) external view returns (address pool); - - /// @notice Creates a pool for the given two tokens and fee - /// @param tokenA One of the two tokens in the desired pool - /// @param tokenB The other of the two tokens in the desired pool - /// @param fee The desired fee for the pool - /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved - /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments - /// are invalid. - /// @return pool The address of the newly created pool - function createPool( - address tokenA, - address tokenB, - uint24 fee - ) external returns (address pool); - - /// @notice Updates the owner of the factory - /// @dev Must be called by the current owner - /// @param _owner The new owner of the factory - function setOwner(address _owner) external; - - /// @notice Enables a fee amount with the given tickSpacing - /// @dev Fee amounts may never be removed once enabled - /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) - /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount - function enableFeeAmount(uint24 fee, int24 tickSpacing) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol deleted file mode 100644 index 56df0500df..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3Pool.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -import './pool/IUniswapV3PoolImmutables.sol'; -import './pool/IUniswapV3PoolState.sol'; -import './pool/IUniswapV3PoolDerivedState.sol'; -import './pool/IUniswapV3PoolActions.sol'; -import './pool/IUniswapV3PoolOwnerActions.sol'; -import './pool/IUniswapV3PoolEvents.sol'; - -/// @title The interface for a Uniswap V3 Pool -/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform -/// to the ERC20 specification -/// @dev The pool interface is broken up into many smaller pieces -interface IUniswapV3Pool is - IUniswapV3PoolImmutables, - IUniswapV3PoolState, - IUniswapV3PoolDerivedState, - IUniswapV3PoolActions, - IUniswapV3PoolOwnerActions, - IUniswapV3PoolEvents -{ - -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol deleted file mode 100644 index 72096c1ff5..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/IUniswapV3PoolDeployer.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools -/// @notice A contract that constructs a pool must implement this to pass arguments to the pool -/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash -/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain -interface IUniswapV3PoolDeployer { - /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. - /// @dev Called by the pool constructor to fetch the parameters of the pool - /// Returns factory The factory address - /// Returns token0 The first token of the pool by address sort order - /// Returns token1 The second token of the pool by address sort order - /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip - /// Returns tickSpacing The minimum number of ticks between initialized ticks - function parameters() - external - view - returns ( - address factory, - address token0, - address token1, - uint24 fee, - int24 tickSpacing - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol deleted file mode 100644 index 18e54c4e12..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3FlashCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#flash -/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface -interface IUniswapV3FlashCallback { - /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. - /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// @param fee0 The fee amount in token0 due to the pool by the end of the flash - /// @param fee1 The fee amount in token1 due to the pool by the end of the flash - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call - function uniswapV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol deleted file mode 100644 index 85447e84f9..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3MintCallback.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#mint -/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface -interface IUniswapV3MintCallback { - /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. - /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity - /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call - function uniswapV3MintCallback( - uint256 amount0Owed, - uint256 amount1Owed, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol deleted file mode 100644 index 9f183b22a7..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/callback/IUniswapV3SwapCallback.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Callback for IUniswapV3PoolActions#swap -/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface -interface IUniswapV3SwapCallback { - /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. - /// @dev In the implementation you must pay the pool tokens owed for the swap. - /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. - /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by - /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. - /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by - /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. - /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call - function uniswapV3SwapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol deleted file mode 100644 index 44fb61c24a..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolActions.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Permissionless pool actions -/// @notice Contains pool methods that can be called by anyone -interface IUniswapV3PoolActions { - /// @notice Sets the initial price for the pool - /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value - /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 - function initialize(uint160 sqrtPriceX96) external; - - /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position - /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback - /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends - /// on tickLower, tickUpper, the amount of liquidity, and the current price. - /// @param recipient The address for which the liquidity will be created - /// @param tickLower The lower tick of the position in which to add liquidity - /// @param tickUpper The upper tick of the position in which to add liquidity - /// @param amount The amount of liquidity to mint - /// @param data Any data that should be passed through to the callback - /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback - /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback - function mint( - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount, - bytes calldata data - ) external returns (uint256 amount0, uint256 amount1); - - /// @notice Collects tokens owed to a position - /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. - /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or - /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the - /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. - /// @param recipient The address which should receive the fees collected - /// @param tickLower The lower tick of the position for which to collect fees - /// @param tickUpper The upper tick of the position for which to collect fees - /// @param amount0Requested How much token0 should be withdrawn from the fees owed - /// @param amount1Requested How much token1 should be withdrawn from the fees owed - /// @return amount0 The amount of fees collected in token0 - /// @return amount1 The amount of fees collected in token1 - function collect( - address recipient, - int24 tickLower, - int24 tickUpper, - uint128 amount0Requested, - uint128 amount1Requested - ) external returns (uint128 amount0, uint128 amount1); - - /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position - /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 - /// @dev Fees must be collected separately via a call to #collect - /// @param tickLower The lower tick of the position for which to burn liquidity - /// @param tickUpper The upper tick of the position for which to burn liquidity - /// @param amount How much liquidity to burn - /// @return amount0 The amount of token0 sent to the recipient - /// @return amount1 The amount of token1 sent to the recipient - function burn( - int24 tickLower, - int24 tickUpper, - uint128 amount - ) external returns (uint256 amount0, uint256 amount1); - - /// @notice Swap token0 for token1, or token1 for token0 - /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback - /// @param recipient The address to receive the output of the swap - /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 - /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) - /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this - /// value after the swap. If one for zero, the price cannot be greater than this value after the swap - /// @param data Any data to be passed through to the callback - /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive - /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive - function swap( - address recipient, - bool zeroForOne, - int256 amountSpecified, - uint160 sqrtPriceLimitX96, - bytes calldata data - ) external returns (int256 amount0, int256 amount1); - - /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback - /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback - /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling - /// with 0 amount{0,1} and sending the donation amount(s) from the callback - /// @param recipient The address which will receive the token0 and token1 amounts - /// @param amount0 The amount of token0 to send - /// @param amount1 The amount of token1 to send - /// @param data Any data to be passed through to the callback - function flash( - address recipient, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) external; - - /// @notice Increase the maximum number of price and liquidity observations that this pool will store - /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to - /// the input observationCardinalityNext. - /// @param observationCardinalityNext The desired minimum number of observations for the pool to store - function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol deleted file mode 100644 index eda3a0089d..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that is not stored -/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the -/// blockchain. The functions here may have variable gas costs. -interface IUniswapV3PoolDerivedState { - /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp - /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing - /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, - /// you must call it with secondsAgos = [3600, 0]. - /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in - /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. - /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned - /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp - /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block - /// timestamp - function observe(uint32[] calldata secondsAgos) - external - view - returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); - - /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range - /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. - /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first - /// snapshot is taken and the second snapshot is taken. - /// @param tickLower The lower tick of the range - /// @param tickUpper The upper tick of the range - /// @return tickCumulativeInside The snapshot of the tick accumulator for the range - /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range - /// @return secondsInside The snapshot of seconds per liquidity for the range - function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) - external - view - returns ( - int56 tickCumulativeInside, - uint160 secondsPerLiquidityInsideX128, - uint32 secondsInside - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol deleted file mode 100644 index 9d915dde93..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolEvents.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Events emitted by a pool -/// @notice Contains all events emitted by the pool -interface IUniswapV3PoolEvents { - /// @notice Emitted exactly once by a pool when #initialize is first called on the pool - /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize - /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 - /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool - event Initialize(uint160 sqrtPriceX96, int24 tick); - - /// @notice Emitted when liquidity is minted for a given position - /// @param sender The address that minted the liquidity - /// @param owner The owner of the position and recipient of any minted liquidity - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount The amount of liquidity minted to the position range - /// @param amount0 How much token0 was required for the minted liquidity - /// @param amount1 How much token1 was required for the minted liquidity - event Mint( - address sender, - address indexed owner, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount, - uint256 amount0, - uint256 amount1 - ); - - /// @notice Emitted when fees are collected by the owner of a position - /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees - /// @param owner The owner of the position for which fees are collected - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount0 The amount of token0 fees collected - /// @param amount1 The amount of token1 fees collected - event Collect( - address indexed owner, - address recipient, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount0, - uint128 amount1 - ); - - /// @notice Emitted when a position's liquidity is removed - /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect - /// @param owner The owner of the position for which liquidity is removed - /// @param tickLower The lower tick of the position - /// @param tickUpper The upper tick of the position - /// @param amount The amount of liquidity to remove - /// @param amount0 The amount of token0 withdrawn - /// @param amount1 The amount of token1 withdrawn - event Burn( - address indexed owner, - int24 indexed tickLower, - int24 indexed tickUpper, - uint128 amount, - uint256 amount0, - uint256 amount1 - ); - - /// @notice Emitted by the pool for any swaps between token0 and token1 - /// @param sender The address that initiated the swap call, and that received the callback - /// @param recipient The address that received the output of the swap - /// @param amount0 The delta of the token0 balance of the pool - /// @param amount1 The delta of the token1 balance of the pool - /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 - /// @param liquidity The liquidity of the pool after the swap - /// @param tick The log base 1.0001 of price of the pool after the swap - event Swap( - address indexed sender, - address indexed recipient, - int256 amount0, - int256 amount1, - uint160 sqrtPriceX96, - uint128 liquidity, - int24 tick - ); - - /// @notice Emitted by the pool for any flashes of token0/token1 - /// @param sender The address that initiated the swap call, and that received the callback - /// @param recipient The address that received the tokens from flash - /// @param amount0 The amount of token0 that was flashed - /// @param amount1 The amount of token1 that was flashed - /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee - /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee - event Flash( - address indexed sender, - address indexed recipient, - uint256 amount0, - uint256 amount1, - uint256 paid0, - uint256 paid1 - ); - - /// @notice Emitted by the pool for increases to the number of observations that can be stored - /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index - /// just before a mint/swap/burn. - /// @param observationCardinalityNextOld The previous value of the next observation cardinality - /// @param observationCardinalityNextNew The updated value of the next observation cardinality - event IncreaseObservationCardinalityNext( - uint16 observationCardinalityNextOld, - uint16 observationCardinalityNextNew - ); - - /// @notice Emitted when the protocol fee is changed by the pool - /// @param feeProtocol0Old The previous value of the token0 protocol fee - /// @param feeProtocol1Old The previous value of the token1 protocol fee - /// @param feeProtocol0New The updated value of the token0 protocol fee - /// @param feeProtocol1New The updated value of the token1 protocol fee - event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); - - /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner - /// @param sender The address that collects the protocol fees - /// @param recipient The address that receives the collected protocol fees - /// @param amount0 The amount of token0 protocol fees that is withdrawn - /// @param amount0 The amount of token1 protocol fees that is withdrawn - event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol deleted file mode 100644 index c9beb151e3..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that never changes -/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values -interface IUniswapV3PoolImmutables { - /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface - /// @return The contract address - function factory() external view returns (address); - - /// @notice The first of the two tokens of the pool, sorted by address - /// @return The token contract address - function token0() external view returns (address); - - /// @notice The second of the two tokens of the pool, sorted by address - /// @return The token contract address - function token1() external view returns (address); - - /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 - /// @return The fee - function fee() external view returns (uint24); - - /// @notice The pool tick spacing - /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive - /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... - /// This value is an int24 to avoid casting even though it is always positive. - /// @return The tick spacing - function tickSpacing() external view returns (int24); - - /// @notice The maximum amount of position liquidity that can use any tick in the range - /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and - /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool - /// @return The max amount of liquidity per tick - function maxLiquidityPerTick() external view returns (uint128); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol deleted file mode 100644 index 2395ed321d..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Permissioned pool actions -/// @notice Contains pool methods that may only be called by the factory owner -interface IUniswapV3PoolOwnerActions { - /// @notice Set the denominator of the protocol's % share of the fees - /// @param feeProtocol0 new protocol fee for token0 of the pool - /// @param feeProtocol1 new protocol fee for token1 of the pool - function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; - - /// @notice Collect the protocol fee accrued to the pool - /// @param recipient The address to which collected protocol fees should be sent - /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 - /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 - /// @return amount0 The protocol fee collected in token0 - /// @return amount1 The protocol fee collected in token1 - function collectProtocol( - address recipient, - uint128 amount0Requested, - uint128 amount1Requested - ) external returns (uint128 amount0, uint128 amount1); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol deleted file mode 100644 index 620256c311..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/interfaces/pool/IUniswapV3PoolState.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Pool state that can change -/// @notice These methods compose the pool's state, and can change with any frequency including multiple times -/// per transaction -interface IUniswapV3PoolState { - /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas - /// when accessed externally. - /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value - /// tick The current tick of the pool, i.e. according to the last tick transition that was run. - /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick - /// boundary. - /// observationIndex The index of the last oracle observation that was written, - /// observationCardinality The current maximum number of observations stored in the pool, - /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. - /// feeProtocol The protocol fee for both tokens of the pool. - /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 - /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. - /// unlocked Whether the pool is currently locked to reentrancy - function slot0() - external - view - returns ( - uint160 sqrtPriceX96, - int24 tick, - uint16 observationIndex, - uint16 observationCardinality, - uint16 observationCardinalityNext, - uint8 feeProtocol, - bool unlocked - ); - - /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool - /// @dev This value can overflow the uint256 - function feeGrowthGlobal0X128() external view returns (uint256); - - /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool - /// @dev This value can overflow the uint256 - function feeGrowthGlobal1X128() external view returns (uint256); - - /// @notice The amounts of token0 and token1 that are owed to the protocol - /// @dev Protocol fees will never exceed uint128 max in either token - function protocolFees() external view returns (uint128 token0, uint128 token1); - - /// @notice The currently in range liquidity available to the pool - /// @dev This value has no relationship to the total liquidity across all ticks - function liquidity() external view returns (uint128); - - /// @notice Look up information about a specific tick in the pool - /// @param tick The tick to look up - /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or - /// tick upper, - /// liquidityNet how much liquidity changes when the pool price crosses the tick, - /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, - /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, - /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick - /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, - /// secondsOutside the seconds spent on the other side of the tick from the current tick, - /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. - /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. - /// In addition, these values are only relative and must be used only in comparison to previous snapshots for - /// a specific position. - function ticks(int24 tick) - external - view - returns ( - uint128 liquidityGross, - int128 liquidityNet, - uint256 feeGrowthOutside0X128, - uint256 feeGrowthOutside1X128, - int56 tickCumulativeOutside, - uint160 secondsPerLiquidityOutsideX128, - uint32 secondsOutside, - bool initialized - ); - - /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information - function tickBitmap(int16 wordPosition) external view returns (uint256); - - /// @notice Returns the information about a position by the position's key - /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper - /// @return _liquidity The amount of liquidity in the position, - /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, - /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, - /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, - /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke - function positions(bytes32 key) - external - view - returns ( - uint128 _liquidity, - uint256 feeGrowthInside0LastX128, - uint256 feeGrowthInside1LastX128, - uint128 tokensOwed0, - uint128 tokensOwed1 - ); - - /// @notice Returns data about a specific observation index - /// @param index The element of the observations array to fetch - /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time - /// ago, rather than at a specific index in the array. - /// @return blockTimestamp The timestamp of the observation, - /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, - /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, - /// Returns initialized whether the observation has been initialized and the values are safe to use - function observations(uint256 index) - external - view - returns ( - uint32 blockTimestamp, - int56 tickCumulative, - uint160 secondsPerLiquidityCumulativeX128, - bool initialized - ); -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol deleted file mode 100644 index 3a7216c7bd..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/BitMath.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title BitMath -/// @dev This library provides functionality for computing bit properties of an unsigned integer -library BitMath { - /// @notice Returns the index of the most significant bit of the number, - /// where the least significant bit is at index 0 and the most significant bit is at index 255 - /// @dev The function satisfies the property: - /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) - /// @param x the value for which to compute the most significant bit, must be greater than 0 - /// @return r the index of the most significant bit - function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { - require(x > 0); - - if (x >= 0x100000000000000000000000000000000) { - x >>= 128; - r += 128; - } - if (x >= 0x10000000000000000) { - x >>= 64; - r += 64; - } - if (x >= 0x100000000) { - x >>= 32; - r += 32; - } - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 0x4) { - x >>= 2; - r += 2; - } - if (x >= 0x2) r += 1; - } - - /// @notice Returns the index of the least significant bit of the number, - /// where the least significant bit is at index 0 and the most significant bit is at index 255 - /// @dev The function satisfies the property: - /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) - /// @param x the value for which to compute the least significant bit, must be greater than 0 - /// @return r the index of the least significant bit - function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { - require(x > 0); - - r = 255; - if (x & type(uint128).max > 0) { - r -= 128; - } else { - x >>= 128; - } - if (x & type(uint64).max > 0) { - r -= 64; - } else { - x >>= 64; - } - if (x & type(uint32).max > 0) { - r -= 32; - } else { - x >>= 32; - } - if (x & type(uint16).max > 0) { - r -= 16; - } else { - x >>= 16; - } - if (x & type(uint8).max > 0) { - r -= 8; - } else { - x >>= 8; - } - if (x & 0xf > 0) { - r -= 4; - } else { - x >>= 4; - } - if (x & 0x3 > 0) { - r -= 2; - } else { - x >>= 2; - } - if (x & 0x1 > 0) r -= 1; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol deleted file mode 100644 index 6d6948b106..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint128.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.4.0; - -/// @title FixedPoint128 -/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) -library FixedPoint128 { - uint256 internal constant Q128 = 0x100000000000000000000000000000000; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol deleted file mode 100644 index 63b42c294e..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FixedPoint96.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.4.0; - -/// @title FixedPoint96 -/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) -/// @dev Used in SqrtPriceMath.sol -library FixedPoint96 { - uint8 internal constant RESOLUTION = 96; - uint256 internal constant Q96 = 0x1000000000000000000000000; -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol deleted file mode 100644 index 8688a1773f..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/FullMath.sol +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.0; - -/// @title Contains 512-bit math functions -/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision -/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits -library FullMath { - /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - /// @param a The multiplicand - /// @param b The multiplier - /// @param denominator The divisor - /// @return result The 256-bit result - /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv - function mulDiv( - uint256 a, - uint256 b, - uint256 denominator - ) internal pure returns (uint256 result) { - // 512-bit multiply [prod1 prod0] = a * b - // Compute the product mod 2**256 and mod 2**256 - 1 - // then use the Chinese Remainder Theorem to reconstruct - // the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2**256 + prod0 - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(a, b, not(0)) - prod0 := mul(a, b) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - - // Handle non-overflow cases, 256 by 256 division - if (prod1 == 0) { - require(denominator > 0); - assembly { - result := div(prod0, denominator) - } - return result; - } - - // Make sure the result is less than 2**256. - // Also prevents denominator == 0 - require(denominator > prod1); - - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// - - // Make division exact by subtracting the remainder from [prod1 prod0] - // Compute remainder using mulmod - uint256 remainder; - assembly { - remainder := mulmod(a, b, denominator) - } - // Subtract 256 bit number from 512 bit number - assembly { - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) - } - - // Factor powers of two out of denominator - // Compute largest power of two divisor of denominator. - // Always >= 1. - uint256 twos = -denominator & denominator; - // Divide denominator by power of two - assembly { - denominator := div(denominator, twos) - } - - // Divide [prod1 prod0] by the factors of two - assembly { - prod0 := div(prod0, twos) - } - // Shift in bits from prod1 into prod0. For this we need - // to flip `twos` such that it is 2**256 / twos. - // If twos is zero, then it becomes one - assembly { - twos := add(div(sub(0, twos), twos), 1) - } - prod0 |= prod1 * twos; - - // Invert denominator mod 2**256 - // Now that denominator is an odd number, it has an inverse - // modulo 2**256 such that denominator * inv = 1 mod 2**256. - // Compute the inverse by starting with a seed that is correct - // correct for four bits. That is, denominator * inv = 1 mod 2**4 - uint256 inv = (3 * denominator) ^ 2; - // Now use Newton-Raphson iteration to improve the precision. - // Thanks to Hensel's lifting lemma, this also works in modular - // arithmetic, doubling the correct bits in each step. - inv *= 2 - denominator * inv; // inverse mod 2**8 - inv *= 2 - denominator * inv; // inverse mod 2**16 - inv *= 2 - denominator * inv; // inverse mod 2**32 - inv *= 2 - denominator * inv; // inverse mod 2**64 - inv *= 2 - denominator * inv; // inverse mod 2**128 - inv *= 2 - denominator * inv; // inverse mod 2**256 - - // Because the division is now exact we can divide by multiplying - // with the modular inverse of denominator. This will give us the - // correct result modulo 2**256. Since the precoditions guarantee - // that the outcome is less than 2**256, this is the final result. - // We don't need to compute the high bits of the result and prod1 - // is no longer required. - result = prod0 * inv; - return result; - } - - /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - /// @param a The multiplicand - /// @param b The multiplier - /// @param denominator The divisor - /// @return result The 256-bit result - function mulDivRoundingUp( - uint256 a, - uint256 b, - uint256 denominator - ) internal pure returns (uint256 result) { - result = mulDiv(a, b, denominator); - if (mulmod(a, b, denominator) > 0) { - require(result < type(uint256).max); - result++; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol deleted file mode 100644 index d5e23032e8..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LiquidityMath.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math library for liquidity -library LiquidityMath { - /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows - /// @param x The liquidity before change - /// @param y The delta by which liquidity should be changed - /// @return z The liquidity delta - function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { - if (y < 0) { - require((z = x - uint128(-y)) < x, 'LS'); - } else { - require((z = x + uint128(y)) >= x, 'LA'); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol deleted file mode 100644 index dbc817c2ec..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/LowGasSafeMath.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.7.0; - -/// @title Optimized overflow and underflow safe math operations -/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost -library LowGasSafeMath { - /// @notice Returns x + y, reverts if sum overflows uint256 - /// @param x The augend - /// @param y The addend - /// @return z The sum of x and y - function add(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x + y) >= x); - } - - /// @notice Returns x - y, reverts if underflows - /// @param x The minuend - /// @param y The subtrahend - /// @return z The difference of x and y - function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x - y) <= x); - } - - /// @notice Returns x * y, reverts if overflows - /// @param x The multiplicand - /// @param y The multiplier - /// @return z The product of x and y - function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { - require(x == 0 || (z = x * y) / x == y); - } - - /// @notice Returns x + y, reverts if overflows or underflows - /// @param x The augend - /// @param y The addend - /// @return z The sum of x and y - function add(int256 x, int256 y) internal pure returns (int256 z) { - require((z = x + y) >= x == (y >= 0)); - } - - /// @notice Returns x - y, reverts if overflows or underflows - /// @param x The minuend - /// @param y The subtrahend - /// @return z The difference of x and y - function sub(int256 x, int256 y) internal pure returns (int256 z) { - require((z = x - y) <= x == (y >= 0)); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol deleted file mode 100644 index 3f6b3f32c9..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Oracle.sol +++ /dev/null @@ -1,325 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -/// @title Oracle -/// @notice Provides price and liquidity data useful for a wide variety of system designs -/// @dev Instances of stored oracle data, "observations", are collected in the oracle array -/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the -/// maximum length of the oracle array. New slots will be added when the array is fully populated. -/// Observations are overwritten when the full length of the oracle array is populated. -/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() -library Oracle { - struct Observation { - // the block timestamp of the observation - uint32 blockTimestamp; - // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized - int56 tickCumulative; - // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized - uint160 secondsPerLiquidityCumulativeX128; - // whether or not the observation is initialized - bool initialized; - } - - /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values - /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows - /// @param last The specified observation to be transformed - /// @param blockTimestamp The timestamp of the new observation - /// @param tick The active tick at the time of the new observation - /// @param liquidity The total in-range liquidity at the time of the new observation - /// @return Observation The newly populated observation - function transform( - Observation memory last, - uint32 blockTimestamp, - int24 tick, - uint128 liquidity - ) private pure returns (Observation memory) { - uint32 delta = blockTimestamp - last.blockTimestamp; - return - Observation({ - blockTimestamp: blockTimestamp, - tickCumulative: last.tickCumulative + int56(tick) * delta, - secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + - ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), - initialized: true - }); - } - - /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array - /// @param self The stored oracle array - /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 - /// @return cardinality The number of populated elements in the oracle array - /// @return cardinalityNext The new length of the oracle array, independent of population - function initialize(Observation[65535] storage self, uint32 time) - internal - returns (uint16 cardinality, uint16 cardinalityNext) - { - self[0] = Observation({ - blockTimestamp: time, - tickCumulative: 0, - secondsPerLiquidityCumulativeX128: 0, - initialized: true - }); - return (1, 1); - } - - /// @notice Writes an oracle observation to the array - /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. - /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality - /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. - /// @param self The stored oracle array - /// @param index The index of the observation that was most recently written to the observations array - /// @param blockTimestamp The timestamp of the new observation - /// @param tick The active tick at the time of the new observation - /// @param liquidity The total in-range liquidity at the time of the new observation - /// @param cardinality The number of populated elements in the oracle array - /// @param cardinalityNext The new length of the oracle array, independent of population - /// @return indexUpdated The new index of the most recently written element in the oracle array - /// @return cardinalityUpdated The new cardinality of the oracle array - function write( - Observation[65535] storage self, - uint16 index, - uint32 blockTimestamp, - int24 tick, - uint128 liquidity, - uint16 cardinality, - uint16 cardinalityNext - ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { - Observation memory last = self[index]; - - // early return if we've already written an observation this block - if (last.blockTimestamp == blockTimestamp) return (index, cardinality); - - // if the conditions are right, we can bump the cardinality - if (cardinalityNext > cardinality && index == (cardinality - 1)) { - cardinalityUpdated = cardinalityNext; - } else { - cardinalityUpdated = cardinality; - } - - indexUpdated = (index + 1) % cardinalityUpdated; - self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); - } - - /// @notice Prepares the oracle array to store up to `next` observations - /// @param self The stored oracle array - /// @param current The current next cardinality of the oracle array - /// @param next The proposed next cardinality which will be populated in the oracle array - /// @return next The next cardinality which will be populated in the oracle array - function grow( - Observation[65535] storage self, - uint16 current, - uint16 next - ) internal returns (uint16) { - require(current > 0, 'I'); - // no-op if the passed next value isn't greater than the current next value - if (next <= current) return current; - // store in each slot to prevent fresh SSTOREs in swaps - // this data will not be used because the initialized boolean is still false - for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; - return next; - } - - /// @notice comparator for 32-bit timestamps - /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time - /// @param time A timestamp truncated to 32 bits - /// @param a A comparison timestamp from which to determine the relative position of `time` - /// @param b From which to determine the relative position of `time` - /// @return bool Whether `a` is chronologically <= `b` - function lte( - uint32 time, - uint32 a, - uint32 b - ) private pure returns (bool) { - // if there hasn't been overflow, no need to adjust - if (a <= time && b <= time) return a <= b; - - uint256 aAdjusted = a > time ? a : a + 2**32; - uint256 bAdjusted = b > time ? b : b + 2**32; - - return aAdjusted <= bAdjusted; - } - - /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. - /// The result may be the same observation, or adjacent observations. - /// @dev The answer must be contained in the array, used when the target is located within the stored observation - /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param target The timestamp at which the reserved observation should be for - /// @param index The index of the observation that was most recently written to the observations array - /// @param cardinality The number of populated elements in the oracle array - /// @return beforeOrAt The observation recorded before, or at, the target - /// @return atOrAfter The observation recorded at, or after, the target - function binarySearch( - Observation[65535] storage self, - uint32 time, - uint32 target, - uint16 index, - uint16 cardinality - ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - uint256 l = (index + 1) % cardinality; // oldest observation - uint256 r = l + cardinality - 1; // newest observation - uint256 i; - while (true) { - i = (l + r) / 2; - - beforeOrAt = self[i % cardinality]; - - // we've landed on an uninitialized tick, keep searching higher (more recently) - if (!beforeOrAt.initialized) { - l = i + 1; - continue; - } - - atOrAfter = self[(i + 1) % cardinality]; - - bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); - - // check if we've found the answer! - if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; - - if (!targetAtOrAfter) r = i - 1; - else l = i + 1; - } - } - - /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied - /// @dev Assumes there is at least 1 initialized observation. - /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param target The timestamp at which the reserved observation should be for - /// @param tick The active tick at the time of the returned or simulated observation - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The total pool liquidity at the time of the call - /// @param cardinality The number of populated elements in the oracle array - /// @return beforeOrAt The observation which occurred at, or before, the given timestamp - /// @return atOrAfter The observation which occurred at, or after, the given timestamp - function getSurroundingObservations( - Observation[65535] storage self, - uint32 time, - uint32 target, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - // optimistically set before to the newest observation - beforeOrAt = self[index]; - - // if the target is chronologically at or after the newest observation, we can early return - if (lte(time, beforeOrAt.blockTimestamp, target)) { - if (beforeOrAt.blockTimestamp == target) { - // if newest observation equals target, we're in the same block, so we can ignore atOrAfter - return (beforeOrAt, atOrAfter); - } else { - // otherwise, we need to transform - return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); - } - } - - // now, set before to the oldest observation - beforeOrAt = self[(index + 1) % cardinality]; - if (!beforeOrAt.initialized) beforeOrAt = self[0]; - - // ensure that the target is chronologically at or after the oldest observation - require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); - - // if we've reached this point, we have to binary search - return binarySearch(self, time, target, index, cardinality); - } - - /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. - /// 0 may be passed as `secondsAgo' to return the current cumulative values. - /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values - /// at exactly the timestamp between the two observations. - /// @param self The stored oracle array - /// @param time The current block timestamp - /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation - /// @param tick The current tick - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The current in-range pool liquidity - /// @param cardinality The number of populated elements in the oracle array - /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` - /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` - function observeSingle( - Observation[65535] storage self, - uint32 time, - uint32 secondsAgo, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { - if (secondsAgo == 0) { - Observation memory last = self[index]; - if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); - return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); - } - - uint32 target = time - secondsAgo; - - (Observation memory beforeOrAt, Observation memory atOrAfter) = - getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); - - if (target == beforeOrAt.blockTimestamp) { - // we're at the left boundary - return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); - } else if (target == atOrAfter.blockTimestamp) { - // we're at the right boundary - return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); - } else { - // we're in the middle - uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; - uint32 targetDelta = target - beforeOrAt.blockTimestamp; - return ( - beforeOrAt.tickCumulative + - ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * - targetDelta, - beforeOrAt.secondsPerLiquidityCumulativeX128 + - uint160( - (uint256( - atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 - ) * targetDelta) / observationTimeDelta - ) - ); - } - } - - /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` - /// @dev Reverts if `secondsAgos` > oldest observation - /// @param self The stored oracle array - /// @param time The current block.timestamp - /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation - /// @param tick The current tick - /// @param index The index of the observation that was most recently written to the observations array - /// @param liquidity The current in-range pool liquidity - /// @param cardinality The number of populated elements in the oracle array - /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` - /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` - function observe( - Observation[65535] storage self, - uint32 time, - uint32[] memory secondsAgos, - int24 tick, - uint16 index, - uint128 liquidity, - uint16 cardinality - ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { - require(cardinality > 0, 'I'); - - tickCumulatives = new int56[](secondsAgos.length); - secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); - for (uint256 i = 0; i < secondsAgos.length; i++) { - (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( - self, - time, - secondsAgos[i], - tick, - index, - liquidity, - cardinality - ); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol deleted file mode 100644 index 1c67c7f279..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Position.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './FullMath.sol'; -import './FixedPoint128.sol'; -import './LiquidityMath.sol'; - -/// @title Position -/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary -/// @dev Positions store additional state for tracking fees owed to the position -library Position { - // info stored for each user's position - struct Info { - // the amount of liquidity owned by this position - uint128 liquidity; - // fee growth per unit of liquidity as of the last update to liquidity or fees owed - uint256 feeGrowthInside0LastX128; - uint256 feeGrowthInside1LastX128; - // the fees owed to the position owner in token0/token1 - uint128 tokensOwed0; - uint128 tokensOwed1; - } - - /// @notice Returns the Info struct of a position, given an owner and position boundaries - /// @param self The mapping containing all user positions - /// @param owner The address of the position owner - /// @param tickLower The lower tick boundary of the position - /// @param tickUpper The upper tick boundary of the position - /// @return position The position info struct of the given owners' position - function get( - mapping(bytes32 => Info) storage self, - address owner, - int24 tickLower, - int24 tickUpper - ) internal view returns (Position.Info storage position) { - position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; - } - - /// @notice Credits accumulated fees to a user's position - /// @param self The individual position to update - /// @param liquidityDelta The change in pool liquidity as a result of the position update - /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries - /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries - function update( - Info storage self, - int128 liquidityDelta, - uint256 feeGrowthInside0X128, - uint256 feeGrowthInside1X128 - ) internal { - Info memory _self = self; - - uint128 liquidityNext; - if (liquidityDelta == 0) { - require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions - liquidityNext = _self.liquidity; - } else { - liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); - } - - // calculate accumulated fees - uint128 tokensOwed0 = - uint128( - FullMath.mulDiv( - feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) - ); - uint128 tokensOwed1 = - uint128( - FullMath.mulDiv( - feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, - _self.liquidity, - FixedPoint128.Q128 - ) - ); - - // update the position - if (liquidityDelta != 0) self.liquidity = liquidityNext; - self.feeGrowthInside0LastX128 = feeGrowthInside0X128; - self.feeGrowthInside1LastX128 = feeGrowthInside1X128; - if (tokensOwed0 > 0 || tokensOwed1 > 0) { - // overflow is acceptable, have to withdraw before you hit type(uint128).max fees - self.tokensOwed0 += tokensOwed0; - self.tokensOwed1 += tokensOwed1; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol deleted file mode 100644 index a8ea229878..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SafeCast.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Safe casting methods -/// @notice Contains methods for safely casting between types -library SafeCast { - /// @notice Cast a uint256 to a uint160, revert on overflow - /// @param y The uint256 to be downcasted - /// @return z The downcasted integer, now type uint160 - function toUint160(uint256 y) internal pure returns (uint160 z) { - require((z = uint160(y)) == y); - } - - /// @notice Cast a int256 to a int128, revert on overflow or underflow - /// @param y The int256 to be downcasted - /// @return z The downcasted integer, now type int128 - function toInt128(int256 y) internal pure returns (int128 z) { - require((z = int128(y)) == y); - } - - /// @notice Cast a uint256 to a int256, revert on overflow - /// @param y The uint256 to be casted - /// @return z The casted integer, now type int256 - function toInt256(uint256 y) internal pure returns (int256 z) { - require(y < 2**255); - z = int256(y); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol deleted file mode 100644 index 685f485da4..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SqrtPriceMath.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './LowGasSafeMath.sol'; -import './SafeCast.sol'; - -import './FullMath.sol'; -import './UnsafeMath.sol'; -import './FixedPoint96.sol'; - -/// @title Functions based on Q64.96 sqrt price and liquidity -/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas -library SqrtPriceMath { - using LowGasSafeMath for uint256; - using SafeCast for uint256; - - /// @notice Gets the next sqrt price given a delta of token0 - /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least - /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the - /// price less in order to not send too much output. - /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), - /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). - /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta - /// @param liquidity The amount of usable liquidity - /// @param amount How much of token0 to add or remove from virtual reserves - /// @param add Whether to add or remove the amount of token0 - /// @return The price after adding or removing amount, depending on add - function getNextSqrtPriceFromAmount0RoundingUp( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amount, - bool add - ) internal pure returns (uint160) { - // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price - if (amount == 0) return sqrtPX96; - uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; - - if (add) { - uint256 product; - if ((product = amount * sqrtPX96) / amount == sqrtPX96) { - uint256 denominator = numerator1 + product; - if (denominator >= numerator1) - // always fits in 160 bits - return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); - } - - return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); - } else { - uint256 product; - // if the product overflows, we know the denominator underflows - // in addition, we must check that the denominator does not underflow - require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); - uint256 denominator = numerator1 - product; - return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); - } - } - - /// @notice Gets the next sqrt price given a delta of token1 - /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least - /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the - /// price less in order to not send too much output. - /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity - /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta - /// @param liquidity The amount of usable liquidity - /// @param amount How much of token1 to add, or remove, from virtual reserves - /// @param add Whether to add, or remove, the amount of token1 - /// @return The price after adding or removing `amount` - function getNextSqrtPriceFromAmount1RoundingDown( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amount, - bool add - ) internal pure returns (uint160) { - // if we're adding (subtracting), rounding down requires rounding the quotient down (up) - // in both cases, avoid a mulDiv for most inputs - if (add) { - uint256 quotient = - ( - amount <= type(uint160).max - ? (amount << FixedPoint96.RESOLUTION) / liquidity - : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) - ); - - return uint256(sqrtPX96).add(quotient).toUint160(); - } else { - uint256 quotient = - ( - amount <= type(uint160).max - ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) - : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) - ); - - require(sqrtPX96 > quotient); - // always fits 160 bits - return uint160(sqrtPX96 - quotient); - } - } - - /// @notice Gets the next sqrt price given an input amount of token0 or token1 - /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds - /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount - /// @param liquidity The amount of usable liquidity - /// @param amountIn How much of token0, or token1, is being swapped in - /// @param zeroForOne Whether the amount in is token0 or token1 - /// @return sqrtQX96 The price after adding the input amount to token0 or token1 - function getNextSqrtPriceFromInput( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amountIn, - bool zeroForOne - ) internal pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); - - // round to make sure that we don't pass the target price - return - zeroForOne - ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) - : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); - } - - /// @notice Gets the next sqrt price given an output amount of token0 or token1 - /// @dev Throws if price or liquidity are 0 or the next price is out of bounds - /// @param sqrtPX96 The starting price before accounting for the output amount - /// @param liquidity The amount of usable liquidity - /// @param amountOut How much of token0, or token1, is being swapped out - /// @param zeroForOne Whether the amount out is token0 or token1 - /// @return sqrtQX96 The price after removing the output amount of token0 or token1 - function getNextSqrtPriceFromOutput( - uint160 sqrtPX96, - uint128 liquidity, - uint256 amountOut, - bool zeroForOne - ) internal pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); - - // round to make sure that we pass the target price - return - zeroForOne - ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) - : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); - } - - /// @notice Gets the amount0 delta between two prices - /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), - /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The amount of usable liquidity - /// @param roundUp Whether to round the amount up or down - /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices - function getAmount0Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - uint128 liquidity, - bool roundUp - ) internal pure returns (uint256 amount0) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - - uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; - uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; - - require(sqrtRatioAX96 > 0); - - return - roundUp - ? UnsafeMath.divRoundingUp( - FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), - sqrtRatioAX96 - ) - : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; - } - - /// @notice Gets the amount1 delta between two prices - /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The amount of usable liquidity - /// @param roundUp Whether to round the amount up, or down - /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices - function getAmount1Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - uint128 liquidity, - bool roundUp - ) internal pure returns (uint256 amount1) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - - return - roundUp - ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) - : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); - } - - /// @notice Helper that gets signed token0 delta - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The change in liquidity for which to compute the amount0 delta - /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices - function getAmount0Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - int128 liquidity - ) internal pure returns (int256 amount0) { - return - liquidity < 0 - ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } - - /// @notice Helper that gets signed token1 delta - /// @param sqrtRatioAX96 A sqrt price - /// @param sqrtRatioBX96 Another sqrt price - /// @param liquidity The change in liquidity for which to compute the amount1 delta - /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices - function getAmount1Delta( - uint160 sqrtRatioAX96, - uint160 sqrtRatioBX96, - int128 liquidity - ) internal pure returns (int256 amount1) { - return - liquidity < 0 - ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol deleted file mode 100644 index ee176fbee4..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/SwapMath.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './FullMath.sol'; -import './SqrtPriceMath.sol'; - -/// @title Computes the result of a swap within ticks -/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. -library SwapMath { - /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap - /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive - /// @param sqrtRatioCurrentX96 The current sqrt price of the pool - /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred - /// @param liquidity The usable liquidity - /// @param amountRemaining How much input or output amount is remaining to be swapped in/out - /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip - /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target - /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap - /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap - /// @return feeAmount The amount of input that will be taken as a fee - function computeSwapStep( - uint160 sqrtRatioCurrentX96, - uint160 sqrtRatioTargetX96, - uint128 liquidity, - int256 amountRemaining, - uint24 feePips - ) - internal - pure - returns ( - uint160 sqrtRatioNextX96, - uint256 amountIn, - uint256 amountOut, - uint256 feeAmount - ) - { - bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; - bool exactIn = amountRemaining >= 0; - - if (exactIn) { - uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); - amountIn = zeroForOne - ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) - : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); - if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; - else - sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( - sqrtRatioCurrentX96, - liquidity, - amountRemainingLessFee, - zeroForOne - ); - } else { - amountOut = zeroForOne - ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) - : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); - if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; - else - sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( - sqrtRatioCurrentX96, - liquidity, - uint256(-amountRemaining), - zeroForOne - ); - } - - bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; - - // get the input/output amounts - if (zeroForOne) { - amountIn = max && exactIn - ? amountIn - : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); - } else { - amountIn = max && exactIn - ? amountIn - : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); - amountOut = max && !exactIn - ? amountOut - : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); - } - - // cap the output amount to not exceed the remaining output amount - if (!exactIn && amountOut > uint256(-amountRemaining)) { - amountOut = uint256(-amountRemaining); - } - - if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { - // we didn't reach the target, so take the remainder of the maximum input as fee - feeAmount = uint256(amountRemaining) - amountIn; - } else { - feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol deleted file mode 100644 index 13d3428494..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/Tick.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './LowGasSafeMath.sol'; -import './SafeCast.sol'; - -import './TickMath.sol'; -import './LiquidityMath.sol'; - -/// @title Tick -/// @notice Contains functions for managing tick processes and relevant calculations -library Tick { - using LowGasSafeMath for int256; - using SafeCast for int256; - - // info stored for each initialized individual tick - struct Info { - // the total position liquidity that references this tick - uint128 liquidityGross; - // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), - int128 liquidityNet; - // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint256 feeGrowthOutside0X128; - uint256 feeGrowthOutside1X128; - // the cumulative tick value on the other side of the tick - int56 tickCumulativeOutside; - // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint160 secondsPerLiquidityOutsideX128; - // the seconds spent on the other side of the tick (relative to the current tick) - // only has relative meaning, not absolute — the value depends on when the tick is initialized - uint32 secondsOutside; - // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 - // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks - bool initialized; - } - - /// @notice Derives max liquidity per tick from given tick spacing - /// @dev Executed within the pool constructor - /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` - /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... - /// @return The max liquidity per tick - function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { - int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; - int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; - uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; - return type(uint128).max / numTicks; - } - - /// @notice Retrieves fee growth data - /// @param self The mapping containing all tick information for initialized ticks - /// @param tickLower The lower tick boundary of the position - /// @param tickUpper The upper tick boundary of the position - /// @param tickCurrent The current tick - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries - /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries - function getFeeGrowthInside( - mapping(int24 => Tick.Info) storage self, - int24 tickLower, - int24 tickUpper, - int24 tickCurrent, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128 - ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { - Info storage lower = self[tickLower]; - Info storage upper = self[tickUpper]; - - // calculate fee growth below - uint256 feeGrowthBelow0X128; - uint256 feeGrowthBelow1X128; - if (tickCurrent >= tickLower) { - feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; - feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; - } else { - feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; - feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; - } - - // calculate fee growth above - uint256 feeGrowthAbove0X128; - uint256 feeGrowthAbove1X128; - if (tickCurrent < tickUpper) { - feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; - feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; - } else { - feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; - feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; - } - - feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; - feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; - } - - /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa - /// @param self The mapping containing all tick information for initialized ticks - /// @param tick The tick that will be updated - /// @param tickCurrent The current tick - /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool - /// @param time The current block timestamp cast to a uint32 - /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick - /// @param maxLiquidity The maximum liquidity allocation for a single tick - /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa - function update( - mapping(int24 => Tick.Info) storage self, - int24 tick, - int24 tickCurrent, - int128 liquidityDelta, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128, - uint160 secondsPerLiquidityCumulativeX128, - int56 tickCumulative, - uint32 time, - bool upper, - uint128 maxLiquidity - ) internal returns (bool flipped) { - Tick.Info storage info = self[tick]; - - uint128 liquidityGrossBefore = info.liquidityGross; - uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); - - require(liquidityGrossAfter <= maxLiquidity, 'LO'); - - flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); - - if (liquidityGrossBefore == 0) { - // by convention, we assume that all growth before a tick was initialized happened _below_ the tick - if (tick <= tickCurrent) { - info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; - info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; - info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; - info.tickCumulativeOutside = tickCumulative; - info.secondsOutside = time; - } - info.initialized = true; - } - - info.liquidityGross = liquidityGrossAfter; - - // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) - info.liquidityNet = upper - ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() - : int256(info.liquidityNet).add(liquidityDelta).toInt128(); - } - - /// @notice Clears tick data - /// @param self The mapping containing all initialized tick information for initialized ticks - /// @param tick The tick that will be cleared - function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { - delete self[tick]; - } - - /// @notice Transitions to next tick as needed by price movement - /// @param self The mapping containing all tick information for initialized ticks - /// @param tick The destination tick of the transition - /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 - /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 - /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity - /// @param time The current block.timestamp - /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) - function cross( - mapping(int24 => Tick.Info) storage self, - int24 tick, - uint256 feeGrowthGlobal0X128, - uint256 feeGrowthGlobal1X128, - uint160 secondsPerLiquidityCumulativeX128, - int56 tickCumulative, - uint32 time - ) internal returns (int128 liquidityNet) { - Tick.Info storage info = self[tick]; - info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; - info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; - info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; - info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; - info.secondsOutside = time - info.secondsOutside; - liquidityNet = info.liquidityNet; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol deleted file mode 100644 index 3c43585771..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickBitmap.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import './BitMath.sol'; - -/// @title Packed tick initialized state library -/// @notice Stores a packed mapping of tick index to its initialized state -/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. -library TickBitmap { - /// @notice Computes the position in the mapping where the initialized bit for a tick lives - /// @param tick The tick for which to compute the position - /// @return wordPos The key in the mapping containing the word in which the bit is stored - /// @return bitPos The bit position in the word where the flag is stored - function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { - wordPos = int16(tick >> 8); - bitPos = uint8(tick % 256); - } - - /// @notice Flips the initialized state for a given tick from false to true, or vice versa - /// @param self The mapping in which to flip the tick - /// @param tick The tick to flip - /// @param tickSpacing The spacing between usable ticks - function flipTick( - mapping(int16 => uint256) storage self, - int24 tick, - int24 tickSpacing - ) internal { - require(tick % tickSpacing == 0); // ensure that the tick is spaced - (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); - uint256 mask = 1 << bitPos; - self[wordPos] ^= mask; - } - - /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either - /// to the left (less than or equal to) or right (greater than) of the given tick - /// @param self The mapping in which to compute the next initialized tick - /// @param tick The starting tick - /// @param tickSpacing The spacing between usable ticks - /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) - /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick - /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks - function nextInitializedTickWithinOneWord( - mapping(int16 => uint256) storage self, - int24 tick, - int24 tickSpacing, - bool lte - ) internal view returns (int24 next, bool initialized) { - int24 compressed = tick / tickSpacing; - if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity - - if (lte) { - (int16 wordPos, uint8 bitPos) = position(compressed); - // all the 1s at or to the right of the current bitPos - uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); - uint256 masked = self[wordPos] & mask; - - // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word - initialized = masked != 0; - // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick - next = initialized - ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing - : (compressed - int24(bitPos)) * tickSpacing; - } else { - // start from the word of the next tick, since the current tick state doesn't matter - (int16 wordPos, uint8 bitPos) = position(compressed + 1); - // all the 1s at or to the left of the bitPos - uint256 mask = ~((1 << bitPos) - 1); - uint256 masked = self[wordPos] & mask; - - // if there are no initialized ticks to the left of the current tick, return leftmost in the word - initialized = masked != 0; - // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick - next = initialized - ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing - : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; - } - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol deleted file mode 100644 index 378e44528c..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TickMath.sol +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math library for computing sqrt prices from ticks and vice versa -/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports -/// prices between 2**-128 and 2**128 -library TickMath { - /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 - int24 internal constant MIN_TICK = -887272; - /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 - int24 internal constant MAX_TICK = -MIN_TICK; - - /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) - uint160 internal constant MIN_SQRT_RATIO = 4295128739; - /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) - uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; - - /// @notice Calculates sqrt(1.0001^tick) * 2^96 - /// @dev Throws if |tick| > max tick - /// @param tick The input tick for the above formula - /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) - /// at the given tick - function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { - uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); - require(absTick <= uint256(MAX_TICK), 'T'); - - uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; - if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; - if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; - if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; - if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; - if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; - if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; - if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; - if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; - if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; - if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; - if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; - if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; - if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; - if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; - if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; - if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; - if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; - if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; - if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; - - if (tick > 0) ratio = type(uint256).max / ratio; - - // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. - // we then downcast because we know the result always fits within 160 bits due to our tick input constraint - // we round up in the division so getTickAtSqrtRatio of the output price is always consistent - sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); - } - - /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio - /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may - /// ever return. - /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 - /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio - function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { - // second inequality must be < because the price can never reach the price at the max tick - require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); - uint256 ratio = uint256(sqrtPriceX96) << 32; - - uint256 r = ratio; - uint256 msb = 0; - - assembly { - let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(5, gt(r, 0xFFFFFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(4, gt(r, 0xFFFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(3, gt(r, 0xFF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(2, gt(r, 0xF)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := shl(1, gt(r, 0x3)) - msb := or(msb, f) - r := shr(f, r) - } - assembly { - let f := gt(r, 0x1) - msb := or(msb, f) - } - - if (msb >= 128) r = ratio >> (msb - 127); - else r = ratio << (127 - msb); - - int256 log_2 = (int256(msb) - 128) << 64; - - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(63, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(62, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(61, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(60, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(59, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(58, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(57, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(56, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(55, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(54, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(53, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(52, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(51, f)) - r := shr(f, r) - } - assembly { - r := shr(127, mul(r, r)) - let f := shr(128, r) - log_2 := or(log_2, shl(50, f)) - } - - int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number - - int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); - int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); - - tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol deleted file mode 100644 index 25d6309028..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/TransferHelper.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.6.0; - -import '../interfaces/IERC20Minimal.sol'; - -/// @title TransferHelper -/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false -library TransferHelper { - /// @notice Transfers tokens from msg.sender to a recipient - /// @dev Calls transfer on token contract, errors with TF if transfer fails - /// @param token The contract address of the token which will be transferred - /// @param to The recipient of the transfer - /// @param value The value of the transfer - function safeTransfer( - address token, - address to, - uint256 value - ) internal { - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); - } -} diff --git a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol b/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol deleted file mode 100644 index f62f84676f..0000000000 --- a/crytic-export/etherscan-contracts/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8-UniswapV3Pool/contracts/libraries/UnsafeMath.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -/// @title Math functions that do not check inputs or outputs -/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks -library UnsafeMath { - /// @notice Returns ceil(x / y) - /// @dev division by 0 has unspecified behavior, and must be checked externally - /// @param x The dividend - /// @param y The divisor - /// @return z The quotient, ceil(x / y) - function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - assembly { - z := add(div(x, y), gt(mod(x, y), 0)) - } - } -} From d8455e3213ef587f73cbe0ca55118e8ee881a311 Mon Sep 17 00:00:00 2001 From: noxx Date: Mon, 18 Jul 2022 12:43:35 +0100 Subject: [PATCH 06/23] add square brackets to make it clear that the number is an array index of observations --- .gitignore | 3 +++ slither/tools/read_storage/read_storage.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 096634e91f..fe1019a11d 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,6 @@ ENV/ # Test results test_artifacts/ + +# crytic export +crytic-export/ diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index c2507ce114..daa891ed14 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -325,7 +325,7 @@ def print_table(self) -> None: struct_var = info["elems"][item][key].get('struct_var') # doesn't handle deep keys currently - var_name_struct_or_array_var = "{} -> {} -> {}".format(var, item, struct_var) + var_name_struct_or_array_var = "{}[{}] -> {}".format(var, item, struct_var) if environ.get("TABLE_VALUE") is None: tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) From 3d690088e3f434458a7391fbb6d9ac5f214157a3 Mon Sep 17 00:00:00 2001 From: noxx Date: Thu, 21 Jul 2022 07:09:02 +0100 Subject: [PATCH 07/23] lintig and tabulate install on github action --- .github/workflows/read_storage.yml | 1 + slither/tools/read_storage/read_storage.py | 117 ++++++++++++--------- 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/.github/workflows/read_storage.yml b/.github/workflows/read_storage.yml index 2ca618a19c..74d14c39ce 100644 --- a/.github/workflows/read_storage.yml +++ b/.github/workflows/read_storage.yml @@ -40,6 +40,7 @@ jobs: pip install pytest==7.0.1 pip install typing_extensions==4.1.1 pip install importlib_metadata==4.8.3 + pip install tabulate==0.8.10 solc-select install 0.8.1 solc-select install 0.8.10 solc-select use 0.8.1 diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index daa891ed14..6d72b9c122 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -145,7 +145,8 @@ def get_storage_slot( contracts (`Contract`): The contract that contains the given state variable. **kwargs: key (str): Key of a mapping or index position if an array. - deep_key (str): Key of a mapping embedded within another mapping or secondary index if array. + deep_key (str): Key of a mapping embedded within another mapping or + secondary index if array. struct_var (str): Structure variable name. Returns: (`SlotInfo`) | None : A dictionary of the slot information. @@ -200,14 +201,13 @@ def get_storage_slot( "size": size, "offset": offset, } - else: - return { - "type_string": type_to, - "slot": int_slot, - "size": size, - "offset": offset, - "struct_var": struct_var, - } + return { + "type_string": type_to, + "slot": int_slot, + "size": size, + "offset": offset, + "struct_var": struct_var, + } def get_target_variables(self, **kwargs) -> None: """ @@ -261,83 +261,104 @@ def get_all_storage_variables(self, func: Callable = None) -> None: def print_table(self) -> None: - if environ.get("TABLE_VALUE") is None: - tabulate_headers = ['slot', 'offset', 'size', 'type', 'name'] + if environ.get("TABLE_VALUE") is None: + tabulate_headers = ["slot", "offset", "size", "type", "name"] else: - tabulate_headers = ['slot', 'offset', 'size', 'type', 'name', 'value'] + tabulate_headers = ["slot", "offset", "size", "type", "name", "value"] print("Processing, grabbing values from rpc endpoint...") tabulate_data = [] - for contract, state_var in self.target_variables: + for _, state_var in self.target_variables: type_ = state_var.type - var = state_var.name info = self.slot_info[var] - - slot = info.get('slot') - offset = info.get('offset') - size = info.get('size') - type_string = info.get('type_string') - struct_var = info.get('struct_var') - + slot = info.get("slot") + offset = info.get("offset") + size = info.get("size") + type_string = info.get("type_string") + struct_var = info.get("struct_var") if environ.get("TABLE_VALUE") is None: - tabulate_data.append([slot, offset, size, type_string, var]) + tabulate_data.append( + [slot, offset, size, type_string, var] + ) else: hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type( - hex_bytes, size, offset, type_string + value = self.convert_value_to_type(hex_bytes, size, offset, type_string) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var, + value + ] ) - tabulate_data.append([slot, offset, size, type_string, var, value]) - if is_user_defined_type(type_) and is_struct(type_.type): tabulate_data.pop() for item in info["elems"]: - slot = info["elems"][item].get('slot') - offset = info["elems"][item].get('offset') - size = info["elems"][item].get('size') - type_string = info["elems"][item].get('type_string') - struct_var = info["elems"][item].get('struct_var') + slot = info["elems"][item].get("slot") + offset = info["elems"][item].get("offset") + size = info["elems"][item].get("size") + type_string = info["elems"][item].get("type_string") + struct_var = info["elems"][item].get("struct_var") - # doesn't handle deep keys currently + # doesn't handle deep keys currently var_name_struct_or_array_var = "{} -> {}".format(var, struct_var) - if environ.get("TABLE_VALUE") is None: - tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) + tabulate_data.append( + [slot, offset, size, type_string, var_name_struct_or_array_var] + ) else: hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type( - hex_bytes, size, offset, type_string + value = self.convert_value_to_type(hex_bytes, size, offset, type_string) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var_name_struct_or_array_var, + value + ] ) - tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var, value]) if is_array(type_): tabulate_data.pop() for item in info["elems"]: for key in info["elems"][item]: - slot = info["elems"][item][key].get('slot') - offset = info["elems"][item][key].get('offset') - size = info["elems"][item][key].get('size') - type_string = info["elems"][item][key].get('type_string') - struct_var = info["elems"][item][key].get('struct_var') + slot = info["elems"][item][key].get("slot") + offset = info["elems"][item][key].get("offset") + size = info["elems"][item][key].get("size") + type_string = info["elems"][item][key].get("type_string") + struct_var = info["elems"][item][key].get("struct_var") # doesn't handle deep keys currently var_name_struct_or_array_var = "{}[{}] -> {}".format(var, item, struct_var) if environ.get("TABLE_VALUE") is None: - tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var]) + tabulate_data.append( + [slot, offset, size, type_string, var_name_struct_or_array_var] + ) else: hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type( - hex_bytes, size, offset, type_string + value = self.convert_value_to_type(hex_bytes, size, offset, type_string) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var_name_struct_or_array_var, + value, + ] ) - tabulate_data.append([slot, offset, size, type_string, var_name_struct_or_array_var, value]) - - print(tabulate(tabulate_data, headers=tabulate_headers, tablefmt='grid')) + print(tabulate(tabulate_data, headers=tabulate_headers, tablefmt="grid")) @staticmethod def _find_struct_var_slot( From c18b5ae43b31fa711a2adf29f5359f1f169e0cbe Mon Sep 17 00:00:00 2001 From: noxx Date: Tue, 26 Jul 2022 16:21:59 +0100 Subject: [PATCH 08/23] more linting --- slither/tools/read_storage/__main__.py | 9 +- slither/tools/read_storage/read_storage.py | 158 +++++++++++++-------- 2 files changed, 101 insertions(+), 66 deletions(-) diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 3ec47d1768..660af2c175 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -139,8 +139,7 @@ def main() -> None: srs.rpc = args.rpc_url - if args.silent: - environ["SILENT"] = "1" + environ["SILENT"] = args.silent if args.table_storage_layout: environ["TABLE"] = "1" @@ -148,14 +147,12 @@ def main() -> None: srs.get_storage_layout() srs.print_table() return - + if args.table_storage_value: - assert args.rpc_url environ["TABLE"] = "1" - environ["TABLE_VALUE"] = "1" srs.get_all_storage_variables() srs.get_storage_layout() - srs.print_table() + srs.print_table_with_values() return if args.layout: diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 6d72b9c122..5bd2cdbd9b 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -145,7 +145,7 @@ def get_storage_slot( contracts (`Contract`): The contract that contains the given state variable. **kwargs: key (str): Key of a mapping or index position if an array. - deep_key (str): Key of a mapping embedded within another mapping or + deep_key (str): Key of a mapping embedded within another mapping or secondary index if array. struct_var (str): Structure variable name. Returns: @@ -261,12 +261,6 @@ def get_all_storage_variables(self, func: Callable = None) -> None: def print_table(self) -> None: - if environ.get("TABLE_VALUE") is None: - tabulate_headers = ["slot", "offset", "size", "type", "name"] - else: - tabulate_headers = ["slot", "offset", "size", "type", "name", "value"] - print("Processing, grabbing values from rpc endpoint...") - tabulate_data = [] for _, state_var in self.target_variables: @@ -279,23 +273,7 @@ def print_table(self) -> None: type_string = info.get("type_string") struct_var = info.get("struct_var") - if environ.get("TABLE_VALUE") is None: - tabulate_data.append( - [slot, offset, size, type_string, var] - ) - else: - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type(hex_bytes, size, offset, type_string) - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - var, - value - ] - ) + tabulate_data.append([slot, offset, size, type_string, var]) if is_user_defined_type(type_) and is_struct(type_.type): tabulate_data.pop() @@ -307,25 +285,85 @@ def print_table(self) -> None: struct_var = info["elems"][item].get("struct_var") # doesn't handle deep keys currently - var_name_struct_or_array_var = "{} -> {}".format(var, struct_var) + var_name_struct_or_array_var = f"{var} -> {struct_var}" + + tabulate_data.append( + [slot, offset, size, type_string, var_name_struct_or_array_var] + ) + + if is_array(type_): + tabulate_data.pop() + for item in info["elems"]: + for key in info["elems"][item]: + slot = info["elems"][item][key].get("slot") + offset = info["elems"][item][key].get("offset") + size = info["elems"][item][key].get("size") + type_string = info["elems"][item][key].get("type_string") + struct_var = info["elems"][item][key].get("struct_var") + + # doesn't handle deep keys currently + var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" - if environ.get("TABLE_VALUE") is None: tabulate_data.append( [slot, offset, size, type_string, var_name_struct_or_array_var] ) - else: - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type(hex_bytes, size, offset, type_string) - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - var_name_struct_or_array_var, - value - ] - ) + + print( + tabulate( + tabulate_data, headers=["slot", "offset", "size", "type", "name"], tablefmt="grid" + ) + ) + + def print_table_with_values(self) -> None: + + print("Processing, grabbing values from rpc endpoint...") + tabulate_data = [] + + for _, state_var in self.target_variables: + type_ = state_var.type + var = state_var.name + info = self.slot_info[var] + slot = info.get("slot") + offset = info.get("offset") + size = info.get("size") + type_string = info.get("type_string") + struct_var = info.get("struct_var") + + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var, + self.convert_value_to_type(hex_bytes, size, offset, type_string), + ] + ) + + if is_user_defined_type(type_) and is_struct(type_.type): + tabulate_data.pop() + for item in info["elems"]: + slot = info["elems"][item].get("slot") + offset = info["elems"][item].get("offset") + size = info["elems"][item].get("size") + type_string = info["elems"][item].get("type_string") + struct_var = info["elems"][item].get("struct_var") + + # doesn't handle deep keys currently + var_name_struct_or_array_var = f"{var} -> {struct_var}" + + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var_name_struct_or_array_var, + self.convert_value_to_type(hex_bytes, size, offset, type_string), + ] + ) if is_array(type_): tabulate_data.pop() @@ -338,27 +376,27 @@ def print_table(self) -> None: struct_var = info["elems"][item][key].get("struct_var") # doesn't handle deep keys currently - var_name_struct_or_array_var = "{}[{}] -> {}".format(var, item, struct_var) - - if environ.get("TABLE_VALUE") is None: - tabulate_data.append( - [slot, offset, size, type_string, var_name_struct_or_array_var] - ) - else: - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - value = self.convert_value_to_type(hex_bytes, size, offset, type_string) - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - var_name_struct_or_array_var, - value, - ] - ) - - print(tabulate(tabulate_data, headers=tabulate_headers, tablefmt="grid")) + var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" + + hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var_name_struct_or_array_var, + self.convert_value_to_type(hex_bytes, size, offset, type_string), + ] + ) + + print( + tabulate( + tabulate_data, + headers=["slot", "offset", "size", "type", "name", "value"], + tablefmt="grid", + ) + ) @staticmethod def _find_struct_var_slot( From 646b6a62508de096c61cb0e8b6c618f224d3c5f0 Mon Sep 17 00:00:00 2001 From: noxx Date: Wed, 27 Jul 2022 09:13:29 +0100 Subject: [PATCH 09/23] linting --- slither/tools/read_storage/__main__.py | 2 +- slither/tools/read_storage/read_storage.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 660af2c175..25314f0001 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -147,7 +147,7 @@ def main() -> None: srs.get_storage_layout() srs.print_table() return - + if args.table_storage_value: environ["TABLE"] = "1" srs.get_all_storage_variables() diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 5bd2cdbd9b..8101ebb38f 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -329,7 +329,6 @@ def print_table_with_values(self) -> None: type_string = info.get("type_string") struct_var = info.get("struct_var") - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) tabulate_data.append( [ slot, @@ -337,7 +336,12 @@ def print_table_with_values(self) -> None: size, type_string, var, - self.convert_value_to_type(hex_bytes, size, offset, type_string), + self.convert_value_to_type( + get_storage_data(self.web3, self.checksum_address, slot), + size, + offset, + type_string, + ), ] ) @@ -353,7 +357,6 @@ def print_table_with_values(self) -> None: # doesn't handle deep keys currently var_name_struct_or_array_var = f"{var} -> {struct_var}" - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) tabulate_data.append( [ slot, @@ -361,7 +364,12 @@ def print_table_with_values(self) -> None: size, type_string, var_name_struct_or_array_var, - self.convert_value_to_type(hex_bytes, size, offset, type_string), + self.convert_value_to_type( + get_storage_data(self.web3, self.checksum_address, slot), + size, + offset, + type_string, + ), ] ) From a1963cbab02d40cec487a0bff95309bf28794c9f Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 10:18:05 +0200 Subject: [PATCH 10/23] Multiple improvements to slither-read-storage - Fix types - Use dataclass - Multiple minor fixes --- slither/tools/read_storage/__main__.py | 8 +- slither/tools/read_storage/read_storage.py | 350 ++++++++++--------- slither/tools/read_storage/utils/__init__.py | 6 - slither/tools/read_storage/utils/utils.py | 38 +- 4 files changed, 196 insertions(+), 206 deletions(-) diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 25314f0001..0f3bb2e83f 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -1,6 +1,7 @@ """ Tool to read on-chain storage from EVM """ +import dataclasses import json import argparse from os import environ @@ -139,8 +140,6 @@ def main() -> None: srs.rpc = args.rpc_url - environ["SILENT"] = args.silent - if args.table_storage_layout: environ["TABLE"] = "1" srs.get_all_storage_variables() @@ -173,7 +172,10 @@ def main() -> None: # Only write file if storage layout is used. if len(srs.slot_info) > 1: with open("storage_layout.json", "w", encoding="utf-8") as file: - json.dump(srs.slot_info, file, indent=4) + slot_infos_json = { + key: dataclasses.asdict(value) for key, value in srs.slot_info.items() + } + json.dump(slot_infos_json, file, indent=4) if __name__ == "__main__": diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 8101ebb38f..304b4fcfcd 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -1,3 +1,4 @@ +import json import sys import logging from math import floor @@ -18,11 +19,6 @@ from eth_utils import keccak from hexbytes import HexBytes from .utils import ( - is_elementary, - is_array, - is_mapping, - is_struct, - is_user_defined_type, get_offset_value, get_storage_data, coerce_type, @@ -39,25 +35,28 @@ print("$ pip3 install tabulate --user\n") sys.exit(-1) +from dataclasses import dataclass, field, asdict + from slither.core.solidity_types.type import Type -from slither.core.solidity_types import ArrayType -from slither.core.declarations import Contract, StructureContract +from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType +from slither.core.declarations import Contract, StructureContract, Structure from slither.core.variables.state_variable import StateVariable from slither.core.variables.structure_variable import StructureVariable - logging.basicConfig() logger = logging.getLogger("Slither-read-storage") logger.setLevel(logging.INFO) -class SlotInfo(TypedDict): +@dataclass +class SlotInfo: type_string: str slot: int size: int offset: int - value: Optional[Union[int, bool, str, ChecksumAddress, hex]] - elems: Optional[TypedDict] # same types as SlotInfo + value: Optional[Union[int, bool, str, ChecksumAddress]] = None + elems: Dict[int, "SlotInfo"] = field(default_factory=lambda: {}) + struct_var: Optional[str] = None class SlitherReadStorageException(Exception): @@ -66,12 +65,12 @@ class SlitherReadStorageException(Exception): # pylint: disable=too-many-instance-attributes class SlitherReadStorage: - def __init__(self, contracts, max_depth): + def __init__(self, contracts: List[Contract], max_depth: int) -> None: self._contracts: List[Contract] = contracts self._max_depth: int = max_depth self._log: str = "" - self._slot_info: SlotInfo = {} - self._target_variables = [] + self._slot_info: Dict[str, SlotInfo] = {} + self._target_variables: List[Tuple[Contract, StateVariable]] = [] self._web3: Optional[Web3] = None self._checksum_address: Optional[ChecksumAddress] = None self.storage_address: Optional[str] = None @@ -90,7 +89,7 @@ def log(self) -> str: return self._log @log.setter - def log(self, log) -> str: + def log(self, log: str) -> None: self._log = log @property @@ -111,25 +110,25 @@ def target_variables(self) -> List[Tuple[Contract, StateVariable]]: return self._target_variables @property - def slot_info(self) -> SlotInfo: + def slot_info(self) -> Dict[str, SlotInfo]: """Contains the location, type, size, offset, and value of contract slots.""" return self._slot_info def get_storage_layout(self) -> None: """Retrieves the storage layout of entire contract.""" - tmp = {} + tmp: Dict[str, SlotInfo] = {} for contract, var in self.target_variables: type_ = var.type info = self.get_storage_slot(var, contract) - tmp[var.name] = info + if info: + tmp[var.name] = info + if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): + tmp[var.name].elems = self._all_struct_slots(var, type_.type, contract) + continue - if is_user_defined_type(type_) and is_struct(type_.type): - tmp[var.name]["elems"] = self._all_struct_slots(var, contract) - continue - - if is_array(type_): - elems = self._all_array_slots(var, contract, type_, info["slot"]) - tmp[var.name]["elems"] = elems + if isinstance(type_, ArrayType): + elems = self._all_array_slots(var, contract, type_, info.slot) + tmp[var.name].elems = elems self._slot_info = tmp @@ -155,7 +154,7 @@ def get_storage_slot( key = kwargs.get("key", None) deep_key = kwargs.get("deep_key", None) struct_var = kwargs.get("struct_var", None) - info = "" + info: str var_log_name = target_variable.name try: int_slot, size, offset, type_to = self.get_variable_info(contract, target_variable) @@ -168,22 +167,28 @@ def get_storage_slot( slot = int.to_bytes(int_slot, 32, byteorder="big") - if is_elementary(target_variable.type): - type_to = target_variable.type.name + target_variable_type = target_variable.type - elif is_array(target_variable.type) and key: + if isinstance(target_variable_type, ElementaryType): + type_to = target_variable_type.name + + elif isinstance(target_variable_type, ArrayType) and key: info, type_to, slot, size, offset = self._find_array_slot( - target_variable, slot, key, deep_key=deep_key, struct_var=struct_var + target_variable_type, + slot, + key, + deep_key=deep_key, + struct_var=struct_var, ) self.log += info - elif is_user_defined_type(target_variable.type) and struct_var: + elif isinstance(target_variable_type, UserDefinedType) and struct_var: var_log_name = struct_var - elems = target_variable.type.type.elems_ordered + elems = target_variable_type.type.elems_ordered info, type_to, slot, size, offset = self._find_struct_var_slot(elems, slot, struct_var) self.log += info - elif is_mapping(target_variable.type) and key: + elif isinstance(target_variable_type, MappingType) and key: info, type_to, slot, size, offset = self._find_mapping_slot( target_variable, slot, key, struct_var=struct_var, deep_key=deep_key ) @@ -195,19 +200,19 @@ def get_storage_slot( logger.info(self.log) self.log = "" if environ.get("TABLE") is None: - return { - "type_string": type_to, - "slot": int_slot, - "size": size, - "offset": offset, - } - return { - "type_string": type_to, - "slot": int_slot, - "size": size, - "offset": offset, - "struct_var": struct_var, - } + return SlotInfo( + type_string=type_to, + slot=int_slot, + size=size, + offset=offset, + ) + return SlotInfo( + type_string=type_to, + slot=int_slot, + size=size, + offset=offset, + struct_var=struct_var, + ) def get_target_variables(self, **kwargs) -> None: """ @@ -223,7 +228,7 @@ def get_target_variables(self, **kwargs) -> None: var, contract, **kwargs ) - def get_slot_values(self) -> SlotInfo: + def get_slot_values(self) -> Dict[str, SlotInfo]: """ Fetches the slot values and inserts them in slot info dictionary. Returns: @@ -267,22 +272,21 @@ def print_table(self) -> None: type_ = state_var.type var = state_var.name info = self.slot_info[var] - slot = info.get("slot") - offset = info.get("offset") - size = info.get("size") - type_string = info.get("type_string") - struct_var = info.get("struct_var") + slot = info.slot + offset = info.offset + size = info.size + type_string = info.type_string tabulate_data.append([slot, offset, size, type_string, var]) - if is_user_defined_type(type_) and is_struct(type_.type): + if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): tabulate_data.pop() - for item in info["elems"]: - slot = info["elems"][item].get("slot") - offset = info["elems"][item].get("offset") - size = info["elems"][item].get("size") - type_string = info["elems"][item].get("type_string") - struct_var = info["elems"][item].get("struct_var") + for item in info.elems: + slot = info.elems[item].slot + offset = info.elems[item].offset + size = info.elems[item].size + type_string = info.elems[item].type_string + struct_var = info.elems[item].struct_var # doesn't handle deep keys currently var_name_struct_or_array_var = f"{var} -> {struct_var}" @@ -291,15 +295,15 @@ def print_table(self) -> None: [slot, offset, size, type_string, var_name_struct_or_array_var] ) - if is_array(type_): + if isinstance(type_, ArrayType): tabulate_data.pop() - for item in info["elems"]: - for key in info["elems"][item]: - slot = info["elems"][item][key].get("slot") - offset = info["elems"][item][key].get("offset") - size = info["elems"][item][key].get("size") - type_string = info["elems"][item][key].get("type_string") - struct_var = info["elems"][item][key].get("struct_var") + for item in info.elems: + for elem in info.elems.values(): + slot = elem.slot + offset = elem.offset + size = elem.size + type_string = elem.type_string + struct_var = elem.struct_var # doesn't handle deep keys currently var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" @@ -323,11 +327,10 @@ def print_table_with_values(self) -> None: type_ = state_var.type var = state_var.name info = self.slot_info[var] - slot = info.get("slot") - offset = info.get("offset") - size = info.get("size") - type_string = info.get("type_string") - struct_var = info.get("struct_var") + slot = info.slot + offset = info.offset + size = info.size + type_string = info.type_string tabulate_data.append( [ @@ -337,7 +340,11 @@ def print_table_with_values(self) -> None: type_string, var, self.convert_value_to_type( - get_storage_data(self.web3, self.checksum_address, slot), + get_storage_data( + self.web3, + self.checksum_address, + int.to_bytes(slot, 32, byteorder="big"), + ), size, offset, type_string, @@ -345,14 +352,14 @@ def print_table_with_values(self) -> None: ] ) - if is_user_defined_type(type_) and is_struct(type_.type): + if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): tabulate_data.pop() - for item in info["elems"]: - slot = info["elems"][item].get("slot") - offset = info["elems"][item].get("offset") - size = info["elems"][item].get("size") - type_string = info["elems"][item].get("type_string") - struct_var = info["elems"][item].get("struct_var") + for item in info.elems.values(): + slot = item.slot + offset = item.offset + size = item.size + type_string = item.type_string + struct_var = item.struct_var # doesn't handle deep keys currently var_name_struct_or_array_var = f"{var} -> {struct_var}" @@ -365,7 +372,11 @@ def print_table_with_values(self) -> None: type_string, var_name_struct_or_array_var, self.convert_value_to_type( - get_storage_data(self.web3, self.checksum_address, slot), + get_storage_data( + self.web3, + self.checksum_address, + int.to_bytes(slot, 32, byteorder="big"), + ), size, offset, type_string, @@ -373,30 +384,32 @@ def print_table_with_values(self) -> None: ] ) - if is_array(type_): + if isinstance(type_, ArrayType): tabulate_data.pop() - for item in info["elems"]: - for key in info["elems"][item]: - slot = info["elems"][item][key].get("slot") - offset = info["elems"][item][key].get("offset") - size = info["elems"][item][key].get("size") - type_string = info["elems"][item][key].get("type_string") - struct_var = info["elems"][item][key].get("struct_var") + for item_key, item in info.elems.items(): + # for elem in info.elems[item].values(): + slot = item.slot + offset = item.offset + size = item.size + type_string = item.type_string + struct_var = item.struct_var - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" + # doesn't handle deep keys currently + var_name_struct_or_array_var = f"{var}[{item_key}] -> {struct_var}" - hex_bytes = get_storage_data(self.web3, self.checksum_address, slot) - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - var_name_struct_or_array_var, - self.convert_value_to_type(hex_bytes, size, offset, type_string), - ] - ) + hex_bytes = get_storage_data( + self.web3, self.checksum_address, int.to_bytes(slot, 32, byteorder="big") + ) + tabulate_data.append( + [ + slot, + offset, + size, + type_string, + var_name_struct_or_array_var, + self.convert_value_to_type(hex_bytes, size, offset, type_string), + ] + ) print( tabulate( @@ -424,15 +437,22 @@ def _find_struct_var_slot( """ slot = int.from_bytes(slot, "big") offset = 0 + type_to = "" + size = 0 # TODO: find out what size should return here? (montyly) for var in elems: - size = var.type.size - if offset >= 256: - slot += 1 - offset = 0 - if struct_var == var.name: - type_to = var.type.name - break # found struct var - offset += size + var_type = var.type + if isinstance(var_type, ElementaryType): + size_ = var_type.size + if size_: + if offset >= 256: + slot += 1 + offset = 0 + if struct_var == var.name: + type_to = var_type.name + break # found struct var + offset += size + else: + print(f'{type(var_type)} is current not implemented in structure') slot = int.to_bytes(slot, 32, byteorder="big") info = f"\nStruct Variable: {struct_var}" @@ -441,12 +461,12 @@ def _find_struct_var_slot( # pylint: disable=too-many-branches @staticmethod def _find_array_slot( - target_variable: StateVariable, + target_variable_type: ArrayType, slot: bytes, key: int, deep_key: int = None, struct_var: str = None, - ) -> Tuple[str, str, bytes]: + ) -> Tuple[str, str, bytes, int, int]: """Finds the slot of array's index. Args: target_variable (`StateVariable`): The array that contains the target variable. @@ -458,31 +478,35 @@ def _find_array_slot( info (str): Info about the target variable to log. type_to (str): The type of the target variable. slot (bytes): The storage location of the target variable. + size (int): The size + offset (int): The offset """ info = f"\nKey: {key}" offset = 0 size = 256 - if is_array( - target_variable.type.type + target_variable_type_type = target_variable_type.type + + if isinstance( + target_variable_type_type, ArrayType ): # multidimensional array uint[i][], , uint[][i], or uint[][] - size = target_variable.type.type.type.size - type_to = target_variable.type.type.type.name + size = target_variable_type_type.type.size + type_to = target_variable_type_type.type.name - if target_variable.type.is_fixed_array: # uint[][i] + if target_variable_type.is_fixed_array: # uint[][i] slot_int = int.from_bytes(slot, "big") + int(key) else: slot = keccak(slot) key = int(key) - if target_variable.type.type.is_fixed_array: # arr[i][] - key *= int(str(target_variable.type.type.length)) + if target_variable_type_type.is_fixed_array: # arr[i][] + key *= int(str(target_variable_type_type.length)) slot_int = int.from_bytes(slot, "big") + key if not deep_key: return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset info += f"\nDeep Key: {deep_key}" - if target_variable.type.type.is_dynamic_array: # uint[][] + if target_variable_type_type.is_dynamic_array: # uint[][] # keccak256(keccak256(slot) + index) + floor(j / floor(256 / size)) slot = keccak(int.to_bytes(slot_int, 32, "big")) slot_int = int.from_bytes(slot, "big") @@ -490,13 +514,13 @@ def _find_array_slot( # keccak256(slot) + index + floor(j / floor(256 / size)) slot_int += floor(int(deep_key) / floor(256 / size)) # uint[i][] - elif target_variable.type.is_fixed_array: + elif target_variable_type.is_fixed_array: slot_int = int.from_bytes(slot, "big") + int(key) - if is_user_defined_type(target_variable.type.type): # struct[i] - type_to = target_variable.type.type.type.name + if isinstance(target_variable_type_type, UserDefinedType): # struct[i] + type_to = target_variable_type_type.type.name if not struct_var: return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset - elems = target_variable.type.type.type.elems_ordered + elems = target_variable_type_type.type.elems_ordered slot = int.to_bytes(slot_int, 32, byteorder="big") info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot( elems, slot, struct_var @@ -504,16 +528,16 @@ def _find_array_slot( info += info_tmp else: - type_to = target_variable.type.type.name - size = target_variable.type.type.size # bits + type_to = target_variable_type_type.name + size = ttarget_variable_type_type.size # bits - elif is_user_defined_type(target_variable.type.type): # struct[] + elif isinstance(target_variable_type_type, UserDefinedType): # struct[] slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) - type_to = target_variable.type.type.type.name + type_to = target_variable_type_type.type.name if not struct_var: return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset - elems = target_variable.type.type.type.elems_ordered + elems = target_variable_type_type.type.elems_ordered slot = int.to_bytes(slot_int, 32, byteorder="big") info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot( elems, slot, struct_var @@ -523,8 +547,8 @@ def _find_array_slot( else: slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) - type_to = target_variable.type.type.name - size = target_variable.type.type.size # bits + type_to = target_variable_type_type.name + size = target_variable_type_type.size # bits slot = int.to_bytes(slot_int, 32, byteorder="big") @@ -631,7 +655,7 @@ def get_variable_info( @staticmethod def convert_value_to_type( hex_bytes: HexBytes, size: int, offset: int, type_to: str - ) -> Union[int, bool, str, ChecksumAddress, hex]: + ) -> Union[int, bool, str, ChecksumAddress]: """Convert slot data to type representation.""" # Account for storage packing offset_hex_bytes = get_offset_value(hex_bytes, offset, size) @@ -643,14 +667,11 @@ def convert_value_to_type( return value def _all_struct_slots( - self, var: Union[StructureVariable, StructureContract], contract: Contract, key=None + self, var: StateVariable, st: Structure, contract: Contract, key=None ) -> Dict[str, SlotInfo]: """Retrieves all members of a struct.""" - if isinstance(var.type.type, StructureContract): - struct_elems = var.type.type.elems_ordered - else: - struct_elems = var.type.type.type.elems_ordered - data = {} + struct_elems = st.elems_ordered + data: Dict[str, SlotInfo] = {} for elem in struct_elems: info = self.get_storage_slot( var, @@ -658,17 +679,18 @@ def _all_struct_slots( key=key, struct_var=elem.name, ) - data[elem.name] = info + if info: + data[elem.name] = info return data def _all_array_slots( - self, var: ArrayType, contract: Contract, type_: Type, slot: int + self, var: StateVariable, contract: Contract, type_: Type, slot: int ) -> Dict[int, SlotInfo]: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) - elems = {} - if is_user_defined_type(type_.type): + elems: Dict[int, SlotInfo] = {} + if isinstance(type_, UserDefinedType): for i in range(min(array_length, self.max_depth)): elems[i] = self._all_struct_slots(var, contract, key=str(i)) continue @@ -680,28 +702,29 @@ def _all_array_slots( contract, key=str(i), ) - elems[i] = info - - if is_array(type_.type): # multidimensional array - array_length = self._get_array_length(type_.type, info["slot"]) - - elems[i]["elems"] = {} - for j in range(min(array_length, self.max_depth)): - info = self.get_storage_slot( - var, - contract, - key=str(i), - deep_key=str(j), - ) - - elems[i]["elems"][j] = info + if info: + elems[i] = info + + if isinstance(type_.type, ArrayType): # multidimensional array + array_length = self._get_array_length(type_.type, info["slot"]) + + elems[i].elems = {} + for j in range(min(array_length, self.max_depth)): + info = self.get_storage_slot( + var, + contract, + key=str(i), + deep_key=str(j), + ) + + elems[i].elems[j] = info return elems - def _get_array_length(self, type_: Type, slot: int = None) -> int: + def _get_array_length(self, type_: Type, slot: int) -> int: """Gets the length of dynamic and fixed arrays. Args: type_ (`Type`): The array type. - slot (int, optional): Slot a dynamic array's length is stored at. + slot (int): Slot a dynamic array's length is stored at. Returns: (int): The length of the array. """ @@ -709,8 +732,13 @@ def _get_array_length(self, type_: Type, slot: int = None) -> int: if self.rpc: # The length of dynamic arrays is stored at the starting slot. # Convert from hexadecimal to decimal. - val = int(get_storage_data(self.web3, self.checksum_address, slot).hex(), 16) - if is_array(type_): + val = int( + get_storage_data( + self.web3, self.checksum_address, int.to_bytes(slot, 32, byteorder="big") + ).hex(), + 16, + ) + if isinstance(type_, ArrayType): if type_.is_fixed_array: val = int(str(type_.length)) diff --git a/slither/tools/read_storage/utils/__init__.py b/slither/tools/read_storage/utils/__init__.py index 5d1cb0d74b..2fb43c8b87 100644 --- a/slither/tools/read_storage/utils/__init__.py +++ b/slither/tools/read_storage/utils/__init__.py @@ -1,10 +1,4 @@ from .utils import ( - is_elementary, - is_array, - is_enum, - is_mapping, - is_struct, - is_user_defined_type, get_offset_value, get_storage_data, coerce_type, diff --git a/slither/tools/read_storage/utils/utils.py b/slither/tools/read_storage/utils/utils.py index e14d763d8e..0d5c860b21 100644 --- a/slither/tools/read_storage/utils/utils.py +++ b/slither/tools/read_storage/utils/utils.py @@ -1,42 +1,8 @@ -from typing import Union +from typing import Union, Type, List, Optional from hexbytes import HexBytes from eth_typing.evm import ChecksumAddress from eth_utils import to_int, to_text, to_checksum_address -from slither.core.declarations import Structure, Enum -from slither.core.solidity_types import ArrayType, MappingType, UserDefinedType, ElementaryType -from slither.core.variables.state_variable import StateVariable - - -def is_elementary(variable: StateVariable) -> bool: - """Returns whether variable is an elementary type.""" - return isinstance(variable, ElementaryType) - - -def is_array(variable: StateVariable) -> bool: - """Returns whether variable is an array.""" - return isinstance(variable, ArrayType) - - -def is_mapping(variable: StateVariable) -> bool: - """Returns whether variable is a mapping.""" - return isinstance(variable, MappingType) - - -def is_struct(variable: StateVariable) -> bool: - """Returns whether variable is a struct.""" - return isinstance(variable, Structure) - - -def is_enum(variable: StateVariable) -> bool: - """Returns whether variable is an enum.""" - return isinstance(variable, Enum) - - -def is_user_defined_type(variable: StateVariable) -> bool: - """Returns whether variable is a struct.""" - return isinstance(variable, UserDefinedType) - def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes: """ @@ -58,7 +24,7 @@ def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes: return value -def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, ChecksumAddress, hex]: +def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, ChecksumAddress]: """ Converts input to the indicated type. Args: From e917a8fd90b4185199426d42c6f1d761d0199d52 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 11:22:26 +0200 Subject: [PATCH 11/23] Minor --- slither/tools/read_storage/read_storage.py | 32 ++++++++++++---------- slither/tools/read_storage/utils/utils.py | 5 ++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 304b4fcfcd..5df5b35bca 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -1,9 +1,7 @@ -import json -import sys import logging +import sys from math import floor from os import environ - from typing import Callable, Optional, Tuple, Union, List, Dict try: @@ -35,11 +33,11 @@ print("$ pip3 install tabulate --user\n") sys.exit(-1) -from dataclasses import dataclass, field, asdict +from dataclasses import dataclass, field from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType -from slither.core.declarations import Contract, StructureContract, Structure +from slither.core.declarations import Contract, Structure from slither.core.variables.state_variable import StateVariable from slither.core.variables.structure_variable import StructureVariable @@ -438,7 +436,7 @@ def _find_struct_var_slot( slot = int.from_bytes(slot, "big") offset = 0 type_to = "" - size = 0 # TODO: find out what size should return here? (montyly) + size = 0 # TODO: find out what size should return here? (montyly) for var in elems: var_type = var.type if isinstance(var_type, ElementaryType): @@ -452,7 +450,7 @@ def _find_struct_var_slot( break # found struct var offset += size else: - print(f'{type(var_type)} is current not implemented in structure') + print(f"{type(var_type)} is current not implemented in structure") slot = int.to_bytes(slot, 32, byteorder="big") info = f"\nStruct Variable: {struct_var}" @@ -529,7 +527,7 @@ def _find_array_slot( else: type_to = target_variable_type_type.name - size = ttarget_variable_type_type.size # bits + size = target_variable_type_type.size # bits elif isinstance(target_variable_type_type, UserDefinedType): # struct[] slot = keccak(slot) @@ -590,7 +588,7 @@ def _find_mapping_slot( key = coerce_type(key_type, key) slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)])) - if is_user_defined_type(target_variable.type.type_to) and is_struct( + if isinstance(target_variable.type.type_to, UserDefinedType) and is_struct( target_variable.type.type_to.type ): # mapping(elem => struct) assert struct_var @@ -600,7 +598,9 @@ def _find_mapping_slot( ) info += info_tmp - elif is_mapping(target_variable.type.type_to): # mapping(elem => mapping(elem => ???)) + elif isinstance( + target_variable.type.type_to, MappingType + ): # mapping(elem => mapping(elem => ???)) assert deep_key key_type = target_variable.type.type_to.type_from.name if "int" in key_type: # without this eth_utils encoding fails @@ -615,7 +615,7 @@ def _find_mapping_slot( size = byte_size * 8 # bits offset = 0 - if is_user_defined_type(target_variable.type.type_to.type_to) and is_struct( + if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and is_struct( target_variable.type.type_to.type_to.type ): # mapping(elem => mapping(elem => struct)) assert struct_var @@ -691,9 +691,13 @@ def _all_array_slots( array_length = self._get_array_length(type_, slot) elems: Dict[int, SlotInfo] = {} if isinstance(type_, UserDefinedType): - for i in range(min(array_length, self.max_depth)): - elems[i] = self._all_struct_slots(var, contract, key=str(i)) - continue + st = type_.type + if isinstance(st, Structure): + for i in range(min(array_length, self.max_depth)): + # TODO: figure out why _all_struct_slots returns a Dict[str, SlotInfo] + # but this expect a SlotInfo (montyly) + elems[i] = self._all_struct_slots(var, st, key=str(i)) + continue else: for i in range(min(array_length, self.max_depth)): diff --git a/slither/tools/read_storage/utils/utils.py b/slither/tools/read_storage/utils/utils.py index 0d5c860b21..d9f38d0f0a 100644 --- a/slither/tools/read_storage/utils/utils.py +++ b/slither/tools/read_storage/utils/utils.py @@ -1,7 +1,8 @@ -from typing import Union, Type, List, Optional -from hexbytes import HexBytes +from typing import Union + from eth_typing.evm import ChecksumAddress from eth_utils import to_int, to_text, to_checksum_address +from hexbytes import HexBytes def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes: From c57acfdde7ed7f7747eaa56bc38b63e9c5b01097 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 11:38:40 +0200 Subject: [PATCH 12/23] Fix get_slot_values + minor improvements --- slither/tools/read_storage/read_storage.py | 56 ++++++++-------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 5df5b35bca..89af172182 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -4,12 +4,6 @@ from os import environ from typing import Callable, Optional, Tuple, Union, List, Dict -try: - from typing import TypedDict -except ImportError: - # < Python 3.8 - from typing_extensions import TypedDict - try: from web3 import Web3 from eth_typing.evm import ChecksumAddress @@ -130,6 +124,8 @@ def get_storage_layout(self) -> None: self._slot_info = tmp + # TODO: remove this pylint exception (montyly) + # pylint: disable=too-many-locals def get_storage_slot( self, target_variable: StateVariable, @@ -226,24 +222,18 @@ def get_target_variables(self, **kwargs) -> None: var, contract, **kwargs ) - def get_slot_values(self) -> Dict[str, SlotInfo]: + def get_slot_values(self) -> None: """ - Fetches the slot values and inserts them in slot info dictionary. - Returns: - (`SlotInfo`): The dictionary of slot info. + Fetches the slot values """ - stack = list(self.slot_info.items()) - while stack: - _, v = stack.pop() - if isinstance(v, dict): - stack.extend(v.items()) - if "slot" in v: - hex_bytes = get_storage_data(self.web3, self.checksum_address, v["slot"]) - v["value"] = self.convert_value_to_type( - hex_bytes, v["size"], v["offset"], v["type_string"] - ) - logger.info(f"\nValue: {v['value']}\n") - return self.slot_info + for slot_info in self.slot_info.values(): + hex_bytes = get_storage_data( + self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") + ) + slot_info.value = self.convert_value_to_type( + hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string + ) + logger.info(f"\nValue: {slot_info.value}\n") def get_all_storage_variables(self, func: Callable = None) -> None: """Fetches all storage variables from a list of contracts. @@ -359,16 +349,13 @@ def print_table_with_values(self) -> None: type_string = item.type_string struct_var = item.struct_var - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var} -> {struct_var}" - tabulate_data.append( [ slot, offset, size, type_string, - var_name_struct_or_array_var, + f"{var} -> {struct_var}", # doesn't handle deep keys currently self.convert_value_to_type( get_storage_data( self.web3, @@ -392,9 +379,6 @@ def print_table_with_values(self) -> None: type_string = item.type_string struct_var = item.struct_var - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var}[{item_key}] -> {struct_var}" - hex_bytes = get_storage_data( self.web3, self.checksum_address, int.to_bytes(slot, 32, byteorder="big") ) @@ -404,7 +388,7 @@ def print_table_with_values(self) -> None: offset, size, type_string, - var_name_struct_or_array_var, + f"{var}[{item_key}] -> {struct_var}", # doesn't handle deep keys currently self.convert_value_to_type(hex_bytes, size, offset, type_string), ] ) @@ -588,8 +572,8 @@ def _find_mapping_slot( key = coerce_type(key_type, key) slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)])) - if isinstance(target_variable.type.type_to, UserDefinedType) and is_struct( - target_variable.type.type_to.type + if isinstance(target_variable.type.type_to, UserDefinedType) and isinstance( + target_variable.type.type_to.type, Structure ): # mapping(elem => struct) assert struct_var elems = target_variable.type.type_to.type.elems_ordered @@ -615,8 +599,8 @@ def _find_mapping_slot( size = byte_size * 8 # bits offset = 0 - if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and is_struct( - target_variable.type.type_to.type_to.type + if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and isinstance( + target_variable.type.type_to.type_to.type, Structure ): # mapping(elem => mapping(elem => struct)) assert struct_var elems = target_variable.type.type_to.type_to.type.elems_ordered @@ -696,7 +680,7 @@ def _all_array_slots( for i in range(min(array_length, self.max_depth)): # TODO: figure out why _all_struct_slots returns a Dict[str, SlotInfo] # but this expect a SlotInfo (montyly) - elems[i] = self._all_struct_slots(var, st, key=str(i)) + elems[i] = self._all_struct_slots(var, st, contract, key=str(i)) continue else: @@ -710,7 +694,7 @@ def _all_array_slots( elems[i] = info if isinstance(type_.type, ArrayType): # multidimensional array - array_length = self._get_array_length(type_.type, info["slot"]) + array_length = self._get_array_length(type_.type, info.slot) elems[i].elems = {} for j in range(min(array_length, self.max_depth)): From 46cb8a5cd24908d41a254cc05ca3755eeb2db8ee Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 11:45:00 +0200 Subject: [PATCH 13/23] Fix test_read_storage.py --- tests/test_read_storage.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_read_storage.py b/tests/test_read_storage.py index 0dbbb5bf2b..1573940007 100644 --- a/tests/test_read_storage.py +++ b/tests/test_read_storage.py @@ -1,3 +1,4 @@ +import dataclasses import re import os import sys @@ -14,6 +15,7 @@ try: from web3 import Web3 + from web3.contract import Contract except ImportError: print("ERROR: in order to use slither-read-storage, you need to install web3") print("$ pip3 install web3 --user\n") @@ -67,14 +69,14 @@ def fixture_ganache() -> Generator[GanacheInstance, None, None]: p.wait() -def get_source_file(file_path): +def get_source_file(file_path) -> str: with open(file_path, "r", encoding="utf8") as f: source = f.read() return source -def deploy_contract(w3, ganache, contract_bin, contract_abi): +def deploy_contract(w3, ganache, contract_bin, contract_abi) -> Contract: """Deploy contract to the local ganache network""" signed_txn = w3.eth.account.sign_transaction( dict( @@ -96,7 +98,7 @@ def deploy_contract(w3, ganache, contract_bin, contract_abi): # pylint: disable=too-many-locals @pytest.mark.usefixtures("web3", "ganache") -def test_read_storage(web3, ganache): +def test_read_storage(web3, ganache) -> None: assert web3.isConnected() bin_path = os.path.join(STORAGE_TEST_ROOT, "StorageLayout.bin") abi_path = os.path.join(STORAGE_TEST_ROOT, "StorageLayout.abi") @@ -116,7 +118,8 @@ def test_read_storage(web3, ganache): srs.get_storage_layout() srs.get_slot_values() with open("storage_layout.json", "w", encoding="utf-8") as file: - json.dump(srs.slot_info, file, indent=4) + slot_infos_json = {key: dataclasses.asdict(value) for key, value in srs.slot_info.items()} + json.dump(slot_infos_json, file, indent=4) expected_file = os.path.join(STORAGE_TEST_ROOT, "TEST_storage_layout.json") actual_file = os.path.join(SLITHER_ROOT, "storage_layout.json") From 485e4a9674ba6f492afae332f6eb5edb0a90a000 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 15:16:42 +0200 Subject: [PATCH 14/23] Additional improvements - CI still failing --- slither/tools/read_storage/read_storage.py | 141 +++++++++++---------- tests/test_read_storage.py | 4 +- 2 files changed, 74 insertions(+), 71 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 89af172182..f961557ef9 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -2,7 +2,7 @@ import sys from math import floor from os import environ -from typing import Callable, Optional, Tuple, Union, List, Dict +from typing import Callable, Optional, Tuple, Union, List, Dict, Any try: from web3 import Web3 @@ -31,7 +31,7 @@ from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType -from slither.core.declarations import Contract, Structure +from slither.core.declarations import Contract, Structure, StructureContract from slither.core.variables.state_variable import StateVariable from slither.core.variables.structure_variable import StructureVariable @@ -47,7 +47,8 @@ class SlotInfo: size: int offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None - elems: Dict[int, "SlotInfo"] = field(default_factory=lambda: {}) + # For structure, str->SlotInfo, for array, str-> SlotInfo + elems: Dict[str, "SlotInfo"] = field(default_factory=lambda: {}) struct_var: Optional[str] = None @@ -116,9 +117,8 @@ def get_storage_layout(self) -> None: tmp[var.name] = info if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): tmp[var.name].elems = self._all_struct_slots(var, type_.type, contract) - continue - if isinstance(type_, ArrayType): + elif isinstance(type_, ArrayType): elems = self._all_array_slots(var, contract, type_, info.slot) tmp[var.name].elems = elems @@ -127,27 +127,27 @@ def get_storage_layout(self) -> None: # TODO: remove this pylint exception (montyly) # pylint: disable=too-many-locals def get_storage_slot( - self, - target_variable: StateVariable, - contract: Contract, - **kwargs, + self, + target_variable: StateVariable, + contract: Contract, + **kwargs: Any, ) -> Union[SlotInfo, None]: """Finds the storage slot of a variable in a given contract. Args: target_variable (`StateVariable`): The variable to retrieve the slot for. contracts (`Contract`): The contract that contains the given state variable. **kwargs: - key (str): Key of a mapping or index position if an array. - deep_key (str): Key of a mapping embedded within another mapping or + key (int): Key of a mapping or index position if an array. + deep_key (int): Key of a mapping embedded within another mapping or secondary index if array. struct_var (str): Structure variable name. Returns: (`SlotInfo`) | None : A dictionary of the slot information. """ - key = kwargs.get("key", None) - deep_key = kwargs.get("deep_key", None) - struct_var = kwargs.get("struct_var", None) + key: Optional[int] = kwargs.get("key", None) + deep_key: Optional[int]= kwargs.get("deep_key", None) + struct_var: Optional[str] = kwargs.get("struct_var", None) info: str var_log_name = target_variable.name try: @@ -178,7 +178,9 @@ def get_storage_slot( elif isinstance(target_variable_type, UserDefinedType) and struct_var: var_log_name = struct_var - elems = target_variable_type.type.elems_ordered + target_variable_type_type = target_variable_type.type + assert isinstance(target_variable_type_type, Structure) + elems = target_variable_type_type.elems_ordered info, type_to, slot, size, offset = self._find_struct_var_slot(elems, slot, struct_var) self.log += info @@ -226,7 +228,11 @@ def get_slot_values(self) -> None: """ Fetches the slot values """ - for slot_info in self.slot_info.values(): + stack = list(self.slot_info.values()) + while stack: + slot_info = stack.pop() + if slot_info.elems: + stack.extend(slot_info.elems.values()) hex_bytes = get_storage_data( self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") ) @@ -269,12 +275,12 @@ def print_table(self) -> None: if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): tabulate_data.pop() - for item in info.elems: - slot = info.elems[item].slot - offset = info.elems[item].offset - size = info.elems[item].size - type_string = info.elems[item].type_string - struct_var = info.elems[item].struct_var + for item in info.elems.values(): + slot = item.slot + offset = item.offset + size = item.size + type_string = item.type_string + struct_var = item.struct_var # doesn't handle deep keys currently var_name_struct_or_array_var = f"{var} -> {struct_var}" @@ -285,20 +291,19 @@ def print_table(self) -> None: if isinstance(type_, ArrayType): tabulate_data.pop() - for item in info.elems: - for elem in info.elems.values(): - slot = elem.slot - offset = elem.offset - size = elem.size - type_string = elem.type_string - struct_var = elem.struct_var + for item_key, item in info.elems.items(): + slot = item.slot + offset = item.offset + size = item.size + type_string = item.type_string + struct_var = item.struct_var - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var}[{item}] -> {struct_var}" + # doesn't handle deep keys currently + var_name_struct_or_array_var = f"{var}[{item_key}] -> {struct_var}" - tabulate_data.append( - [slot, offset, size, type_string, var_name_struct_or_array_var] - ) + tabulate_data.append( + [slot, offset, size, type_string, var_name_struct_or_array_var] + ) print( tabulate( @@ -403,12 +408,12 @@ def print_table_with_values(self) -> None: @staticmethod def _find_struct_var_slot( - elems: List[StructureVariable], slot: bytes, struct_var: str + elems: List[StructureVariable], slot_as_bytes: bytes, struct_var: str ) -> Tuple[str, str, bytes, int, int]: """Finds the slot of a structure variable. Args: elems (List[StructureVariable]): Ordered list of structure variables. - slot (bytes): The slot of the struct to begin searching at. + slot_as_bytes (bytes): The slot of the struct to begin searching at. struct_var (str): The target structure variable. Returns: info (str): Info about the target variable to log. @@ -417,15 +422,15 @@ def _find_struct_var_slot( size (int): The size (in bits) of the target variable. offset (int): The size of other variables that share the same slot. """ - slot = int.from_bytes(slot, "big") + slot = int.from_bytes(slot_as_bytes, "big") offset = 0 type_to = "" - size = 0 # TODO: find out what size should return here? (montyly) + size = 0 for var in elems: var_type = var.type if isinstance(var_type, ElementaryType): - size_ = var_type.size - if size_: + size = var_type.size + if size: if offset >= 256: slot += 1 offset = 0 @@ -434,20 +439,20 @@ def _find_struct_var_slot( break # found struct var offset += size else: - print(f"{type(var_type)} is current not implemented in structure") + logger.info(f"{type(var_type)} is current not implemented in _find_struct_var_slot") - slot = int.to_bytes(slot, 32, byteorder="big") + slot_as_bytes = int.to_bytes(slot, 32, byteorder="big") info = f"\nStruct Variable: {struct_var}" - return info, type_to, slot, size, offset + return info, type_to, slot_as_bytes, size, offset # pylint: disable=too-many-branches @staticmethod def _find_array_slot( - target_variable_type: ArrayType, - slot: bytes, - key: int, - deep_key: int = None, - struct_var: str = None, + target_variable_type: ArrayType, + slot: bytes, + key: int, + deep_key: int = None, + struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: """Finds the slot of array's index. Args: @@ -470,7 +475,7 @@ def _find_array_slot( target_variable_type_type = target_variable_type.type if isinstance( - target_variable_type_type, ArrayType + target_variable_type_type, ArrayType ): # multidimensional array uint[i][], , uint[][i], or uint[][] size = target_variable_type_type.type.size type_to = target_variable_type_type.type.name @@ -538,11 +543,11 @@ def _find_array_slot( @staticmethod def _find_mapping_slot( - target_variable: StateVariable, - slot: bytes, - key: Union[int, str], - deep_key: Union[int, str] = None, - struct_var: str = None, + target_variable: StateVariable, + slot: bytes, + key: Union[int, str], + deep_key: Union[int, str] = None, + struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: """Finds the data slot of a target variable within a mapping. target_variable (`StateVariable`): The mapping that contains the target variable. @@ -573,7 +578,7 @@ def _find_mapping_slot( slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)])) if isinstance(target_variable.type.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type, Structure + target_variable.type.type_to.type, Structure ): # mapping(elem => struct) assert struct_var elems = target_variable.type.type_to.type.elems_ordered @@ -583,7 +588,7 @@ def _find_mapping_slot( info += info_tmp elif isinstance( - target_variable.type.type_to, MappingType + target_variable.type.type_to, MappingType ): # mapping(elem => mapping(elem => ???)) assert deep_key key_type = target_variable.type.type_to.type_from.name @@ -600,7 +605,7 @@ def _find_mapping_slot( offset = 0 if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type_to.type, Structure + target_variable.type.type_to.type_to.type, Structure ): # mapping(elem => mapping(elem => struct)) assert struct_var elems = target_variable.type.type_to.type_to.type.elems_ordered @@ -621,7 +626,7 @@ def _find_mapping_slot( @staticmethod def get_variable_info( - contract: Contract, target_variable: StateVariable + contract: Contract, target_variable: StateVariable ) -> Tuple[int, int, int, str]: """Return slot, size, offset, and type.""" type_to = str(target_variable.type) @@ -638,7 +643,7 @@ def get_variable_info( @staticmethod def convert_value_to_type( - hex_bytes: HexBytes, size: int, offset: int, type_to: str + hex_bytes: HexBytes, size: int, offset: int, type_to: str ) -> Union[int, bool, str, ChecksumAddress]: """Convert slot data to type representation.""" # Account for storage packing @@ -651,7 +656,7 @@ def convert_value_to_type( return value def _all_struct_slots( - self, var: StateVariable, st: Structure, contract: Contract, key=None + self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None ) -> Dict[str, SlotInfo]: """Retrieves all members of a struct.""" struct_elems = st.elems_ordered @@ -669,19 +674,18 @@ def _all_struct_slots( return data def _all_array_slots( - self, var: StateVariable, contract: Contract, type_: Type, slot: int - ) -> Dict[int, SlotInfo]: + self, var: StateVariable, contract: Contract, type_: Type, slot: int + ) -> Dict[str, SlotInfo]: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) - elems: Dict[int, SlotInfo] = {} + elems: Dict[str, SlotInfo] = {} if isinstance(type_, UserDefinedType): st = type_.type if isinstance(st, Structure): for i in range(min(array_length, self.max_depth)): # TODO: figure out why _all_struct_slots returns a Dict[str, SlotInfo] # but this expect a SlotInfo (montyly) - elems[i] = self._all_struct_slots(var, st, contract, key=str(i)) - continue + elems[str(i)] = self._all_struct_slots(var, st, contract, key=i) else: for i in range(min(array_length, self.max_depth)): @@ -691,12 +695,11 @@ def _all_array_slots( key=str(i), ) if info: - elems[i] = info + elems[str(i)] = info if isinstance(type_.type, ArrayType): # multidimensional array array_length = self._get_array_length(type_.type, info.slot) - elems[i].elems = {} for j in range(min(array_length, self.max_depth)): info = self.get_storage_slot( var, @@ -704,8 +707,8 @@ def _all_array_slots( key=str(i), deep_key=str(j), ) - - elems[i].elems[j] = info + if info: + elems[str(i)].elems[str(j)] = info return elems def _get_array_length(self, type_: Type, slot: int) -> int: diff --git a/tests/test_read_storage.py b/tests/test_read_storage.py index 1573940007..3b1aaa8232 100644 --- a/tests/test_read_storage.py +++ b/tests/test_read_storage.py @@ -135,8 +135,8 @@ def test_read_storage(web3, ganache) -> None: path_list = re.findall(r"\['(.*?)'\]", change.path()) path = "_".join(path_list) with open(f"{path}_expected.txt", "w", encoding="utf8") as f: - f.write(change.t1) + f.write(str(change.t1)) with open(f"{path}_actual.txt", "w", encoding="utf8") as f: - f.write(change.t2) + f.write(str(change.t2)) assert not diff From 14ae013c9a36ae79df3d52ea71d6a2a38f47c235 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 28 Jul 2022 15:32:31 +0200 Subject: [PATCH 15/23] black & pylint --- slither/tools/read_storage/read_storage.py | 52 +++++++++++----------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index f961557ef9..96365dcf8a 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -31,7 +31,7 @@ from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType -from slither.core.declarations import Contract, Structure, StructureContract +from slither.core.declarations import Contract, Structure from slither.core.variables.state_variable import StateVariable from slither.core.variables.structure_variable import StructureVariable @@ -127,10 +127,10 @@ def get_storage_layout(self) -> None: # TODO: remove this pylint exception (montyly) # pylint: disable=too-many-locals def get_storage_slot( - self, - target_variable: StateVariable, - contract: Contract, - **kwargs: Any, + self, + target_variable: StateVariable, + contract: Contract, + **kwargs: Any, ) -> Union[SlotInfo, None]: """Finds the storage slot of a variable in a given contract. Args: @@ -146,7 +146,7 @@ def get_storage_slot( """ key: Optional[int] = kwargs.get("key", None) - deep_key: Optional[int]= kwargs.get("deep_key", None) + deep_key: Optional[int] = kwargs.get("deep_key", None) struct_var: Optional[str] = kwargs.get("struct_var", None) info: str var_log_name = target_variable.name @@ -408,7 +408,7 @@ def print_table_with_values(self) -> None: @staticmethod def _find_struct_var_slot( - elems: List[StructureVariable], slot_as_bytes: bytes, struct_var: str + elems: List[StructureVariable], slot_as_bytes: bytes, struct_var: str ) -> Tuple[str, str, bytes, int, int]: """Finds the slot of a structure variable. Args: @@ -448,11 +448,11 @@ def _find_struct_var_slot( # pylint: disable=too-many-branches @staticmethod def _find_array_slot( - target_variable_type: ArrayType, - slot: bytes, - key: int, - deep_key: int = None, - struct_var: str = None, + target_variable_type: ArrayType, + slot: bytes, + key: int, + deep_key: int = None, + struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: """Finds the slot of array's index. Args: @@ -475,7 +475,7 @@ def _find_array_slot( target_variable_type_type = target_variable_type.type if isinstance( - target_variable_type_type, ArrayType + target_variable_type_type, ArrayType ): # multidimensional array uint[i][], , uint[][i], or uint[][] size = target_variable_type_type.type.size type_to = target_variable_type_type.type.name @@ -543,11 +543,11 @@ def _find_array_slot( @staticmethod def _find_mapping_slot( - target_variable: StateVariable, - slot: bytes, - key: Union[int, str], - deep_key: Union[int, str] = None, - struct_var: str = None, + target_variable: StateVariable, + slot: bytes, + key: Union[int, str], + deep_key: Union[int, str] = None, + struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: """Finds the data slot of a target variable within a mapping. target_variable (`StateVariable`): The mapping that contains the target variable. @@ -578,7 +578,7 @@ def _find_mapping_slot( slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)])) if isinstance(target_variable.type.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type, Structure + target_variable.type.type_to.type, Structure ): # mapping(elem => struct) assert struct_var elems = target_variable.type.type_to.type.elems_ordered @@ -588,7 +588,7 @@ def _find_mapping_slot( info += info_tmp elif isinstance( - target_variable.type.type_to, MappingType + target_variable.type.type_to, MappingType ): # mapping(elem => mapping(elem => ???)) assert deep_key key_type = target_variable.type.type_to.type_from.name @@ -605,7 +605,7 @@ def _find_mapping_slot( offset = 0 if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type_to.type, Structure + target_variable.type.type_to.type_to.type, Structure ): # mapping(elem => mapping(elem => struct)) assert struct_var elems = target_variable.type.type_to.type_to.type.elems_ordered @@ -626,7 +626,7 @@ def _find_mapping_slot( @staticmethod def get_variable_info( - contract: Contract, target_variable: StateVariable + contract: Contract, target_variable: StateVariable ) -> Tuple[int, int, int, str]: """Return slot, size, offset, and type.""" type_to = str(target_variable.type) @@ -643,7 +643,7 @@ def get_variable_info( @staticmethod def convert_value_to_type( - hex_bytes: HexBytes, size: int, offset: int, type_to: str + hex_bytes: HexBytes, size: int, offset: int, type_to: str ) -> Union[int, bool, str, ChecksumAddress]: """Convert slot data to type representation.""" # Account for storage packing @@ -656,7 +656,7 @@ def convert_value_to_type( return value def _all_struct_slots( - self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None + self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None ) -> Dict[str, SlotInfo]: """Retrieves all members of a struct.""" struct_elems = st.elems_ordered @@ -673,8 +673,10 @@ def _all_struct_slots( return data + # TODO: remove this exception (montyly) + # pylint: disable=too-many-nested-blocks def _all_array_slots( - self, var: StateVariable, contract: Contract, type_: Type, slot: int + self, var: StateVariable, contract: Contract, type_: Type, slot: int ) -> Dict[str, SlotInfo]: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) From 5669dc5cd4aab7f380097c133470f2a489092271 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 1 Aug 2022 16:54:12 -0500 Subject: [PATCH 16/23] address mypy warnings --- .../core/solidity_types/elementary_type.py | 4 +- slither/tools/read_storage/read_storage.py | 109 ++++++++++-------- slither/tools/read_storage/utils/utils.py | 24 ++-- 3 files changed, 76 insertions(+), 61 deletions(-) diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index ce782a5e65..e589054c28 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -172,7 +172,7 @@ def name(self) -> str: return self.type @property - def size(self) -> Optional[int]: + def size(self) -> int: """ Return the size in bits Return None if the size is not known @@ -190,7 +190,7 @@ def size(self) -> Optional[int]: return int(160) if t.startswith("bytes") and t != "bytes": return int(t[len("bytes") :]) * 8 - return None + raise SlitherException(f"{t} does not have a size") @property def storage_size(self) -> Tuple[int, bool]: diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 96365dcf8a..5acfb83edb 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -2,7 +2,7 @@ import sys from math import floor from os import environ -from typing import Callable, Optional, Tuple, Union, List, Dict, Any +from typing import Callable, Optional, Tuple, Union, List, Dict, Any, NewType try: from web3 import Web3 @@ -20,12 +20,12 @@ print("$ pip3 install web3 --user\n") sys.exit(-1) -try: - from tabulate import tabulate -except ImportError: - print("ERROR: in order to use slither-read-storage --table, you need to install tabulate") - print("$ pip3 install tabulate --user\n") - sys.exit(-1) +# try: +# from tabulate import tabulate +# except ImportError: +# print("ERROR: in order to use slither-read-storage --table, you need to install tabulate") +# print("$ pip3 install tabulate --user\n") +# sys.exit(-1) from dataclasses import dataclass, field @@ -39,7 +39,8 @@ logger = logging.getLogger("Slither-read-storage") logger.setLevel(logging.INFO) - +Elem = NewType("Elem", Dict[str, "SlotInfo"]) +NestedElem = NewType("NestedElem", Dict[str, Elem]) @dataclass class SlotInfo: type_string: str @@ -48,7 +49,7 @@ class SlotInfo: offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None # For structure, str->SlotInfo, for array, str-> SlotInfo - elems: Dict[str, "SlotInfo"] = field(default_factory=lambda: {}) + elems: Elem = field(default_factory=lambda: {}) struct_var: Optional[str] = None @@ -109,7 +110,7 @@ def slot_info(self) -> Dict[str, SlotInfo]: def get_storage_layout(self) -> None: """Retrieves the storage layout of entire contract.""" - tmp: Dict[str, SlotInfo] = {} + tmp: Elem for contract, var in self.target_variables: type_ = var.type info = self.get_storage_slot(var, contract) @@ -186,7 +187,7 @@ def get_storage_slot( elif isinstance(target_variable_type, MappingType) and key: info, type_to, slot, size, offset = self._find_mapping_slot( - target_variable, slot, key, struct_var=struct_var, deep_key=deep_key + target_variable_type, slot, key, struct_var=struct_var, deep_key=deep_key ) self.log += info @@ -220,9 +221,11 @@ def get_target_variables(self, **kwargs) -> None: struct_var (str): Structure variable name. """ for contract, var in self.target_variables: - self._slot_info[f"{contract.name}.{var.name}"] = self.get_storage_slot( + slot_info = self.get_storage_slot( var, contract, **kwargs ) + if slot_info: + self._slot_info[f"{contract.name}.{var.name}"] = slot_info def get_slot_values(self) -> None: """ @@ -425,7 +428,6 @@ def _find_struct_var_slot( slot = int.from_bytes(slot_as_bytes, "big") offset = 0 type_to = "" - size = 0 for var in elems: var_type = var.type if isinstance(var_type, ElementaryType): @@ -477,6 +479,8 @@ def _find_array_slot( if isinstance( target_variable_type_type, ArrayType ): # multidimensional array uint[i][], , uint[][i], or uint[][] + assert isinstance(target_variable_type_type.type, ElementaryType) + assert target_variable_type_type.type.size size = target_variable_type_type.type.size type_to = target_variable_type_type.type.name @@ -503,7 +507,7 @@ def _find_array_slot( elif target_variable_type.is_fixed_array: slot_int = int.from_bytes(slot, "big") + int(key) - if isinstance(target_variable_type_type, UserDefinedType): # struct[i] + if isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[i] type_to = target_variable_type_type.type.name if not struct_var: return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset @@ -515,10 +519,12 @@ def _find_array_slot( info += info_tmp else: + assert isinstance(target_variable_type_type, ElementaryType) type_to = target_variable_type_type.name + assert target_variable_type_type.size size = target_variable_type_type.size # bits - elif isinstance(target_variable_type_type, UserDefinedType): # struct[] + elif isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[] slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) type_to = target_variable_type_type.type.name @@ -532,6 +538,7 @@ def _find_array_slot( info += info_tmp else: + assert isinstance(target_variable_type_type, ElementaryType) and target_variable_type_type.size slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) type_to = target_variable_type_type.name @@ -543,7 +550,7 @@ def _find_array_slot( @staticmethod def _find_mapping_slot( - target_variable: StateVariable, + target_variable_type: MappingType, slot: bytes, key: Union[int, str], deep_key: Union[int, str] = None, @@ -557,7 +564,7 @@ def _find_mapping_slot( struct_var (str, optional): Structure variable name. :returns: log (str): Info about the target variable to log. - type_to (bytes): The type of the target variable. + type_to (str): The type of the target variable. slot (bytes): The storage location of the target variable. size (int): The size (in bits) of the target variable. offset (int): The size of other variables that share the same slot. @@ -569,29 +576,30 @@ def _find_mapping_slot( info += f"\nKey: {key}" if deep_key: info += f"\nDeep Key: {deep_key}" - - key_type = target_variable.type.type_from.name + assert isinstance(target_variable_type.type_from, ElementaryType) + key_type = target_variable_type.type_from.name assert key if "int" in key_type: # without this eth_utils encoding fails key = int(key) key = coerce_type(key_type, key) slot = keccak(encode_abi([key_type, "uint256"], [key, decode_single("uint256", slot)])) - if isinstance(target_variable.type.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type, Structure + if isinstance(target_variable_type.type_to, UserDefinedType) and isinstance( + target_variable_type.type_to.type, Structure ): # mapping(elem => struct) assert struct_var - elems = target_variable.type.type_to.type.elems_ordered + elems = target_variable_type.type_to.type.elems_ordered info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot( elems, slot, struct_var ) info += info_tmp elif isinstance( - target_variable.type.type_to, MappingType + target_variable_type.type_to, MappingType ): # mapping(elem => mapping(elem => ???)) assert deep_key - key_type = target_variable.type.type_to.type_from.name + assert isinstance(target_variable_type.type_to.type_from, ElementaryType) + key_type = target_variable_type.type_to.type_from.name if "int" in key_type: # without this eth_utils encoding fails deep_key = int(deep_key) @@ -599,16 +607,18 @@ def _find_mapping_slot( slot = keccak(encode_abi([key_type, "bytes32"], [deep_key, slot])) # mapping(elem => mapping(elem => elem)) - type_to = target_variable.type.type_to.type_to.type - byte_size, _ = target_variable.type.type_to.type_to.storage_size + target_variable_type_type_to_type_to = target_variable_type.type_to.type_to + assert isinstance(target_variable_type_type_to_type_to, (UserDefinedType, ElementaryType)) + type_to = str(target_variable_type_type_to_type_to.type) + byte_size, _ = target_variable_type_type_to_type_to.storage_size size = byte_size * 8 # bits offset = 0 - if isinstance(target_variable.type.type_to.type_to, UserDefinedType) and isinstance( - target_variable.type.type_to.type_to.type, Structure + if isinstance(target_variable_type_type_to_type_to, UserDefinedType) and isinstance( + target_variable_type_type_to_type_to.type, Structure ): # mapping(elem => mapping(elem => struct)) assert struct_var - elems = target_variable.type.type_to.type_to.type.elems_ordered + elems = target_variable_type_type_to_type_to.type.elems_ordered # If map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth); info_tmp, type_to, slot, size, offset = SlitherReadStorage._find_struct_var_slot( elems, slot, struct_var @@ -617,11 +627,15 @@ def _find_mapping_slot( # TODO: suppory mapping with dynamic arrays - else: # mapping(elem => elem) - type_to = target_variable.type.type_to.name # the value's elementary type - byte_size, _ = target_variable.type.type_to.storage_size + # mapping(elem => elem) + elif isinstance(target_variable_type.type_to, ElementaryType): + type_to = target_variable_type.type_to.name # the value's elementary type + byte_size, _ = target_variable_type.type_to.storage_size size = byte_size * 8 # bits + else: + raise NotImplementedError(f"{target_variable_type} => {target_variable_type.type_to} not implemented") + return info, type_to, slot, size, offset @staticmethod @@ -629,6 +643,7 @@ def get_variable_info( contract: Contract, target_variable: StateVariable ) -> Tuple[int, int, int, str]: """Return slot, size, offset, and type.""" + assert isinstance(target_variable.type, Type) type_to = str(target_variable.type) byte_size, _ = target_variable.type.storage_size size = byte_size * 8 # bits @@ -643,7 +658,7 @@ def get_variable_info( @staticmethod def convert_value_to_type( - hex_bytes: HexBytes, size: int, offset: int, type_to: str + hex_bytes: bytes, size: int, offset: int, type_to: str ) -> Union[int, bool, str, ChecksumAddress]: """Convert slot data to type representation.""" # Account for storage packing @@ -657,10 +672,10 @@ def convert_value_to_type( def _all_struct_slots( self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None - ) -> Dict[str, SlotInfo]: + ) -> Elem: """Retrieves all members of a struct.""" struct_elems = st.elems_ordered - data: Dict[str, SlotInfo] = {} + data: Elem for elem in struct_elems: info = self.get_storage_slot( var, @@ -673,21 +688,18 @@ def _all_struct_slots( return data - # TODO: remove this exception (montyly) # pylint: disable=too-many-nested-blocks def _all_array_slots( - self, var: StateVariable, contract: Contract, type_: Type, slot: int - ) -> Dict[str, SlotInfo]: + self, var: StateVariable, contract: Contract, type_: ArrayType, slot: int + ) -> Elem: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) - elems: Dict[str, SlotInfo] = {} + elems: Elem + tartget_variable_type = type_.type if isinstance(type_, UserDefinedType): - st = type_.type - if isinstance(st, Structure): + if isinstance(tartget_variable_type, Structure): for i in range(min(array_length, self.max_depth)): - # TODO: figure out why _all_struct_slots returns a Dict[str, SlotInfo] - # but this expect a SlotInfo (montyly) - elems[str(i)] = self._all_struct_slots(var, st, contract, key=i) + elems[str(i)] = self._all_struct_slots(var, tartget_variable_type, contract, key=i) else: for i in range(min(array_length, self.max_depth)): @@ -699,8 +711,8 @@ def _all_array_slots( if info: elems[str(i)] = info - if isinstance(type_.type, ArrayType): # multidimensional array - array_length = self._get_array_length(type_.type, info.slot) + if isinstance(tartget_variable_type, ArrayType): # multidimensional array + array_length = self._get_array_length(tartget_variable_type, info.slot) for j in range(min(array_length, self.max_depth)): info = self.get_storage_slot( @@ -710,13 +722,14 @@ def _all_array_slots( deep_key=str(j), ) if info: - elems[str(i)].elems[str(j)] = info + elem = elems[str(i)] + elem.elems[str(j)] = info return elems def _get_array_length(self, type_: Type, slot: int) -> int: """Gets the length of dynamic and fixed arrays. Args: - type_ (`Type`): The array type. + type_ (`AbstractType`): The array type. slot (int): Slot a dynamic array's length is stored at. Returns: (int): The length of the array. diff --git a/slither/tools/read_storage/utils/utils.py b/slither/tools/read_storage/utils/utils.py index d9f38d0f0a..04cc6518f6 100644 --- a/slither/tools/read_storage/utils/utils.py +++ b/slither/tools/read_storage/utils/utils.py @@ -2,10 +2,9 @@ from eth_typing.evm import ChecksumAddress from eth_utils import to_int, to_text, to_checksum_address -from hexbytes import HexBytes -def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes: +def get_offset_value(hex_bytes: bytes, offset: int, size: int) -> bytes: """ Trims slot data to only contain the target variable's. Args: @@ -25,7 +24,7 @@ def get_offset_value(hex_bytes: HexBytes, offset: int, size: int) -> bytes: return value -def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, ChecksumAddress]: +def coerce_type(solidity_type: str, value: Union[int, str, bytes]) -> Union[int, bool, str, ChecksumAddress]: """ Converts input to the indicated type. Args: @@ -35,24 +34,27 @@ def coerce_type(solidity_type: str, value: bytes) -> Union[int, bool, str, Check (Union[int, bool, str, ChecksumAddress, hex]): The type representation of the value. """ if "int" in solidity_type: - converted_value = to_int(value) + return to_int(value) elif "bool" in solidity_type: - converted_value = bool(to_int(value)) - elif "string" in solidity_type: + return bool(to_int(value)) + elif "string" in solidity_type and isinstance(value, bytes): # length * 2 is stored in lower end bits # TODO handle bytes and strings greater than 32 bytes length = int(int.from_bytes(value[-2:], "big") / 2) - converted_value = to_text(value[:length]) + return to_text(value[:length]) elif "address" in solidity_type: - converted_value = to_checksum_address(value) + if not isinstance(value, (str, bytes)): + raise TypeError + return to_checksum_address(value) else: - converted_value = value.hex() + if not isinstance(value, bytes): + raise TypeError + return value.hex() - return converted_value -def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> HexBytes: +def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> bytes: """ Retrieves the storage data from the blockchain at target address and slot. Args: From a236975f397404c0167ca5321367c7efd4648293 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 00:08:56 -0500 Subject: [PATCH 17/23] initialize Elems --- slither/tools/read_storage/read_storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 5acfb83edb..0454888dda 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -110,7 +110,7 @@ def slot_info(self) -> Dict[str, SlotInfo]: def get_storage_layout(self) -> None: """Retrieves the storage layout of entire contract.""" - tmp: Elem + tmp = Elem({}) for contract, var in self.target_variables: type_ = var.type info = self.get_storage_slot(var, contract) @@ -675,7 +675,7 @@ def _all_struct_slots( ) -> Elem: """Retrieves all members of a struct.""" struct_elems = st.elems_ordered - data: Elem + data = Elem({}) for elem in struct_elems: info = self.get_storage_slot( var, @@ -694,7 +694,7 @@ def _all_array_slots( ) -> Elem: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) - elems: Elem + elems = Elem({}) tartget_variable_type = type_.type if isinstance(type_, UserDefinedType): if isinstance(tartget_variable_type, Structure): From adace8ecc41ad042aa79260154a47bb76f05a916 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 09:18:22 -0500 Subject: [PATCH 18/23] fix isinstance check in _all_array_slots --- slither/tools/read_storage/read_storage.py | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 0454888dda..02533d402e 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -49,7 +49,7 @@ class SlotInfo: offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None # For structure, str->SlotInfo, for array, str-> SlotInfo - elems: Elem = field(default_factory=lambda: {}) + elems: Elem = field(default_factory=lambda: Elem({})) struct_var: Optional[str] = None @@ -674,7 +674,10 @@ def _all_struct_slots( self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None ) -> Elem: """Retrieves all members of a struct.""" - struct_elems = st.elems_ordered + if isinstance(var.type.type, Structure): + struct_elems = var.type.type.elems_ordered + else: + struct_elems = var.type.type.type.elems_ordered data = Elem({}) for elem in struct_elems: info = self.get_storage_slot( @@ -695,11 +698,10 @@ def _all_array_slots( """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) elems = Elem({}) - tartget_variable_type = type_.type - if isinstance(type_, UserDefinedType): - if isinstance(tartget_variable_type, Structure): - for i in range(min(array_length, self.max_depth)): - elems[str(i)] = self._all_struct_slots(var, tartget_variable_type, contract, key=i) + target_variable_type = type_.type + if isinstance(target_variable_type, UserDefinedType) and isinstance(target_variable_type.type, Structure): + for i in range(min(array_length, self.max_depth)): + elems[str(i)] = self._all_struct_slots(var, target_variable_type, contract, key=i) else: for i in range(min(array_length, self.max_depth)): @@ -711,8 +713,8 @@ def _all_array_slots( if info: elems[str(i)] = info - if isinstance(tartget_variable_type, ArrayType): # multidimensional array - array_length = self._get_array_length(tartget_variable_type, info.slot) + if isinstance(target_variable_type, ArrayType): # multidimensional array + array_length = self._get_array_length(target_variable_type, info.slot) for j in range(min(array_length, self.max_depth)): info = self.get_storage_slot( @@ -722,8 +724,7 @@ def _all_array_slots( deep_key=str(j), ) if info: - elem = elems[str(i)] - elem.elems[str(j)] = info + elems[str(i)].elems[str(j)] = info return elems def _get_array_length(self, type_: Type, slot: int) -> int: From 28b4b84ef838cff4514d3c8e6ae1c6bb0db024ed Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 11:04:01 -0500 Subject: [PATCH 19/23] add NestedElem type hint and fix get_slot_values for nested type --- slither/tools/read_storage/read_storage.py | 51 ++++++++++------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 02533d402e..33f18dd840 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -48,9 +48,8 @@ class SlotInfo: size: int offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None - # For structure, str->SlotInfo, for array, str-> SlotInfo - elems: Elem = field(default_factory=lambda: Elem({})) - struct_var: Optional[str] = None + # For structure and array, str->SlotInfo + elems: Elem = field(default_factory=lambda:{}) class SlitherReadStorageException(Exception): @@ -196,19 +195,12 @@ def get_storage_slot( if environ.get("SILENT") is None: logger.info(self.log) self.log = "" - if environ.get("TABLE") is None: - return SlotInfo( - type_string=type_to, - slot=int_slot, - size=size, - offset=offset, - ) + return SlotInfo( type_string=type_to, slot=int_slot, size=size, offset=offset, - struct_var=struct_var, ) def get_target_variables(self, **kwargs) -> None: @@ -234,15 +226,18 @@ def get_slot_values(self) -> None: stack = list(self.slot_info.values()) while stack: slot_info = stack.pop() - if slot_info.elems: - stack.extend(slot_info.elems.values()) - hex_bytes = get_storage_data( - self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") - ) - slot_info.value = self.convert_value_to_type( - hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string - ) - logger.info(f"\nValue: {slot_info.value}\n") + if isinstance(slot_info, dict): # NestedElem + stack.extend(slot_info.values()) + elif slot_info.elems: + stack.extend(list(slot_info.elems.values())) + if isinstance(slot_info, SlotInfo): + hex_bytes = get_storage_data( + self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") + ) + slot_info.value = self.convert_value_to_type( + hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string + ) + logger.info(f"\nValue: {slot_info.value}\n") def get_all_storage_variables(self, func: Callable = None) -> None: """Fetches all storage variables from a list of contracts. @@ -674,10 +669,7 @@ def _all_struct_slots( self, var: StateVariable, st: Structure, contract: Contract, key: Optional[int] = None ) -> Elem: """Retrieves all members of a struct.""" - if isinstance(var.type.type, Structure): - struct_elems = var.type.type.elems_ordered - else: - struct_elems = var.type.type.type.elems_ordered + struct_elems = st.elems_ordered data = Elem({}) for elem in struct_elems: info = self.get_storage_slot( @@ -694,16 +686,17 @@ def _all_struct_slots( # pylint: disable=too-many-nested-blocks def _all_array_slots( self, var: StateVariable, contract: Contract, type_: ArrayType, slot: int - ) -> Elem: + ) -> Union[Elem, NestedElem]: """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) - elems = Elem({}) target_variable_type = type_.type if isinstance(target_variable_type, UserDefinedType) and isinstance(target_variable_type.type, Structure): + nested_elems = NestedElem({}) for i in range(min(array_length, self.max_depth)): - elems[str(i)] = self._all_struct_slots(var, target_variable_type, contract, key=i) - + nested_elems[str(i)] = self._all_struct_slots(var, target_variable_type.type, contract, key=i) + return nested_elems else: + elems = Elem({}) for i in range(min(array_length, self.max_depth)): info = self.get_storage_slot( var, @@ -725,7 +718,7 @@ def _all_array_slots( ) if info: elems[str(i)].elems[str(j)] = info - return elems + return elems def _get_array_length(self, type_: Type, slot: int) -> int: """Gets the length of dynamic and fixed arrays. From a5a8df6a799b66a4627a7cbe77feecaf819df03c Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 11:55:29 -0500 Subject: [PATCH 20/23] implement table for MyPrettyTable and remove tabulate dep. --- .github/workflows/read_storage.yml | 1 - slither/tools/read_storage/__main__.py | 32 +--- slither/tools/read_storage/read_storage.py | 198 ++++----------------- 3 files changed, 37 insertions(+), 194 deletions(-) diff --git a/.github/workflows/read_storage.yml b/.github/workflows/read_storage.yml index 250c214e17..8a423d6c26 100644 --- a/.github/workflows/read_storage.yml +++ b/.github/workflows/read_storage.yml @@ -37,7 +37,6 @@ jobs: run: | pip install ".[dev]" pip install web3 - pip install tabulate==0.8.10 solc-select install 0.8.1 solc-select install 0.8.10 solc-select use 0.8.1 diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 0f3bb2e83f..bb6689d452 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -4,7 +4,6 @@ import dataclasses import json import argparse -from os import environ from crytic_compile import cryticparser @@ -87,17 +86,11 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( - "--table-storage-layout", + "--table", action="store_true", help="Print table view of storage layout", ) - parser.add_argument( - "--table-storage-value", - action="store_true", - help="Print table view of storage layout & values", - ) - parser.add_argument( "--silent", action="store_true", @@ -140,20 +133,6 @@ def main() -> None: srs.rpc = args.rpc_url - if args.table_storage_layout: - environ["TABLE"] = "1" - srs.get_all_storage_variables() - srs.get_storage_layout() - srs.print_table() - return - - if args.table_storage_value: - environ["TABLE"] = "1" - srs.get_all_storage_variables() - srs.get_storage_layout() - srs.print_table_with_values() - return - if args.layout: srs.get_all_storage_variables() srs.get_storage_layout() @@ -167,10 +146,13 @@ def main() -> None: # To retrieve slot values an rpc url is required. if args.value: assert args.rpc_url - srs.get_slot_values() + srs.walk_slot_info(srs.get_slot_values) - # Only write file if storage layout is used. - if len(srs.slot_info) > 1: + if args.table: + srs.walk_slot_info(srs.convert_slot_info_to_rows) + print(srs.table) + # Only write file if storage layout is used. TODO add flag for file + elif len(srs.slot_info) > 1: with open("storage_layout.json", "w", encoding="utf-8") as file: slot_infos_json = { key: dataclasses.asdict(value) for key, value in srs.slot_info.items() diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 33f18dd840..f4b911e2df 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -3,7 +3,7 @@ from math import floor from os import environ from typing import Callable, Optional, Tuple, Union, List, Dict, Any, NewType - +from slither.utils.myprettytable import MyPrettyTable try: from web3 import Web3 from eth_typing.evm import ChecksumAddress @@ -20,14 +20,7 @@ print("$ pip3 install web3 --user\n") sys.exit(-1) -# try: -# from tabulate import tabulate -# except ImportError: -# print("ERROR: in order to use slither-read-storage --table, you need to install tabulate") -# print("$ pip3 install tabulate --user\n") -# sys.exit(-1) - -from dataclasses import dataclass, field +from dataclasses import dataclass, field, fields from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType @@ -43,6 +36,7 @@ NestedElem = NewType("NestedElem", Dict[str, Elem]) @dataclass class SlotInfo: + name: str type_string: str slot: int size: int @@ -68,6 +62,7 @@ def __init__(self, contracts: List[Contract], max_depth: int) -> None: self._checksum_address: Optional[ChecksumAddress] = None self.storage_address: Optional[str] = None self.rpc: Optional[str] = None + self.table: Optional[MyPrettyTable] = None @property def contracts(self) -> List[Contract]: @@ -167,6 +162,7 @@ def get_storage_slot( type_to = target_variable_type.name elif isinstance(target_variable_type, ArrayType) and key: + var_log_name = f"{var_log_name}[{key}]" info, type_to, slot, size, offset = self._find_array_slot( target_variable_type, slot, @@ -177,7 +173,7 @@ def get_storage_slot( self.log += info elif isinstance(target_variable_type, UserDefinedType) and struct_var: - var_log_name = struct_var + var_log_name = f"{var_log_name}.{struct_var}" target_variable_type_type = target_variable_type.type assert isinstance(target_variable_type_type, Structure) elems = target_variable_type_type.elems_ordered @@ -197,6 +193,7 @@ def get_storage_slot( self.log = "" return SlotInfo( + name=var_log_name, type_string=type_to, slot=int_slot, size=size, @@ -219,10 +216,7 @@ def get_target_variables(self, **kwargs) -> None: if slot_info: self._slot_info[f"{contract.name}.{var.name}"] = slot_info - def get_slot_values(self) -> None: - """ - Fetches the slot values - """ + def walk_slot_info(self, func: Callable) -> None: stack = list(self.slot_info.values()) while stack: slot_info = stack.pop() @@ -231,13 +225,19 @@ def get_slot_values(self) -> None: elif slot_info.elems: stack.extend(list(slot_info.elems.values())) if isinstance(slot_info, SlotInfo): - hex_bytes = get_storage_data( - self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") - ) - slot_info.value = self.convert_value_to_type( - hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string - ) - logger.info(f"\nValue: {slot_info.value}\n") + func(slot_info) + + def get_slot_values(self, slot_info: SlotInfo) -> None: + """ Fetches the slot value of `SlotInfo` object + :param slot_info: + """ + hex_bytes = get_storage_data( + self.web3, self.checksum_address, int.to_bytes(slot_info.slot, 32, byteorder="big") + ) + slot_info.value = self.convert_value_to_type( + hex_bytes, slot_info.size, slot_info.offset, slot_info.type_string + ) + logger.info(f"\nValue: {slot_info.value}\n") def get_all_storage_variables(self, func: Callable = None) -> None: """Fetches all storage variables from a list of contracts. @@ -256,153 +256,15 @@ def get_all_storage_variables(self, func: Callable = None) -> None: ) ) - def print_table(self) -> None: - - tabulate_data = [] - - for _, state_var in self.target_variables: - type_ = state_var.type - var = state_var.name - info = self.slot_info[var] - slot = info.slot - offset = info.offset - size = info.size - type_string = info.type_string - - tabulate_data.append([slot, offset, size, type_string, var]) - - if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): - tabulate_data.pop() - for item in info.elems.values(): - slot = item.slot - offset = item.offset - size = item.size - type_string = item.type_string - struct_var = item.struct_var - - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var} -> {struct_var}" - - tabulate_data.append( - [slot, offset, size, type_string, var_name_struct_or_array_var] - ) - - if isinstance(type_, ArrayType): - tabulate_data.pop() - for item_key, item in info.elems.items(): - slot = item.slot - offset = item.offset - size = item.size - type_string = item.type_string - struct_var = item.struct_var - - # doesn't handle deep keys currently - var_name_struct_or_array_var = f"{var}[{item_key}] -> {struct_var}" - - tabulate_data.append( - [slot, offset, size, type_string, var_name_struct_or_array_var] - ) - - print( - tabulate( - tabulate_data, headers=["slot", "offset", "size", "type", "name"], tablefmt="grid" - ) - ) - - def print_table_with_values(self) -> None: - - print("Processing, grabbing values from rpc endpoint...") - tabulate_data = [] - - for _, state_var in self.target_variables: - type_ = state_var.type - var = state_var.name - info = self.slot_info[var] - slot = info.slot - offset = info.offset - size = info.size - type_string = info.type_string - - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - var, - self.convert_value_to_type( - get_storage_data( - self.web3, - self.checksum_address, - int.to_bytes(slot, 32, byteorder="big"), - ), - size, - offset, - type_string, - ), - ] - ) - - if isinstance(type_, UserDefinedType) and isinstance(type_.type, Structure): - tabulate_data.pop() - for item in info.elems.values(): - slot = item.slot - offset = item.offset - size = item.size - type_string = item.type_string - struct_var = item.struct_var - - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - f"{var} -> {struct_var}", # doesn't handle deep keys currently - self.convert_value_to_type( - get_storage_data( - self.web3, - self.checksum_address, - int.to_bytes(slot, 32, byteorder="big"), - ), - size, - offset, - type_string, - ), - ] - ) - - if isinstance(type_, ArrayType): - tabulate_data.pop() - for item_key, item in info.elems.items(): - # for elem in info.elems[item].values(): - slot = item.slot - offset = item.offset - size = item.size - type_string = item.type_string - struct_var = item.struct_var - - hex_bytes = get_storage_data( - self.web3, self.checksum_address, int.to_bytes(slot, 32, byteorder="big") - ) - tabulate_data.append( - [ - slot, - offset, - size, - type_string, - f"{var}[{item_key}] -> {struct_var}", # doesn't handle deep keys currently - self.convert_value_to_type(hex_bytes, size, offset, type_string), - ] - ) - - print( - tabulate( - tabulate_data, - headers=["slot", "offset", "size", "type", "name", "value"], - tablefmt="grid", - ) - ) + def convert_slot_info_to_rows(self, slot_info: SlotInfo) -> None: + """ Convert and append slot info to table. Create table if it + does not yet exist + :param slot_info: + """ + field_names = [field.name for field in fields(SlotInfo) if field.name != "elems"] + if not self.table: + self.table = MyPrettyTable(field_names) + self.table.add_row([getattr(slot_info, field) for field in field_names]) @staticmethod def _find_struct_var_slot( From d11ed3608dbc9ac528e23f56d5afd22a93059158 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 16:39:19 -0500 Subject: [PATCH 21/23] fix json output and update with new fields (name and elems) --- slither/tools/read_storage/__main__.py | 5 +- slither/tools/read_storage/read_storage.py | 128 +++++---- tests/storage-layout/TEST_storage_layout.json | 259 +++++++++++++----- tests/test_read_storage.py | 5 +- 4 files changed, 258 insertions(+), 139 deletions(-) diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index bb6689d452..435a138723 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -1,7 +1,6 @@ """ Tool to read on-chain storage from EVM """ -import dataclasses import json import argparse @@ -154,9 +153,7 @@ def main() -> None: # Only write file if storage layout is used. TODO add flag for file elif len(srs.slot_info) > 1: with open("storage_layout.json", "w", encoding="utf-8") as file: - slot_infos_json = { - key: dataclasses.asdict(value) for key, value in srs.slot_info.items() - } + slot_infos_json = srs.to_json() json.dump(slot_infos_json, file, indent=4) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index f4b911e2df..c23d8f661f 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -1,15 +1,13 @@ import logging import sys from math import floor -from os import environ from typing import Callable, Optional, Tuple, Union, List, Dict, Any, NewType -from slither.utils.myprettytable import MyPrettyTable + try: from web3 import Web3 from eth_typing.evm import ChecksumAddress from eth_abi import decode_single, encode_abi from eth_utils import keccak - from hexbytes import HexBytes from .utils import ( get_offset_value, get_storage_data, @@ -20,8 +18,8 @@ print("$ pip3 install web3 --user\n") sys.exit(-1) -from dataclasses import dataclass, field, fields - +import dataclasses +from slither.utils.myprettytable import MyPrettyTable from slither.core.solidity_types.type import Type from slither.core.solidity_types import ArrayType, ElementaryType, UserDefinedType, MappingType from slither.core.declarations import Contract, Structure @@ -33,8 +31,10 @@ logger.setLevel(logging.INFO) Elem = NewType("Elem", Dict[str, "SlotInfo"]) -NestedElem = NewType("NestedElem", Dict[str, Elem]) -@dataclass +NestedElem = NewType("NestedElem", Dict[str, Elem]) + + +@dataclasses.dataclass class SlotInfo: name: str type_string: str @@ -43,7 +43,7 @@ class SlotInfo: offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None # For structure and array, str->SlotInfo - elems: Elem = field(default_factory=lambda:{}) + elems: Elem = dataclasses.field(default_factory=lambda: {}) class SlitherReadStorageException(Exception): @@ -161,7 +161,7 @@ def get_storage_slot( if isinstance(target_variable_type, ElementaryType): type_to = target_variable_type.name - elif isinstance(target_variable_type, ArrayType) and key: + elif isinstance(target_variable_type, ArrayType) and key is not None: var_log_name = f"{var_log_name}[{key}]" info, type_to, slot, size, offset = self._find_array_slot( target_variable_type, @@ -172,7 +172,7 @@ def get_storage_slot( ) self.log += info - elif isinstance(target_variable_type, UserDefinedType) and struct_var: + elif isinstance(target_variable_type, UserDefinedType) and struct_var is not None: var_log_name = f"{var_log_name}.{struct_var}" target_variable_type_type = target_variable_type.type assert isinstance(target_variable_type_type, Structure) @@ -188,8 +188,7 @@ def get_storage_slot( int_slot = int.from_bytes(slot, byteorder="big") self.log += f"\nName: {var_log_name}\nType: {type_to}\nSlot: {int_slot}\n" - if environ.get("SILENT") is None: - logger.info(self.log) + logger.info(self.log) self.log = "" return SlotInfo( @@ -210,9 +209,7 @@ def get_target_variables(self, **kwargs) -> None: struct_var (str): Structure variable name. """ for contract, var in self.target_variables: - slot_info = self.get_storage_slot( - var, contract, **kwargs - ) + slot_info = self.get_storage_slot(var, contract, **kwargs) if slot_info: self._slot_info[f"{contract.name}.{var.name}"] = slot_info @@ -220,15 +217,15 @@ def walk_slot_info(self, func: Callable) -> None: stack = list(self.slot_info.values()) while stack: slot_info = stack.pop() - if isinstance(slot_info, dict): # NestedElem + if isinstance(slot_info, dict): # NestedElem stack.extend(slot_info.values()) elif slot_info.elems: stack.extend(list(slot_info.elems.values())) if isinstance(slot_info, SlotInfo): func(slot_info) - + def get_slot_values(self, slot_info: SlotInfo) -> None: - """ Fetches the slot value of `SlotInfo` object + """Fetches the slot value of `SlotInfo` object :param slot_info: """ hex_bytes = get_storage_data( @@ -257,15 +254,20 @@ def get_all_storage_variables(self, func: Callable = None) -> None: ) def convert_slot_info_to_rows(self, slot_info: SlotInfo) -> None: - """ Convert and append slot info to table. Create table if it - does not yet exist + """Convert and append slot info to table. Create table if it + does not yet exist :param slot_info: """ - field_names = [field.name for field in fields(SlotInfo) if field.name != "elems"] + field_names = [ + field.name for field in dataclasses.fields(SlotInfo) if field.name != "elems" + ] if not self.table: self.table = MyPrettyTable(field_names) self.table.add_row([getattr(slot_info, field) for field in field_names]) + def to_json(self) -> Dict: + return {key: dataclasses.asdict(value) for key, value in self.slot_info.items()} + @staticmethod def _find_struct_var_slot( elems: List[StructureVariable], slot_as_bytes: bytes, struct_var: str @@ -364,7 +366,9 @@ def _find_array_slot( elif target_variable_type.is_fixed_array: slot_int = int.from_bytes(slot, "big") + int(key) - if isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[i] + if isinstance(target_variable_type_type, UserDefinedType) and isinstance( + target_variable_type_type.type, Structure + ): # struct[i] type_to = target_variable_type_type.type.name if not struct_var: return info, type_to, int.to_bytes(slot_int, 32, "big"), size, offset @@ -381,7 +385,9 @@ def _find_array_slot( assert target_variable_type_type.size size = target_variable_type_type.size # bits - elif isinstance(target_variable_type_type, UserDefinedType) and isinstance(target_variable_type_type.type, Structure): # struct[] + elif isinstance(target_variable_type_type, UserDefinedType) and isinstance( + target_variable_type_type.type, Structure + ): # struct[] slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) type_to = target_variable_type_type.type.name @@ -395,7 +401,10 @@ def _find_array_slot( info += info_tmp else: - assert isinstance(target_variable_type_type, ElementaryType) and target_variable_type_type.size + assert ( + isinstance(target_variable_type_type, ElementaryType) + and target_variable_type_type.size + ) slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) type_to = target_variable_type_type.name @@ -465,7 +474,9 @@ def _find_mapping_slot( # mapping(elem => mapping(elem => elem)) target_variable_type_type_to_type_to = target_variable_type.type_to.type_to - assert isinstance(target_variable_type_type_to_type_to, (UserDefinedType, ElementaryType)) + assert isinstance( + target_variable_type_type_to_type_to, (UserDefinedType, ElementaryType) + ) type_to = str(target_variable_type_type_to_type_to.type) byte_size, _ = target_variable_type_type_to_type_to.storage_size size = byte_size * 8 # bits @@ -491,7 +502,9 @@ def _find_mapping_slot( size = byte_size * 8 # bits else: - raise NotImplementedError(f"{target_variable_type} => {target_variable_type.type_to} not implemented") + raise NotImplementedError( + f"{target_variable_type} => {target_variable_type.type_to} not implemented" + ) return info, type_to, slot, size, offset @@ -506,10 +519,9 @@ def get_variable_info( size = byte_size * 8 # bits (int_slot, offset) = contract.compilation_unit.storage_layout_of(contract, target_variable) offset *= 8 # bits - if environ.get("SILENT") is None: - logger.info( - f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" - ) + logger.info( + f"\nContract '{contract.name}'\n{target_variable.canonical_name} with type {target_variable.type} is located at slot: {int_slot}\n" + ) return int_slot, size, offset, type_to @@ -552,35 +564,39 @@ def _all_array_slots( """Retrieves all members of an array.""" array_length = self._get_array_length(type_, slot) target_variable_type = type_.type - if isinstance(target_variable_type, UserDefinedType) and isinstance(target_variable_type.type, Structure): + if isinstance(target_variable_type, UserDefinedType) and isinstance( + target_variable_type.type, Structure + ): nested_elems = NestedElem({}) for i in range(min(array_length, self.max_depth)): - nested_elems[str(i)] = self._all_struct_slots(var, target_variable_type.type, contract, key=i) - return nested_elems - else: - elems = Elem({}) - for i in range(min(array_length, self.max_depth)): - info = self.get_storage_slot( - var, - contract, - key=str(i), + nested_elems[str(i)] = self._all_struct_slots( + var, target_variable_type.type, contract, key=i ) - if info: - elems[str(i)] = info - - if isinstance(target_variable_type, ArrayType): # multidimensional array - array_length = self._get_array_length(target_variable_type, info.slot) - - for j in range(min(array_length, self.max_depth)): - info = self.get_storage_slot( - var, - contract, - key=str(i), - deep_key=str(j), - ) - if info: - elems[str(i)].elems[str(j)] = info - return elems + return nested_elems + + elems = Elem({}) + for i in range(min(array_length, self.max_depth)): + info = self.get_storage_slot( + var, + contract, + key=str(i), + ) + if info: + elems[str(i)] = info + + if isinstance(target_variable_type, ArrayType): # multidimensional array + array_length = self._get_array_length(target_variable_type, info.slot) + + for j in range(min(array_length, self.max_depth)): + info = self.get_storage_slot( + var, + contract, + key=str(i), + deep_key=str(j), + ) + if info: + elems[str(i)].elems[str(j)] = info + return elems def _get_array_length(self, type_: Type, slot: int) -> int: """Gets the length of dynamic and fixed arrays. diff --git a/tests/storage-layout/TEST_storage_layout.json b/tests/storage-layout/TEST_storage_layout.json index e981b17828..9de3aacfd1 100644 --- a/tests/storage-layout/TEST_storage_layout.json +++ b/tests/storage-layout/TEST_storage_layout.json @@ -1,469 +1,576 @@ { "packedUint": { + "name": "packedUint", "type_string": "uint248", "slot": 0, "size": 248, "offset": 0, - "value": 1 + "value": 1, + "elems": {} }, "packedBool": { + "name": "packedBool", "type_string": "bool", "slot": 0, "size": 8, "offset": 248, - "value": true + "value": true, + "elems": {} }, "_packedStruct": { + "name": "_packedStruct", "type_string": "StorageLayout.PackedStruct", "slot": 1, "size": 256, "offset": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000101", "elems": { "b": { + "name": "_packedStruct.b", "type_string": "bool", "slot": 1, "size": 8, "offset": 0, - "value": true + "value": true, + "elems": {} }, "a": { + "name": "_packedStruct.a", "type_string": "uint248", "slot": 1, "size": 248, "offset": 8, - "value": 1 + "value": 1, + "elems": {} } - }, - "value": "0000000000000000000000000000000000000000000000000000000000000101" + } }, "mappingPackedStruct": { + "name": "mappingPackedStruct", "type_string": "mapping(uint256 => StorageLayout.PackedStruct)", "slot": 2, "size": 256, "offset": 0, - "value": 0 + "value": 0, + "elems": {} }, "deepMappingPackedStruct": { + "name": "deepMappingPackedStruct", "type_string": "mapping(address => mapping(uint256 => StorageLayout.PackedStruct))", "slot": 3, "size": 256, "offset": 0, - "value": 0 + "value": 0, + "elems": {} }, "deepMappingElementaryTypes": { + "name": "deepMappingElementaryTypes", "type_string": "mapping(address => mapping(uint256 => bool))", "slot": 4, "size": 256, "offset": 0, - "value": 0 + "value": 0, + "elems": {} }, "mappingDynamicArrayOfStructs": { + "name": "mappingDynamicArrayOfStructs", "type_string": "mapping(address => StorageLayout.PackedStruct[])", "slot": 5, "size": 256, "offset": 0, - "value": 0 + "value": 0, + "elems": {} }, "_address": { + "name": "_address", "type_string": "address", "slot": 6, "size": 160, "offset": 0, - "value": "0xae17D2dD99e07CA3bF2571CCAcEAA9e2Aefc2Dc6" + "value": "0xae17D2dD99e07CA3bF2571CCAcEAA9e2Aefc2Dc6", + "elems": {} }, "_string": { + "name": "_string", "type_string": "string", "slot": 7, "size": 256, "offset": 0, - "value": "slither-read-storage" + "value": "slither-read-storage", + "elems": {} }, "packedUint8": { + "name": "packedUint8", "type_string": "uint8", "slot": 8, "size": 8, "offset": 0, - "value": 8 + "value": 8, + "elems": {} }, "packedBytes": { + "name": "packedBytes", "type_string": "bytes8", "slot": 8, "size": 64, "offset": 8, - "value": "6161616161616161" + "value": "6161616161616161", + "elems": {} }, "_enumA": { + "name": "_enumA", "type_string": "StorageLayout.Enum", "slot": 8, "size": 8, "offset": 72, - "value": "00" + "value": "00", + "elems": {} }, "_enumB": { + "name": "_enumB", "type_string": "StorageLayout.Enum", "slot": 8, "size": 8, "offset": 80, - "value": "01" + "value": "01", + "elems": {} }, "_enumC": { + "name": "_enumC", "type_string": "StorageLayout.Enum", "slot": 8, "size": 8, "offset": 88, - "value": "02" + "value": "02", + "elems": {} }, "fixedArray": { + "name": "fixedArray", "type_string": "uint256[3]", "slot": 9, "size": 768, "offset": 0, + "value": 1, "elems": { "0": { + "name": "fixedArray[0]", "type_string": "uint256", "slot": 9, "size": 256, "offset": 0, - "value": 1 + "value": 1, + "elems": {} }, "1": { + "name": "fixedArray[1]", "type_string": "uint256", "slot": 10, "size": 256, "offset": 0, - "value": 2 + "value": 2, + "elems": {} }, "2": { + "name": "fixedArray[2]", "type_string": "uint256", "slot": 11, "size": 256, "offset": 0, - "value": 3 + "value": 3, + "elems": {} } - }, - "value": 1 + } }, "dynamicArrayOfFixedArrays": { + "name": "dynamicArrayOfFixedArrays", "type_string": "uint256[3][]", "slot": 12, "size": 256, "offset": 0, + "value": 2, "elems": { "0": { + "name": "dynamicArrayOfFixedArrays[0]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386055, "size": 256, "offset": 0, + "value": 1, "elems": { "0": { + "name": "dynamicArrayOfFixedArrays[0]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386055, "size": 256, "offset": 0, - "value": 1 + "value": 1, + "elems": {} }, "1": { + "name": "dynamicArrayOfFixedArrays[0]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386056, "size": 256, "offset": 0, - "value": 2 + "value": 2, + "elems": {} }, "2": { + "name": "dynamicArrayOfFixedArrays[0]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386057, "size": 256, "offset": 0, - "value": 3 + "value": 3, + "elems": {} } - }, - "value": 1 + } }, "1": { + "name": "dynamicArrayOfFixedArrays[1]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386058, "size": 256, "offset": 0, + "value": 4, "elems": { "0": { + "name": "dynamicArrayOfFixedArrays[1]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386058, "size": 256, "offset": 0, - "value": 4 + "value": 4, + "elems": {} }, "1": { + "name": "dynamicArrayOfFixedArrays[1]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386059, "size": 256, "offset": 0, - "value": 5 + "value": 5, + "elems": {} }, "2": { + "name": "dynamicArrayOfFixedArrays[1]", "type_string": "uint256", "slot": 101051993584849178915136821395265346177868384823507754984078593667947067386060, "size": 256, "offset": 0, - "value": 6 + "value": 6, + "elems": {} } - }, - "value": 4 + } } - }, - "value": 2 + } }, "fixedArrayofDynamicArrays": { + "name": "fixedArrayofDynamicArrays", "type_string": "uint256[][3]", "slot": 13, "size": 768, "offset": 0, + "value": 1, "elems": { "0": { + "name": "fixedArrayofDynamicArrays[0]", "type_string": "uint256", "slot": 13, "size": 256, "offset": 0, + "value": 1, "elems": { "0": { + "name": "fixedArrayofDynamicArrays[0]", "type_string": "uint256", "slot": 97569884605916225051403212656556507955018248777258318895762758024193532305077, "size": 256, "offset": 0, - "value": 7 + "value": 7, + "elems": {} } - }, - "value": 1 + } }, "1": { + "name": "fixedArrayofDynamicArrays[1]", "type_string": "uint256", "slot": 14, "size": 256, "offset": 0, + "value": 2, "elems": { "0": { + "name": "fixedArrayofDynamicArrays[1]", "type_string": "uint256", "slot": 84800337471693920904250232874319843718400766719524250287777680170677855896573, "size": 256, "offset": 0, - "value": 8 + "value": 8, + "elems": {} }, "1": { + "name": "fixedArrayofDynamicArrays[1]", "type_string": "uint256", "slot": 84800337471693920904250232874319843718400766719524250287777680170677855896574, "size": 256, "offset": 0, - "value": 9 + "value": 9, + "elems": {} } - }, - "value": 2 + } }, "2": { + "name": "fixedArrayofDynamicArrays[2]", "type_string": "uint256", "slot": 15, "size": 256, "offset": 0, + "value": 3, "elems": { "0": { + "name": "fixedArrayofDynamicArrays[2]", "type_string": "uint256", "slot": 63806209331542711802848847270949280092855778197726125910674179583545433573378, "size": 256, "offset": 0, - "value": 10 + "value": 10, + "elems": {} }, "1": { + "name": "fixedArrayofDynamicArrays[2]", "type_string": "uint256", "slot": 63806209331542711802848847270949280092855778197726125910674179583545433573379, "size": 256, "offset": 0, - "value": 11 + "value": 11, + "elems": {} }, "2": { + "name": "fixedArrayofDynamicArrays[2]", "type_string": "uint256", "slot": 63806209331542711802848847270949280092855778197726125910674179583545433573380, "size": 256, "offset": 0, - "value": 12 + "value": 12, + "elems": {} } - }, - "value": 3 + } } - }, - "value": 1 + } }, "multidimensionalArray": { + "name": "multidimensionalArray", "type_string": "uint256[][]", "slot": 16, "size": 256, "offset": 0, + "value": 3, "elems": { "0": { + "name": "multidimensionalArray[0]", "type_string": "uint256", "slot": 12396694973890998440467380340983585058878106250672390494374587083972727727730, "size": 256, "offset": 0, + "value": 1, "elems": { "0": { + "name": "multidimensionalArray[0]", "type_string": "uint256", "slot": 93856215500098298973000561543003607329881518401177956003908346942307446808932, "size": 256, "offset": 0, - "value": 13 + "value": 13, + "elems": {} } - }, - "value": 1 + } }, "1": { + "name": "multidimensionalArray[1]", "type_string": "uint256", "slot": 12396694973890998440467380340983585058878106250672390494374587083972727727731, "size": 256, "offset": 0, + "value": 2, "elems": { "0": { + "name": "multidimensionalArray[1]", "type_string": "uint256", "slot": 48332168562525185806884758054388614910060623018875025120987491603435926351511, "size": 256, "offset": 0, - "value": 14 + "value": 14, + "elems": {} }, "1": { + "name": "multidimensionalArray[1]", "type_string": "uint256", "slot": 48332168562525185806884758054388614910060623018875025120987491603435926351512, "size": 256, "offset": 0, - "value": 15 + "value": 15, + "elems": {} } - }, - "value": 2 + } }, "2": { + "name": "multidimensionalArray[2]", "type_string": "uint256", "slot": 12396694973890998440467380340983585058878106250672390494374587083972727727732, "size": 256, "offset": 0, + "value": 3, "elems": { "0": { + "name": "multidimensionalArray[2]", "type_string": "uint256", "slot": 69037578548663760355678879060995014288537668748590083357305779656188235687653, "size": 256, "offset": 0, - "value": 16 + "value": 16, + "elems": {} }, "1": { + "name": "multidimensionalArray[2]", "type_string": "uint256", "slot": 69037578548663760355678879060995014288537668748590083357305779656188235687654, "size": 256, "offset": 0, - "value": 17 + "value": 17, + "elems": {} }, "2": { + "name": "multidimensionalArray[2]", "type_string": "uint256", "slot": 69037578548663760355678879060995014288537668748590083357305779656188235687655, "size": 256, "offset": 0, - "value": 18 + "value": 18, + "elems": {} } - }, - "value": 3 + } } - }, - "value": 3 + } }, "dynamicArrayOfStructs": { + "name": "dynamicArrayOfStructs", "type_string": "StorageLayout.PackedStruct[]", "slot": 17, "size": 256, "offset": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000002", "elems": { "0": { "b": { + "name": "dynamicArrayOfStructs[0]", "type_string": "bool", "slot": 22581645139872629890233439717971975110198959689450188087151966948260709403752, "size": 8, "offset": 0, - "value": true + "value": true, + "elems": {} }, "a": { + "name": "dynamicArrayOfStructs[0]", "type_string": "uint248", "slot": 22581645139872629890233439717971975110198959689450188087151966948260709403752, "size": 248, "offset": 8, - "value": 1 + "value": 1, + "elems": {} } }, "1": { "b": { + "name": "dynamicArrayOfStructs[1]", "type_string": "bool", "slot": 22581645139872629890233439717971975110198959689450188087151966948260709403753, "size": 8, "offset": 0, - "value": false + "value": false, + "elems": {} }, "a": { + "name": "dynamicArrayOfStructs[1]", "type_string": "uint248", "slot": 22581645139872629890233439717971975110198959689450188087151966948260709403753, "size": 248, "offset": 8, - "value": 10 + "value": 10, + "elems": {} } } - }, - "value": "0000000000000000000000000000000000000000000000000000000000000002" + } }, "fixedArrayOfStructs": { + "name": "fixedArrayOfStructs", "type_string": "StorageLayout.PackedStruct[3]", "slot": 18, "size": 768, "offset": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000101", "elems": { "0": { "b": { + "name": "fixedArrayOfStructs[0]", "type_string": "bool", "slot": 18, "size": 8, "offset": 0, - "value": true + "value": true, + "elems": {} }, "a": { + "name": "fixedArrayOfStructs[0]", "type_string": "uint248", "slot": 18, "size": 248, "offset": 8, - "value": 1 + "value": 1, + "elems": {} } }, "1": { "b": { + "name": "fixedArrayOfStructs[1]", "type_string": "bool", "slot": 19, "size": 8, "offset": 0, - "value": false + "value": false, + "elems": {} }, "a": { + "name": "fixedArrayOfStructs[1]", "type_string": "uint248", "slot": 19, "size": 248, "offset": 8, - "value": 10 + "value": 10, + "elems": {} } }, "2": { "b": { + "name": "fixedArrayOfStructs[2]", "type_string": "bool", "slot": 20, "size": 8, "offset": 0, - "value": false + "value": false, + "elems": {} }, "a": { + "name": "fixedArrayOfStructs[2]", "type_string": "uint248", "slot": 20, "size": 248, "offset": 8, - "value": 0 + "value": 0, + "elems": {} } } - }, - "value": "0000000000000000000000000000000000000000000000000000000000000101" + } } } diff --git a/tests/test_read_storage.py b/tests/test_read_storage.py index 3b1aaa8232..67a89dae87 100644 --- a/tests/test_read_storage.py +++ b/tests/test_read_storage.py @@ -1,4 +1,3 @@ -import dataclasses import re import os import sys @@ -116,9 +115,9 @@ def test_read_storage(web3, ganache) -> None: srs.storage_address = address srs.get_all_storage_variables() srs.get_storage_layout() - srs.get_slot_values() + srs.walk_slot_info(srs.get_slot_values) with open("storage_layout.json", "w", encoding="utf-8") as file: - slot_infos_json = {key: dataclasses.asdict(value) for key, value in srs.slot_info.items()} + slot_infos_json = srs.to_json() json.dump(slot_infos_json, file, indent=4) expected_file = os.path.join(STORAGE_TEST_ROOT, "TEST_storage_layout.json") From 7ec42127cc01472d18f027662cef861bac1e621a Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 2 Aug 2022 17:04:55 -0500 Subject: [PATCH 22/23] more pylint and black warnings --- .../core/solidity_types/elementary_type.py | 2 +- slither/tools/read_storage/read_storage.py | 43 +++++++++---------- slither/tools/read_storage/utils/utils.py | 19 ++++---- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index e589054c28..396bd200b9 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -1,5 +1,5 @@ import itertools -from typing import Optional, Tuple +from typing import Tuple from slither.core.solidity_types.type import Type diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index c23d8f661f..78e0d610be 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -1,7 +1,7 @@ import logging import sys from math import floor -from typing import Callable, Optional, Tuple, Union, List, Dict, Any, NewType +from typing import Callable, Optional, Tuple, Union, List, Dict, Any try: from web3 import Web3 @@ -30,8 +30,8 @@ logger = logging.getLogger("Slither-read-storage") logger.setLevel(logging.INFO) -Elem = NewType("Elem", Dict[str, "SlotInfo"]) -NestedElem = NewType("NestedElem", Dict[str, Elem]) +Elem = Dict[str, "SlotInfo"] +NestedElem = Dict[str, Elem] @dataclasses.dataclass @@ -43,7 +43,7 @@ class SlotInfo: offset: int value: Optional[Union[int, bool, str, ChecksumAddress]] = None # For structure and array, str->SlotInfo - elems: Elem = dataclasses.field(default_factory=lambda: {}) + elems: Union[Elem, NestedElem] = dataclasses.field(default_factory=lambda: {}) # type: ignore[assignment] class SlitherReadStorageException(Exception): @@ -88,6 +88,8 @@ def web3(self) -> Web3: @property def checksum_address(self) -> ChecksumAddress: + if not self.storage_address: + raise ValueError if not self._checksum_address: self._checksum_address = self.web3.toChecksumAddress(self.storage_address) return self._checksum_address @@ -104,7 +106,7 @@ def slot_info(self) -> Dict[str, SlotInfo]: def get_storage_layout(self) -> None: """Retrieves the storage layout of entire contract.""" - tmp = Elem({}) + tmp = {} for contract, var in self.target_variables: type_ = var.type info = self.get_storage_slot(var, contract) @@ -291,14 +293,13 @@ def _find_struct_var_slot( var_type = var.type if isinstance(var_type, ElementaryType): size = var_type.size - if size: - if offset >= 256: - slot += 1 - offset = 0 - if struct_var == var.name: - type_to = var_type.name - break # found struct var - offset += size + if offset >= 256: + slot += 1 + offset = 0 + if struct_var == var.name: + type_to = var_type.name + break # found struct var + offset += size else: logger.info(f"{type(var_type)} is current not implemented in _find_struct_var_slot") @@ -306,7 +307,7 @@ def _find_struct_var_slot( info = f"\nStruct Variable: {struct_var}" return info, type_to, slot_as_bytes, size, offset - # pylint: disable=too-many-branches + # pylint: disable=too-many-branches,too-many-statements @staticmethod def _find_array_slot( target_variable_type: ArrayType, @@ -339,7 +340,6 @@ def _find_array_slot( target_variable_type_type, ArrayType ): # multidimensional array uint[i][], , uint[][i], or uint[][] assert isinstance(target_variable_type_type.type, ElementaryType) - assert target_variable_type_type.type.size size = target_variable_type_type.type.size type_to = target_variable_type_type.type.name @@ -382,7 +382,6 @@ def _find_array_slot( else: assert isinstance(target_variable_type_type, ElementaryType) type_to = target_variable_type_type.name - assert target_variable_type_type.size size = target_variable_type_type.size # bits elif isinstance(target_variable_type_type, UserDefinedType) and isinstance( @@ -401,10 +400,8 @@ def _find_array_slot( info += info_tmp else: - assert ( - isinstance(target_variable_type_type, ElementaryType) - and target_variable_type_type.size - ) + assert isinstance(target_variable_type_type, ElementaryType) + slot = keccak(slot) slot_int = int.from_bytes(slot, "big") + int(key) type_to = target_variable_type_type.name @@ -544,7 +541,7 @@ def _all_struct_slots( ) -> Elem: """Retrieves all members of a struct.""" struct_elems = st.elems_ordered - data = Elem({}) + data: Elem = {} for elem in struct_elems: info = self.get_storage_slot( var, @@ -567,14 +564,14 @@ def _all_array_slots( if isinstance(target_variable_type, UserDefinedType) and isinstance( target_variable_type.type, Structure ): - nested_elems = NestedElem({}) + nested_elems: NestedElem = {} for i in range(min(array_length, self.max_depth)): nested_elems[str(i)] = self._all_struct_slots( var, target_variable_type.type, contract, key=i ) return nested_elems - elems = Elem({}) + elems: Elem = {} for i in range(min(array_length, self.max_depth)): info = self.get_storage_slot( var, diff --git a/slither/tools/read_storage/utils/utils.py b/slither/tools/read_storage/utils/utils.py index 04cc6518f6..db2052286a 100644 --- a/slither/tools/read_storage/utils/utils.py +++ b/slither/tools/read_storage/utils/utils.py @@ -24,7 +24,9 @@ def get_offset_value(hex_bytes: bytes, offset: int, size: int) -> bytes: return value -def coerce_type(solidity_type: str, value: Union[int, str, bytes]) -> Union[int, bool, str, ChecksumAddress]: +def coerce_type( + solidity_type: str, value: Union[int, str, bytes] +) -> Union[int, bool, str, ChecksumAddress]: """ Converts input to the indicated type. Args: @@ -35,23 +37,22 @@ def coerce_type(solidity_type: str, value: Union[int, str, bytes]) -> Union[int, """ if "int" in solidity_type: return to_int(value) - elif "bool" in solidity_type: + if "bool" in solidity_type: return bool(to_int(value)) - elif "string" in solidity_type and isinstance(value, bytes): + if "string" in solidity_type and isinstance(value, bytes): # length * 2 is stored in lower end bits # TODO handle bytes and strings greater than 32 bytes length = int(int.from_bytes(value[-2:], "big") / 2) return to_text(value[:length]) - elif "address" in solidity_type: + if "address" in solidity_type: if not isinstance(value, (str, bytes)): - raise TypeError + raise TypeError return to_checksum_address(value) - else: - if not isinstance(value, bytes): - raise TypeError - return value.hex() + if not isinstance(value, bytes): + raise TypeError + return value.hex() def get_storage_data(web3, checksum_address: ChecksumAddress, slot: bytes) -> bytes: From 5287b0aebf3452b174c1910ff903f43b48aba683 Mon Sep 17 00:00:00 2001 From: Josselin Feist Date: Thu, 4 Aug 2022 16:26:21 +0200 Subject: [PATCH 23/23] Minor --- slither/tools/read_storage/__main__.py | 23 +++++++++++----------- slither/tools/read_storage/read_storage.py | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index 435a138723..f3cc90efca 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -23,7 +23,7 @@ def parse_args() -> argparse.Namespace: + "To retrieve a contract's storage layout:\n" + "\tslither-read-storage $TARGET address --contract-name $NAME --layout\n" + "To retrieve a contract's storage layout and values:\n" - + "\tslither-read-storage $TARGET address --contract-name $NAME --layout --values\n" + + "\tslither-read-storage $TARGET address --contract-name $NAME --layout --value\n" + "TARGET can be a contract address or project directory" ), ) @@ -73,9 +73,9 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( - "--layout", - action="store_true", - help="Toggle used to write a JSON file with the entire storage layout.", + "--json", + action="store", + help="Save the result in a JSON file.", ) parser.add_argument( @@ -132,15 +132,14 @@ def main() -> None: srs.rpc = args.rpc_url - if args.layout: - srs.get_all_storage_variables() - srs.get_storage_layout() - else: - assert args.variable_name + if args.variable_name: # Use a lambda func to only return variables that have same name as target. # x is a tuple (`Contract`, `StateVariable`). srs.get_all_storage_variables(lambda x: bool(x[1].name == args.variable_name)) srs.get_target_variables(**vars(args)) + else: + srs.get_all_storage_variables() + srs.get_storage_layout() # To retrieve slot values an rpc url is required. if args.value: @@ -150,9 +149,9 @@ def main() -> None: if args.table: srs.walk_slot_info(srs.convert_slot_info_to_rows) print(srs.table) - # Only write file if storage layout is used. TODO add flag for file - elif len(srs.slot_info) > 1: - with open("storage_layout.json", "w", encoding="utf-8") as file: + + if args.json: + with open(args.json, "w", encoding="utf-8") as file: slot_infos_json = srs.to_json() json.dump(slot_infos_json, file, indent=4) diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 78e0d610be..c9523f7330 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -106,7 +106,7 @@ def slot_info(self) -> Dict[str, SlotInfo]: def get_storage_layout(self) -> None: """Retrieves the storage layout of entire contract.""" - tmp = {} + tmp: Dict[str, SlotInfo] = {} for contract, var in self.target_variables: type_ = var.type info = self.get_storage_slot(var, contract)