diff --git a/Makefile b/Makefile index 857afca13..b498f34b7 100644 --- a/Makefile +++ b/Makefile @@ -448,8 +448,8 @@ send-eth: set-traits-multipliers: make SCRIPT_PATH=./scripts/dev/11.set-traits-multipliers.ts run -.PHONY: set-timelock-strategy -set-timelock-strategy: +.PHONY: update-timelock-strategy +update-timelock-strategy: make SCRIPT_PATH=./scripts/dev/12.set-timelock-strategy.ts run .PHONY: acl @@ -576,6 +576,10 @@ set-interest-rate-strategy: set-auction-strategy: make TASK_NAME=set-auction-strategy run-task +.PHONY: set-timelock-strategy +set-timelock-strategy: + make TASK_NAME=set-timelock-strategy run-task + .PHONY: set-supply-cap set-supply-cap: make TASK_NAME=set-supply-cap run-task diff --git a/contracts/account-abstraction/Account.sol b/contracts/account-abstraction/Account.sol index ccd0a10b0..3f6f6ae45 100644 --- a/contracts/account-abstraction/Account.sol +++ b/contracts/account-abstraction/Account.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import "../dependencies/openzeppelin/contracts/ECDSA.sol"; +import "../dependencies/openzeppelin/upgradeability/Initializable.sol"; +import "../dependencies/openzeppelin/contracts/IERC1271.sol"; import {SignatureChecker} from "../dependencies/openzeppelin/contracts/SignatureChecker.sol"; import "./base-account-abstraction/core/BaseAccount.sol"; import "./callback/TokenCallbackHandler.sol"; diff --git a/contracts/account-abstraction/AccountFactory.sol b/contracts/account-abstraction/AccountFactory.sol index b8cafecef..8193caaa1 100644 --- a/contracts/account-abstraction/AccountFactory.sol +++ b/contracts/account-abstraction/AccountFactory.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; -import "@openzeppelin/contracts/utils/Create2.sol"; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../dependencies/openzeppelin/contracts/Create2.sol"; +import "../dependencies/openzeppelin/contracts/ERC1967Proxy.sol"; import "./Account.sol"; import "./AccountProxy.sol"; diff --git a/contracts/account-abstraction/AccountProxy.sol b/contracts/account-abstraction/AccountProxy.sol index c36b89e49..cc24aa1ea 100644 --- a/contracts/account-abstraction/AccountProxy.sol +++ b/contracts/account-abstraction/AccountProxy.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../dependencies/openzeppelin/contracts/ERC1967Proxy.sol"; import "./AccountRegistry.sol"; contract AccountProxy is ERC1967Proxy { diff --git a/contracts/account-abstraction/callback/TokenCallbackHandler.sol b/contracts/account-abstraction/callback/TokenCallbackHandler.sol index e8970127f..aa6a84da6 100644 --- a/contracts/account-abstraction/callback/TokenCallbackHandler.sol +++ b/contracts/account-abstraction/callback/TokenCallbackHandler.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.17; /* solhint-disable no-empty-blocks */ -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import "../../dependencies/openzeppelin/contracts/IERC165.sol"; +import "../../dependencies/openzeppelin/contracts/IERC777Recipient.sol"; +import "../../dependencies/openzeppelin/contracts/IERC1155Receiver.sol"; +import "../../dependencies/openzeppelin/contracts/IERC721Receiver.sol"; /** * Token callback handler. diff --git a/contracts/apestaking/AutoYieldApe.sol b/contracts/apestaking/AutoYieldApe.sol index 72175ea36..c82c66f62 100644 --- a/contracts/apestaking/AutoYieldApe.sol +++ b/contracts/apestaking/AutoYieldApe.sol @@ -8,7 +8,7 @@ import "../dependencies/openzeppelin/contracts/IERC20.sol"; import "../dependencies/openzeppelin/contracts/SafeERC20.sol"; import "../dependencies/openzeppelin/contracts/SafeCast.sol"; import "../dependencies/openzeppelin/contracts/Address.sol"; -import "../dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol"; +import "../interfaces/IUniV3SwapRouter.sol"; import "../dependencies/yoga-labs/ApeCoinStaking.sol"; import "../interfaces/IAutoYieldApe.sol"; import "../interfaces/IYieldInfo.sol"; diff --git a/contracts/dependencies/openzeppelin/contracts/Create2.sol b/contracts/dependencies/openzeppelin/contracts/Create2.sol new file mode 100644 index 000000000..9e8b54f2f --- /dev/null +++ b/contracts/dependencies/openzeppelin/contracts/Create2.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (utils/Create2.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. + * `CREATE2` can be used to compute in advance the address where a smart + * contract will be deployed, which allows for interesting new mechanisms known + * as 'counterfactual interactions'. + * + * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more + * information. + */ +library Create2 { + /** + * @dev Deploys a contract using `CREATE2`. The address where the contract + * will be deployed can be known in advance via {computeAddress}. + * + * The bytecode for a contract can be obtained from Solidity with + * `type(contractName).creationCode`. + * + * Requirements: + * + * - `bytecode` must not be empty. + * - `salt` must have not been used for `bytecode` already. + * - the factory must have a balance of at least `amount`. + * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. + */ + function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { + require(address(this).balance >= amount, "Create2: insufficient balance"); + require(bytecode.length != 0, "Create2: bytecode length is zero"); + /// @solidity memory-safe-assembly + assembly { + addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) + } + require(addr != address(0), "Create2: Failed on deploy"); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the + * `bytecodeHash` or `salt` will result in a new destination address. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { + return computeAddress(salt, bytecodeHash, address(this)); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at + * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) // Get free memory pointer + + // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | + // |-------------------|---------------------------------------------------------------------------| + // | bytecodeHash | CCCCCCCCCCCCC...CC | + // | salt | BBBBBBBBBBBBB...BB | + // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | + // | 0xFF | FF | + // |-------------------|---------------------------------------------------------------------------| + // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | + // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | + + mstore(add(ptr, 0x40), bytecodeHash) + mstore(add(ptr, 0x20), salt) + mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes + let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff + mstore8(start, 0xff) + addr := keccak256(start, 85) + } + } +} diff --git a/contracts/dependencies/openzeppelin/contracts/ERC1967Proxy.sol b/contracts/dependencies/openzeppelin/contracts/ERC1967Proxy.sol new file mode 100644 index 000000000..2f65c8031 --- /dev/null +++ b/contracts/dependencies/openzeppelin/contracts/ERC1967Proxy.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) + +pragma solidity ^0.8.0; + +import "../upgradeability/Proxy.sol"; +import "../upgradeability/ERC1967Upgrade.sol"; + +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializing the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} diff --git a/contracts/dependencies/openzeppelin/contracts/IERC777Recipient.sol b/contracts/dependencies/openzeppelin/contracts/IERC777Recipient.sol new file mode 100644 index 000000000..717dd8f8c --- /dev/null +++ b/contracts/dependencies/openzeppelin/contracts/IERC777Recipient.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. + * + * Accounts can be notified of {IERC777} tokens being sent to them by having a + * contract implement this interface (contract holders can be their own + * implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Recipient { + /** + * @dev Called by an {IERC777} token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/contracts/dependencies/uniswapv3-core/NoDelegateCall.sol b/contracts/dependencies/uniswapv3-core/NoDelegateCall.sol index 4e76e157c..5411979dc 100644 --- a/contracts/dependencies/uniswapv3-core/NoDelegateCall.sol +++ b/contracts/dependencies/uniswapv3-core/NoDelegateCall.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/UniswapV3Factory.sol b/contracts/dependencies/uniswapv3-core/UniswapV3Factory.sol index 62ceab166..3553ce55e 100644 --- a/contracts/dependencies/uniswapv3-core/UniswapV3Factory.sol +++ b/contracts/dependencies/uniswapv3-core/UniswapV3Factory.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {IUniswapV3Factory} from './interfaces/IUniswapV3Factory.sol'; +import './interfaces/IUniswapV3Factory.sol'; -import {UniswapV3PoolDeployer} from './UniswapV3PoolDeployer.sol'; -import {NoDelegateCall} from './NoDelegateCall.sol'; +import './UniswapV3PoolDeployer.sol'; +import './NoDelegateCall.sol'; -import {UniswapV3Pool} from './UniswapV3Pool.sol'; +import './UniswapV3Pool.sol'; /// @title Canonical Uniswap V3 factory /// @notice Deploys Uniswap V3 pools and manages ownership and control over pool protocol fees diff --git a/contracts/dependencies/uniswapv3-core/UniswapV3Pool.sol b/contracts/dependencies/uniswapv3-core/UniswapV3Pool.sol index 42a7b67af..9e0982127 100644 --- a/contracts/dependencies/uniswapv3-core/UniswapV3Pool.sol +++ b/contracts/dependencies/uniswapv3-core/UniswapV3Pool.sol @@ -1,31 +1,35 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolActions, IUniswapV3PoolDerivedState, IUniswapV3PoolOwnerActions, IUniswapV3Pool} from './interfaces/IUniswapV3Pool.sol'; - -import {NoDelegateCall} from './NoDelegateCall.sol'; - -import {SafeCast} from './libraries/SafeCast.sol'; -import {Tick} from './libraries/Tick.sol'; -import {TickBitmap} from './libraries/TickBitmap.sol'; -import {Position} from './libraries/Position.sol'; -import {Oracle} from './libraries/Oracle.sol'; - -import {FullMath} from './libraries/FullMath.sol'; -import {FixedPoint128} from './libraries/FixedPoint128.sol'; -import {TransferHelper} from './libraries/TransferHelper.sol'; -import {TickMath} from './libraries/TickMath.sol'; -import {SqrtPriceMath} from './libraries/SqrtPriceMath.sol'; -import {SwapMath} from './libraries/SwapMath.sol'; - -import {IUniswapV3PoolDeployer} from './interfaces/IUniswapV3PoolDeployer.sol'; -import {IUniswapV3Factory} from './interfaces/IUniswapV3Factory.sol'; -import {IERC20Minimal} from './interfaces/IERC20Minimal.sol'; -import {IUniswapV3MintCallback} from './interfaces/callback/IUniswapV3MintCallback.sol'; -import {IUniswapV3SwapCallback} from './interfaces/callback/IUniswapV3SwapCallback.sol'; -import {IUniswapV3FlashCallback} from './interfaces/callback/IUniswapV3FlashCallback.sol'; +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); @@ -98,7 +102,7 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { /// 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() { - if (!slot0.unlocked) revert LOK(); + require(slot0.unlocked, 'LOK'); slot0.unlocked = false; _; slot0.unlocked = true; @@ -120,9 +124,9 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { /// @dev Common checks for valid tick inputs. function checkTicks(int24 tickLower, int24 tickUpper) private pure { - if (tickLower >= tickUpper) revert TLU(); - if (tickLower < TickMath.MIN_TICK) revert TLM(); - if (tickUpper > TickMath.MAX_TICK) revert TUM(); + 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. @@ -134,9 +138,8 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { /// @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)) - ); + (bool success, bytes memory data) = + token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } @@ -145,9 +148,8 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { /// @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)) - ); + (bool success, bytes memory data) = + token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this))); require(success && data.length >= 32); return abi.decode(data, (uint256)); } @@ -197,16 +199,16 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { Slot0 memory _slot0 = slot0; - unchecked { - 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( + 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, @@ -214,20 +216,19 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { liquidity, _slot0.observationCardinality ); - return ( - tickCumulative - tickCumulativeLower - tickCumulativeUpper, - secondsPerLiquidityCumulativeX128 - - secondsPerLiquidityOutsideLowerX128 - - secondsPerLiquidityOutsideUpperX128, - time - secondsOutsideLower - secondsOutsideUpper - ); - } else { - return ( - tickCumulativeUpper - tickCumulativeLower, - secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, - secondsOutsideUpper - secondsOutsideLower - ); - } + return ( + tickCumulative - tickCumulativeLower - tickCumulativeUpper, + secondsPerLiquidityCumulativeX128 - + secondsPerLiquidityOutsideLowerX128 - + secondsPerLiquidityOutsideUpperX128, + time - secondsOutsideLower - secondsOutsideUpper + ); + } else { + return ( + tickCumulativeUpper - tickCumulativeLower, + secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, + secondsOutsideUpper - secondsOutsideLower + ); } } @@ -258,10 +259,8 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { noDelegateCall { uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event - uint16 observationCardinalityNextNew = observations.grow( - observationCardinalityNextOld, - observationCardinalityNext - ); + uint16 observationCardinalityNextNew = + observations.grow(observationCardinalityNextOld, observationCardinalityNext); slot0.observationCardinalityNext = observationCardinalityNextNew; if (observationCardinalityNextOld != observationCardinalityNextNew) emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); @@ -270,7 +269,7 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { /// @inheritdoc IUniswapV3PoolActions /// @dev not locked because it initializes unlocked function initialize(uint160 sqrtPriceX96) external override { - if (slot0.sqrtPriceX96 != 0) revert AI(); + require(slot0.sqrtPriceX96 == 0, 'AI'); int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); @@ -359,9 +358,7 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { params.liquidityDelta ); - liquidity = params.liquidityDelta < 0 - ? liquidityBefore - uint128(-params.liquidityDelta) - : liquidityBefore + uint128(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 @@ -396,14 +393,15 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { bool flippedUpper; if (liquidityDelta != 0) { uint32 time = _blockTimestamp(); - (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( - time, - 0, - slot0.tick, - slot0.observationIndex, - liquidity, - slot0.observationCardinality - ); + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = + observations.observeSingle( + time, + 0, + slot0.tick, + slot0.observationIndex, + liquidity, + slot0.observationCardinality + ); flippedLower = ticks.update( tickLower, @@ -438,13 +436,8 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { } } - (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside( - tickLower, - tickUpper, - tick, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128 - ); + (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = + ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128); position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); @@ -469,14 +462,15 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { 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(uint256(amount)).toInt128() - }) - ); + (, int256 amount0Int, int256 amount1Int) = + _modifyPosition( + ModifyPositionParams({ + owner: recipient, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: int256(amount).toInt128() + }) + ); amount0 = uint256(amount0Int); amount1 = uint256(amount1Int); @@ -486,8 +480,8 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { if (amount0 > 0) balance0Before = balance0(); if (amount1 > 0) balance1Before = balance1(); IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); - if (amount0 > 0 && balance0Before + amount0 > balance0()) revert M0(); - if (amount1 > 0 && balance1Before + amount1 > balance1()) revert M1(); + 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); } @@ -506,15 +500,13 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested; amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested; - unchecked { - if (amount0 > 0) { - position.tokensOwed0 -= amount0; - TransferHelper.safeTransfer(token0, recipient, amount0); - } - if (amount1 > 0) { - position.tokensOwed1 -= amount1; - TransferHelper.safeTransfer(token1, recipient, amount1); - } + 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); @@ -527,28 +519,27 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { int24 tickUpper, uint128 amount ) external override lock returns (uint256 amount0, uint256 amount1) { - unchecked { - (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( + (Position.Info storage position, int256 amount0Int, int256 amount1Int) = + _modifyPosition( ModifyPositionParams({ owner: msg.sender, tickLower: tickLower, tickUpper: tickUpper, - liquidityDelta: -int256(uint256(amount)).toInt128() + 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) - ); - } + amount0 = uint256(-amount0Int); + amount1 = uint256(-amount1Int); - emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); + 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 { @@ -609,11 +600,11 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { uint160 sqrtPriceLimitX96, bytes calldata data ) external override noDelegateCall returns (int256 amount0, int256 amount1) { - if (amountSpecified == 0) revert AS(); + require(amountSpecified != 0, 'AS'); Slot0 memory slot0Start = slot0; - if (!slot0Start.unlocked) revert LOK(); + require(slot0Start.unlocked, 'LOK'); require( zeroForOne ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO @@ -623,26 +614,28 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { 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 - }); + 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 - }); + 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) { @@ -678,33 +671,23 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { ); if (exactInput) { - // safe because we test that amountSpecified > amountIn + feeAmount in SwapMath - unchecked { - state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); - } - state.amountCalculated -= step.amountOut.toInt256(); + state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); + state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); } else { - unchecked { - state.amountSpecifiedRemaining += step.amountOut.toInt256(); - } - state.amountCalculated += (step.amountIn + step.feeAmount).toInt256(); + 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) { - unchecked { - uint256 delta = step.feeAmount / cache.feeProtocol; - step.feeAmount -= delta; - state.protocolFee += uint128(delta); - } + uint256 delta = step.feeAmount / cache.feeProtocol; + step.feeAmount -= delta; + state.protocolFee += uint128(delta); } // update global fee tracker - if (state.liquidity > 0) { - unchecked { - state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); - } - } + 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) { @@ -723,28 +706,23 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { ); cache.computedLatestObservation = true; } - int128 liquidityNet = ticks.cross( - step.tickNext, - (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), - (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), - cache.secondsPerLiquidityCumulativeX128, - cache.tickCumulative, - cache.blockTimestamp - ); + 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 - unchecked { - if (zeroForOne) liquidityNet = -liquidityNet; - } + if (zeroForOne) liquidityNet = -liquidityNet; - state.liquidity = liquidityNet < 0 - ? state.liquidity - uint128(-liquidityNet) - : state.liquidity + uint128(liquidityNet); + state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); } - unchecked { - state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; - } + 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); @@ -753,14 +731,15 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { // 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 - ); + (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, @@ -779,39 +758,29 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees if (zeroForOne) { feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; - unchecked { - if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; - } + if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; } else { feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; - unchecked { - if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; - } + if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; } - unchecked { - (amount0, amount1) = zeroForOne == exactInput - ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) - : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); - } + (amount0, amount1) = zeroForOne == exactInput + ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) + : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); // do the transfers and collect payment if (zeroForOne) { - unchecked { - if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); - } + if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); uint256 balance0Before = balance0(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); - if (balance0Before + uint256(amount0) > balance0()) revert IIA(); + require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); } else { - unchecked { - if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); - } + if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); uint256 balance1Before = balance1(); IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); - if (balance1Before + uint256(amount1) > balance1()) revert IIA(); + require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); } emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); @@ -826,7 +795,7 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { bytes calldata data ) external override lock noDelegateCall { uint128 _liquidity = liquidity; - if (_liquidity <= 0) revert L(); + require(_liquidity > 0, 'L'); uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); @@ -841,42 +810,38 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { uint256 balance0After = balance0(); uint256 balance1After = balance1(); - if (balance0Before + fee0 > balance0After) revert F0(); - if (balance1Before + fee1 > balance1After) revert F1(); - - unchecked { - // sub is safe because we know balanceAfter is gt balanceBefore by at least fee - uint256 paid0 = balance0After - balance0Before; - uint256 paid1 = balance1After - balance1Before; + require(balance0Before.add(fee0) <= balance0After, 'F0'); + require(balance1Before.add(fee1) <= balance1After, 'F1'); - if (paid0 > 0) { - uint8 feeProtocol0 = slot0.feeProtocol % 16; - uint256 pFees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; - if (uint128(pFees0) > 0) protocolFees.token0 += uint128(pFees0); - feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - pFees0, FixedPoint128.Q128, _liquidity); - } - if (paid1 > 0) { - uint8 feeProtocol1 = slot0.feeProtocol >> 4; - uint256 pFees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; - if (uint128(pFees1) > 0) protocolFees.token1 += uint128(pFees1); - feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - pFees1, FixedPoint128.Q128, _liquidity); - } + // sub is safe because we know balanceAfter is gt balanceBefore by at least fee + uint256 paid0 = balance0After - balance0Before; + uint256 paid1 = balance1After - balance1Before; - emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); + 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 { - unchecked { - 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); - } + 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 @@ -888,17 +853,15 @@ contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; - unchecked { - 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); - } + 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/contracts/dependencies/uniswapv3-core/UniswapV3PoolDeployer.sol b/contracts/dependencies/uniswapv3-core/UniswapV3PoolDeployer.sol index c313dc728..02bfd5354 100644 --- a/contracts/dependencies/uniswapv3-core/UniswapV3PoolDeployer.sol +++ b/contracts/dependencies/uniswapv3-core/UniswapV3PoolDeployer.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {IUniswapV3PoolDeployer} from './interfaces/IUniswapV3PoolDeployer.sol'; +import './interfaces/IUniswapV3PoolDeployer.sol'; -import {UniswapV3Pool} from './UniswapV3Pool.sol'; +import './UniswapV3Pool.sol'; contract UniswapV3PoolDeployer is IUniswapV3PoolDeployer { struct Parameters { diff --git a/contracts/dependencies/uniswapv3-core/interfaces/IERC20Minimal.sol b/contracts/dependencies/uniswapv3-core/interfaces/IERC20Minimal.sol index 727690d64..c303265a3 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/IERC20Minimal.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/IERC20Minimal.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Factory.sol b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Factory.sol index 484bc5fb3..540cfdc68 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Factory.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Factory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Pool.sol b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Pool.sol index 1cab15754..56df0500d 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Pool.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3Pool.sol @@ -1,13 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; -import {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol'; -import {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol'; -import {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol'; -import {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol'; -import {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol'; -import {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol'; -import {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol'; +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 @@ -19,7 +18,6 @@ interface IUniswapV3Pool is IUniswapV3PoolDerivedState, IUniswapV3PoolActions, IUniswapV3PoolOwnerActions, - IUniswapV3PoolErrors, IUniswapV3PoolEvents { diff --git a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3PoolDeployer.sol b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3PoolDeployer.sol index 17925522d..72096c1ff 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3PoolDeployer.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/IUniswapV3PoolDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3FlashCallback.sol b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3FlashCallback.sol index 159509cad..18e54c4e1 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3FlashCallback.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3FlashCallback.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Callback for IUniswapV3PoolActions#flash /// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface diff --git a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3MintCallback.sol b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3MintCallback.sol index 38e2a3094..85447e84f 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3MintCallback.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3MintCallback.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Callback for IUniswapV3PoolActions#mint /// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface diff --git a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol index bebf74f6c..9f183b22a 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Callback for IUniswapV3PoolActions#swap /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolActions.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolActions.sol index 3a5aeedb2..44fb61c24 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolActions.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolActions.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Permissionless pool actions /// @notice Contains pool methods that can be called by anyone diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolDerivedState.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolDerivedState.sol index 3a9078fc6..eda3a0089 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolDerivedState.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolDerivedState.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolErrors.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolErrors.sol deleted file mode 100644 index 76eb55239..000000000 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolErrors.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -/// @title Errors emitted by a pool -/// @notice Contains all events emitted by the pool -interface IUniswapV3PoolErrors { - error LOK(); - error TLU(); - error TLM(); - error TUM(); - error AI(); - error M0(); - error M1(); - error AS(); - error IIA(); - error L(); - error F0(); - error F1(); -} diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolEvents.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolEvents.sol index ade356cb9..9d915dde9 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolEvents.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Events emitted by a pool /// @notice Contains all events emitted by the pool diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolImmutables.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolImmutables.sol index f34e078b2..c9beb151e 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolImmutables.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolImmutables.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolOwnerActions.sol index 9c99a4a95..2395ed321 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolOwnerActions.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolOwnerActions.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Permissioned pool actions /// @notice Contains pool methods that may only be called by the factory owner diff --git a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolState.sol b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolState.sol index 68567fe9a..620256c31 100644 --- a/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolState.sol +++ b/contracts/dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolState.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +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 @@ -8,13 +8,13 @@ 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 - /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// 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. - /// @return observationIndex The index of the last oracle observation that was written, - /// @return observationCardinality The current maximum number of observations stored in the pool, - /// @return observationCardinalityNext The next maximum number of observations, to be updated when the observation. - /// @return feeProtocol The protocol fee for both tokens of the pool. + /// 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 @@ -45,20 +45,19 @@ interface IUniswapV3PoolState { /// @notice The currently in range liquidity available to the pool /// @dev This value has no relationship to the total liquidity across all ticks - /// @return The liquidity at the current price of the pool 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 - /// @return liquidityNet how much liquidity changes when the pool price crosses the tick, - /// @return feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, - /// @return feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, - /// @return tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick - /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, - /// @return secondsOutside the seconds spent on the other side of the tick from the current tick, - /// @return initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// 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. @@ -81,16 +80,16 @@ interface IUniswapV3PoolState { /// @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, - /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, - /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, - /// @return tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, - /// @return tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke + /// @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, + uint128 _liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, @@ -102,9 +101,9 @@ interface IUniswapV3PoolState { /// @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, - /// @return tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, - /// @return secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, - /// @return initialized whether the observation has been initialized and the values are safe to use + /// 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 diff --git a/contracts/dependencies/uniswapv3-core/libraries/BitMath.sol b/contracts/dependencies/uniswapv3-core/libraries/BitMath.sol index 4e22e1ba3..2590d2862 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/BitMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/BitMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title BitMath /// @dev This library provides functionality for computing bit properties of an unsigned integer @@ -13,37 +13,35 @@ library BitMath { function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); - unchecked { - 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; + 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, @@ -55,44 +53,42 @@ library BitMath { function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); - unchecked { - 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; + 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/contracts/dependencies/uniswapv3-core/libraries/FixedPoint128.sol b/contracts/dependencies/uniswapv3-core/libraries/FixedPoint128.sol index 2dbdfc1e5..87a2c8aac 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/FixedPoint128.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/FixedPoint128.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title FixedPoint128 /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) diff --git a/contracts/dependencies/uniswapv3-core/libraries/FixedPoint96.sol b/contracts/dependencies/uniswapv3-core/libraries/FixedPoint96.sol index 46ef263de..08267c917 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/FixedPoint96.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/FixedPoint96.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title FixedPoint96 /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) diff --git a/contracts/dependencies/uniswapv3-core/libraries/FullMath.sol b/contracts/dependencies/uniswapv3-core/libraries/FullMath.sol index e357426a1..d41a2d77c 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/FullMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/FullMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision @@ -16,95 +16,93 @@ library FullMath { uint256 b, uint256 denominator ) internal pure returns (uint256 result) { - unchecked { - // 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)) - } + // 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; + // 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); + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// + /////////////////////////////////////////////// + // 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) - } + // 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 = (0 - denominator) & denominator; - // Divide denominator by power of two - assembly { - denominator := div(denominator, twos) - } + // 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; + // 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 + // 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; - } + // 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 @@ -117,12 +115,10 @@ library FullMath { uint256 b, uint256 denominator ) internal pure returns (uint256 result) { - unchecked { - result = mulDiv(a, b, denominator); - if (mulmod(a, b, denominator) > 0) { - require(result < type(uint256).max); - result++; - } + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max); + result++; } } } diff --git a/contracts/dependencies/uniswapv3-core/libraries/LICENSE b/contracts/dependencies/uniswapv3-core/libraries/LICENSE new file mode 100644 index 000000000..7f6aca78c --- /dev/null +++ b/contracts/dependencies/uniswapv3-core/libraries/LICENSE @@ -0,0 +1,445 @@ +This software is available under your choice of the GNU General Public +License, version 2 or later, or the Business Source License, as set +forth below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Uniswap Labs + +Licensed Work: Uniswap V3 Core + The Licensed Work is (c) 2021 Uniswap Labs + +Additional Use Grant: Any uses listed and defined at + v3-core-license-grants.uniswap.eth + +Change Date: The earlier of 2023-04-01 or a date specified at + v3-core-license-date.uniswap.eth + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/contracts/dependencies/uniswapv3-core/libraries/LICENSE_MIT b/contracts/dependencies/uniswapv3-core/libraries/LICENSE_MIT new file mode 100644 index 000000000..bf4f90a28 --- /dev/null +++ b/contracts/dependencies/uniswapv3-core/libraries/LICENSE_MIT @@ -0,0 +1,20 @@ +Copyright (c) 2021 Remco Bloemen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/contracts/dependencies/uniswapv3-core/libraries/LiquidityMath.sol b/contracts/dependencies/uniswapv3-core/libraries/LiquidityMath.sol new file mode 100644 index 000000000..e2ae591ce --- /dev/null +++ b/contracts/dependencies/uniswapv3-core/libraries/LiquidityMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +/// @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/contracts/dependencies/uniswapv3-core/libraries/LowGasSafeMath.sol b/contracts/dependencies/uniswapv3-core/libraries/LowGasSafeMath.sol new file mode 100644 index 000000000..f2b63c2af --- /dev/null +++ b/contracts/dependencies/uniswapv3-core/libraries/LowGasSafeMath.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +/// @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/contracts/dependencies/uniswapv3-core/libraries/Oracle.sol b/contracts/dependencies/uniswapv3-core/libraries/Oracle.sol index 2aacc9fdb..8eb4e0eec 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/Oracle.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/Oracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title Oracle /// @notice Provides price and liquidity data useful for a wide variety of system designs @@ -9,9 +9,6 @@ pragma solidity ^0.8.0; /// 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 { - error I(); - error OLD(); - struct Observation { // the block timestamp of the observation uint32 blockTimestamp; @@ -36,17 +33,15 @@ library Oracle { int24 tick, uint128 liquidity ) private pure returns (Observation memory) { - unchecked { - uint32 delta = blockTimestamp - last.blockTimestamp; - return - Observation({ - blockTimestamp: blockTimestamp, - tickCumulative: last.tickCumulative + int56(tick) * int56(uint56(delta)), - secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + - ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), - initialized: true - }); - } + 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 @@ -89,22 +84,20 @@ library Oracle { uint16 cardinality, uint16 cardinalityNext ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { - unchecked { - Observation memory last = self[index]; + Observation memory last = self[index]; - // early return if we've already written an observation this block - if (last.blockTimestamp == blockTimestamp) return (index, cardinality); + // 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); + // 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 @@ -117,15 +110,13 @@ library Oracle { uint16 current, uint16 next ) internal returns (uint16) { - unchecked { - if (current <= 0) revert 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; - } + 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 @@ -133,21 +124,19 @@ library Oracle { /// @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 Whether `a` is chronologically <= `b` + /// @return bool Whether `a` is chronologically <= `b` function lte( uint32 time, uint32 a, uint32 b ) private pure returns (bool) { - unchecked { - // if there hasn't been overflow, no need to adjust - if (a <= time && b <= time) return a <= b; + // 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; + uint256 aAdjusted = a > time ? a : a + 2**32; + uint256 bAdjusted = b > time ? b : b + 2**32; - return aAdjusted <= bAdjusted; - } + return aAdjusted <= bAdjusted; } /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. @@ -168,31 +157,29 @@ library Oracle { uint16 index, uint16 cardinality ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - unchecked { - uint256 l = (index + 1) % cardinality; // oldest observation - uint256 r = l + cardinality - 1; // newest observation - uint256 i; - while (true) { - i = (l + r) / 2; + 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]; + beforeOrAt = self[i % cardinality]; - // we've landed on an uninitialized tick, keep searching higher (more recently) - if (!beforeOrAt.initialized) { - l = i + 1; - continue; - } + // we've landed on an uninitialized tick, keep searching higher (more recently) + if (!beforeOrAt.initialized) { + l = i + 1; + continue; + } - atOrAfter = self[(i + 1) % cardinality]; + atOrAfter = self[(i + 1) % cardinality]; - bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); + bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); - // check if we've found the answer! - if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; + // check if we've found the answer! + if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; - if (!targetAtOrAfter) r = i - 1; - else l = i + 1; - } + if (!targetAtOrAfter) r = i - 1; + else l = i + 1; } } @@ -217,31 +204,29 @@ library Oracle { uint128 liquidity, uint16 cardinality ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { - unchecked { - // optimistically set before to the newest observation - beforeOrAt = self[index]; + // 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)); - } + // 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]; + // 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 - if (!lte(time, beforeOrAt.blockTimestamp, target)) revert OLD(); + // 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); - } + // 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. @@ -266,48 +251,38 @@ library Oracle { uint128 liquidity, uint16 cardinality ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { - unchecked { - if (secondsAgo == 0) { - Observation memory last = self[index]; - if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); - return (last.tickCumulative, last.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; + uint32 target = time - secondsAgo; - (Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations( - self, - time, - target, - tick, - index, - liquidity, - cardinality - ); + (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) / int56(uint56(observationTimeDelta))) * - int56(uint56(targetDelta)), - beforeOrAt.secondsPerLiquidityCumulativeX128 + - uint160( - (uint256( - atOrAfter.secondsPerLiquidityCumulativeX128 - - beforeOrAt.secondsPerLiquidityCumulativeX128 - ) * targetDelta) / observationTimeDelta - ) - ); - } + 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 + ) + ); } } @@ -331,22 +306,20 @@ library Oracle { uint128 liquidity, uint16 cardinality ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { - unchecked { - if (cardinality <= 0) revert I(); + 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 - ); - } + 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/contracts/dependencies/uniswapv3-core/libraries/Position.sol b/contracts/dependencies/uniswapv3-core/libraries/Position.sol index 4e1837058..379d6c636 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/Position.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/Position.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {FullMath} from './FullMath.sol'; -import {FixedPoint128} from './FixedPoint128.sol'; +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 { - error NP(); - // info stored for each user's position struct Info { // the amount of liquidity owned by this position @@ -52,26 +51,23 @@ library Position { uint128 liquidityNext; if (liquidityDelta == 0) { - if (_self.liquidity <= 0) revert NP(); // disallow pokes for 0 liquidity positions + require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions liquidityNext = _self.liquidity; } else { - liquidityNext = liquidityDelta < 0 - ? _self.liquidity - uint128(-liquidityDelta) - : _self.liquidity + uint128(liquidityDelta); + liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); } - // calculate accumulated fees. overflow in the subtraction of fee growth is expected - uint128 tokensOwed0; - uint128 tokensOwed1; - unchecked { - tokensOwed0 = uint128( + // calculate accumulated fees + uint128 tokensOwed0 = + uint128( FullMath.mulDiv( feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, _self.liquidity, FixedPoint128.Q128 ) ); - tokensOwed1 = uint128( + uint128 tokensOwed1 = + uint128( FullMath.mulDiv( feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, _self.liquidity, @@ -79,15 +75,14 @@ library Position { ) ); - // update the position - if (liquidityDelta != 0) self.liquidity = liquidityNext; - self.feeGrowthInside0LastX128 = feeGrowthInside0X128; - self.feeGrowthInside1LastX128 = feeGrowthInside1X128; - if (tokensOwed0 > 0 || tokensOwed1 > 0) { - // overflow is acceptable, user must withdraw before they hit type(uint128).max fees - self.tokensOwed0 += tokensOwed0; - self.tokensOwed1 += tokensOwed1; - } + // 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/contracts/dependencies/uniswapv3-core/libraries/SafeCast.sol b/contracts/dependencies/uniswapv3-core/libraries/SafeCast.sol index 5affc60a4..de5a97669 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/SafeCast.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/SafeCast.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title Safe casting methods /// @notice Contains methods for safely casting between types diff --git a/contracts/dependencies/uniswapv3-core/libraries/SqrtPriceMath.sol b/contracts/dependencies/uniswapv3-core/libraries/SqrtPriceMath.sol index a3369e18d..f256958f4 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/SqrtPriceMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/SqrtPriceMath.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {SafeCast} from './SafeCast.sol'; +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; -import {FullMath} from './FullMath.sol'; -import {UnsafeMath} from './UnsafeMath.sol'; -import {FixedPoint96} from './FixedPoint96.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 @@ -34,26 +36,22 @@ library SqrtPriceMath { uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; if (add) { - unchecked { - 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)); - } + 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)); } - // denominator is checked for overflow - return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96) + amount)); + + return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); } else { - unchecked { - 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(); - } + 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(); } } @@ -76,25 +74,25 @@ library SqrtPriceMath { // 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) + quotient).toUint160(); + 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) - ); + 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 - unchecked { - return uint160(sqrtPX96 - quotient); - } + return uint160(sqrtPX96 - quotient); } } @@ -158,22 +156,20 @@ library SqrtPriceMath { uint128 liquidity, bool roundUp ) internal pure returns (uint256 amount0) { - unchecked { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; - uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; - require(sqrtRatioAX96 > 0); + require(sqrtRatioAX96 > 0); - return - roundUp - ? UnsafeMath.divRoundingUp( - FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), - sqrtRatioAX96 - ) - : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; - } + return + roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), + sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; } /// @notice Gets the amount1 delta between two prices @@ -189,14 +185,12 @@ library SqrtPriceMath { uint128 liquidity, bool roundUp ) internal pure returns (uint256 amount1) { - unchecked { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - return - roundUp - ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) - : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); - } + return + roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); } /// @notice Helper that gets signed token0 delta @@ -209,12 +203,10 @@ library SqrtPriceMath { uint160 sqrtRatioBX96, int128 liquidity ) internal pure returns (int256 amount0) { - unchecked { - return - liquidity < 0 - ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } + return + liquidity < 0 + ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } /// @notice Helper that gets signed token1 delta @@ -227,11 +219,9 @@ library SqrtPriceMath { uint160 sqrtRatioBX96, int128 liquidity ) internal pure returns (int256 amount1) { - unchecked { - return - liquidity < 0 - ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() - : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); - } + return + liquidity < 0 + ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); } } diff --git a/contracts/dependencies/uniswapv3-core/libraries/SwapMath.sol b/contracts/dependencies/uniswapv3-core/libraries/SwapMath.sol index ddb8b6859..7fd35947f 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/SwapMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/SwapMath.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {FullMath} from './FullMath.sol'; -import {SqrtPriceMath} from './SqrtPriceMath.sol'; +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. @@ -34,67 +34,65 @@ library SwapMath { uint256 feeAmount ) { - unchecked { - bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; - bool exactIn = amountRemaining >= 0; + 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 - ); - } + 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; + 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); - } + // 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); - } + // 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); - } + 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/contracts/dependencies/uniswapv3-core/libraries/Tick.sol b/contracts/dependencies/uniswapv3-core/libraries/Tick.sol index 97f8c5768..2dd124415 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/Tick.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/Tick.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {SafeCast} from './SafeCast.sol'; +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; -import {TickMath} from './TickMath.sol'; +import './TickMath.sol'; +import './LiquidityMath.sol'; /// @title Tick /// @notice Contains functions for managing tick processes and relevant calculations library Tick { - error LO(); - + using LowGasSafeMath for int256; using SafeCast for int256; // info stored for each initialized individual tick @@ -41,12 +42,10 @@ library Tick { /// 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) { - unchecked { - 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; - } + 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 @@ -66,35 +65,33 @@ library Tick { uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128 ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { - unchecked { - 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; - } + 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; + } - feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; - feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; + // 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 @@ -126,11 +123,9 @@ library Tick { Tick.Info storage info = self[tick]; uint128 liquidityGrossBefore = info.liquidityGross; - uint128 liquidityGrossAfter = liquidityDelta < 0 - ? liquidityGrossBefore - uint128(-liquidityDelta) - : liquidityGrossBefore + uint128(liquidityDelta); + uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); - if (liquidityGrossAfter > maxLiquidity) revert LO(); + require(liquidityGrossAfter <= maxLiquidity, 'LO'); flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); @@ -149,7 +144,9 @@ library Tick { info.liquidityGross = liquidityGrossAfter; // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) - info.liquidityNet = upper ? info.liquidityNet - liquidityDelta : info.liquidityNet + liquidityDelta; + info.liquidityNet = upper + ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() + : int256(info.liquidityNet).add(liquidityDelta).toInt128(); } /// @notice Clears tick data @@ -177,16 +174,12 @@ library Tick { int56 tickCumulative, uint32 time ) internal returns (int128 liquidityNet) { - unchecked { - 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; - } + 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/contracts/dependencies/uniswapv3-core/libraries/TickBitmap.sol b/contracts/dependencies/uniswapv3-core/libraries/TickBitmap.sol index 1cc5bb9ce..0c45f02a1 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/TickBitmap.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/TickBitmap.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {BitMath} from './BitMath.sol'; +import './BitMath.sol'; /// @title Packed tick initialized state library /// @notice Stores a packed mapping of tick index to its initialized state @@ -12,10 +12,8 @@ library TickBitmap { /// @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) { - unchecked { - wordPos = int16(tick >> 8); - bitPos = uint8(int8(tick % 256)); - } + wordPos = int16(tick >> 8); + bitPos = uint8(tick % 256); } /// @notice Flips the initialized state for a given tick from false to true, or vice versa @@ -27,12 +25,10 @@ library TickBitmap { int24 tick, int24 tickSpacing ) internal { - unchecked { - require(tick % tickSpacing == 0); // ensure that the tick is spaced - (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); - uint256 mask = 1 << bitPos; - self[wordPos] ^= mask; - } + 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 @@ -49,36 +45,34 @@ library TickBitmap { int24 tickSpacing, bool lte ) internal view returns (int24 next, bool initialized) { - unchecked { - int24 compressed = tick / tickSpacing; - if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + 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 (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(uint24(bitPos - BitMath.mostSignificantBit(masked)))) * tickSpacing - : (compressed - int24(uint24(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 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(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing - : (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing; - } + // 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/contracts/dependencies/uniswapv3-core/libraries/TickMath.sol b/contracts/dependencies/uniswapv3-core/libraries/TickMath.sol index 3715d15dd..b948f06ff 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/TickMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/TickMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @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 @@ -22,7 +22,7 @@ library TickMath { /// 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(int256(MAX_TICK)), 'T'); + require(absTick <= uint256(MAX_TICK), 'T'); uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; diff --git a/contracts/dependencies/uniswapv3-core/libraries/TransferHelper.sol b/contracts/dependencies/uniswapv3-core/libraries/TransferHelper.sol index d419a13cd..ea8cbad8c 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/TransferHelper.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/TransferHelper.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import {IERC20Minimal} from '../interfaces/IERC20Minimal.sol'; +import '../interfaces/IERC20Minimal.sol'; /// @title TransferHelper /// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false library TransferHelper { - error TF(); - /// @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 @@ -18,9 +16,8 @@ library TransferHelper { address to, uint256 value ) internal { - (bool success, bytes memory data) = token.call( - abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value) - ); - if (!(success && (data.length == 0 || abi.decode(data, (bool))))) revert TF(); + (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/contracts/dependencies/uniswapv3-core/libraries/UnsafeMath.sol b/contracts/dependencies/uniswapv3-core/libraries/UnsafeMath.sol index 2d8e199df..67f1c59c4 100644 --- a/contracts/dependencies/uniswapv3-core/libraries/UnsafeMath.sol +++ b/contracts/dependencies/uniswapv3-core/libraries/UnsafeMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @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 diff --git a/contracts/dependencies/uniswapv3-periphery/NonfungiblePositionManager.sol b/contracts/dependencies/uniswapv3-periphery/NonfungiblePositionManager.sol index cbb4cb930..cd17e19cb 100644 --- a/contracts/dependencies/uniswapv3-periphery/NonfungiblePositionManager.sol +++ b/contracts/dependencies/uniswapv3-periphery/NonfungiblePositionManager.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; pragma abicoder v2; -import {IUniswapV3Pool} from '../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; +import '../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; import '../uniswapv3-core/libraries/FixedPoint128.sol'; import '../uniswapv3-core/libraries/FullMath.sol'; @@ -10,13 +10,13 @@ import './interfaces/INonfungiblePositionManager.sol'; import './interfaces/INonfungibleTokenPositionDescriptor.sol'; import './libraries/PositionKey.sol'; import './libraries/PoolAddress.sol'; -import {LiquidityManagement} from './base/LiquidityManagement.sol'; +import './base/LiquidityManagement.sol'; import './base/PeripheryImmutableState.sol'; import './base/Multicall.sol'; import './base/ERC721Permit.sol'; -import {PeripheryValidation} from './base/PeripheryValidation.sol'; +import './base/PeripheryValidation.sol'; import './base/SelfPermit.sol'; -import {PoolInitializer} from './base/PoolInitializer.sol'; +import './base/PoolInitializer.sol'; /// @title NFT positions /// @notice Wraps Uniswap V3 positions in the ERC721 non-fungible token interface @@ -159,10 +159,11 @@ contract NonfungiblePositionManager is (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); // idempotent set - uint80 poolId = cachePoolKey( - address(pool), - PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}) - ); + uint80 poolId = + cachePoolKey( + address(pool), + PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}) + ); _positions[tokenId] = Position({ nonce: 0, @@ -191,7 +192,7 @@ contract NonfungiblePositionManager is } // save bytecode by removing implementation of unused method - function baseURI() public pure returns (string memory) {} + function baseURI() public pure override returns (string memory) {} /// @inheritdoc INonfungiblePositionManager function increaseLiquidity(IncreaseLiquidityParams calldata params) @@ -327,9 +328,8 @@ contract NonfungiblePositionManager is // trigger an update of the position fees owed and fee growth snapshots if it has any liquidity if (position.liquidity > 0) { pool.burn(position.tickLower, position.tickUpper, 0); - (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions( - PositionKey.compute(address(this), position.tickLower, position.tickUpper) - ); + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = + pool.positions(PositionKey.compute(address(this), position.tickLower, position.tickUpper)); tokensOwed0 += uint128( FullMath.mulDiv( @@ -351,10 +351,11 @@ contract NonfungiblePositionManager is } // compute the arguments to give to the pool#collect method - (uint128 amount0Collect, uint128 amount1Collect) = ( - params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max, - params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max - ); + (uint128 amount0Collect, uint128 amount1Collect) = + ( + params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max, + params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max + ); // the actual amounts collected are returned (amount0, amount1) = pool.collect( diff --git a/contracts/dependencies/uniswapv3-periphery/NonfungibleTokenPositionDescriptor.sol b/contracts/dependencies/uniswapv3-periphery/NonfungibleTokenPositionDescriptor.sol new file mode 100644 index 000000000..43b67989a --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/NonfungibleTokenPositionDescriptor.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; + +import './libraries/ChainId.sol'; +import './interfaces/INonfungiblePositionManager.sol'; +import './interfaces/INonfungibleTokenPositionDescriptor.sol'; +import './interfaces/IERC20Metadata.sol'; +import './libraries/PoolAddress.sol'; +import './libraries/NFTDescriptor.sol'; +import './libraries/TokenRatioSortOrder.sol'; + +/// @title Describes NFT token positions +/// @notice Produces a string containing the data URI for a JSON metadata string +contract NonfungibleTokenPositionDescriptor is INonfungibleTokenPositionDescriptor { + address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; + address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + + address public immutable WETH9; + /// @dev A null-terminated string + bytes32 public immutable nativeCurrencyLabelBytes; + + constructor(address _WETH9, bytes32 _nativeCurrencyLabelBytes) { + WETH9 = _WETH9; + nativeCurrencyLabelBytes = _nativeCurrencyLabelBytes; + } + + /// @notice Returns the native currency label as a string + function nativeCurrencyLabel() public view returns (string memory) { + uint256 len = 0; + while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { + len++; + } + bytes memory b = new bytes(len); + for (uint256 i = 0; i < len; i++) { + b[i] = nativeCurrencyLabelBytes[i]; + } + return string(b); + } + + /// @inheritdoc INonfungibleTokenPositionDescriptor + function tokenURI(INonfungiblePositionManager positionManager, uint256 tokenId) + external + view + override + returns (string memory) + { + (, , address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, , , , , ) = + positionManager.positions(tokenId); + + IUniswapV3Pool pool = + IUniswapV3Pool( + PoolAddress.computeAddress( + positionManager.factory(), + PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee}) + ) + ); + + bool _flipRatio = flipRatio(token0, token1, ChainId.get()); + address quoteTokenAddress = !_flipRatio ? token1 : token0; + address baseTokenAddress = !_flipRatio ? token0 : token1; + (, int24 tick, , , , , ) = pool.slot0(); + + return + NFTDescriptor.constructTokenURI( + NFTDescriptor.ConstructTokenURIParams({ + tokenId: tokenId, + quoteTokenAddress: quoteTokenAddress, + baseTokenAddress: baseTokenAddress, + quoteTokenSymbol: quoteTokenAddress == WETH9 + ? nativeCurrencyLabel() + : IERC20Metadata(quoteTokenAddress).symbol(), + baseTokenSymbol: baseTokenAddress == WETH9 + ? nativeCurrencyLabel() + : IERC20Metadata(baseTokenAddress).symbol(), + quoteTokenDecimals: IERC20Metadata(quoteTokenAddress).decimals(), + baseTokenDecimals: IERC20Metadata(baseTokenAddress).decimals(), + flipRatio: _flipRatio, + tickLower: tickLower, + tickUpper: tickUpper, + tickCurrent: tick, + tickSpacing: pool.tickSpacing(), + fee: fee, + poolAddress: address(pool) + }) + ); + } + + function flipRatio( + address token0, + address token1, + uint256 chainId + ) public view returns (bool) { + return tokenRatioPriority(token0, chainId) > tokenRatioPriority(token1, chainId); + } + + function tokenRatioPriority(address token, uint256 chainId) public view returns (int256) { + if (token == WETH9) { + return TokenRatioSortOrder.DENOMINATOR; + } + if (chainId == 1) { + if (token == USDC) { + return TokenRatioSortOrder.NUMERATOR_MOST; + } else if (token == USDT) { + return TokenRatioSortOrder.NUMERATOR_MORE; + } else if (token == DAI) { + return TokenRatioSortOrder.NUMERATOR; + } else if (token == TBTC) { + return TokenRatioSortOrder.DENOMINATOR_MORE; + } else if (token == WBTC) { + return TokenRatioSortOrder.DENOMINATOR_MOST; + } else { + return 0; + } + } + return 0; + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/SwapRouter.sol b/contracts/dependencies/uniswapv3-periphery/SwapRouter.sol index c91566c72..a1a0eb82c 100644 --- a/contracts/dependencies/uniswapv3-periphery/SwapRouter.sol +++ b/contracts/dependencies/uniswapv3-periphery/SwapRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; pragma abicoder v2; import '../uniswapv3-core/libraries/SafeCast.sol'; @@ -14,7 +14,7 @@ import './base/Multicall.sol'; import './base/SelfPermit.sol'; import './libraries/Path.sol'; import './libraries/PoolAddress.sol'; -import {CallbackValidation} from './libraries/CallbackValidation.sol'; +import './libraries/CallbackValidation.sol'; import './interfaces/external/IWETH9.sol'; /// @title Uniswap V3 Swap Router @@ -64,9 +64,10 @@ contract SwapRouter is (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool(); CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); - (bool isExactInput, uint256 amountToPay) = amount0Delta > 0 - ? (tokenIn < tokenOut, uint256(amount0Delta)) - : (tokenOut < tokenIn, uint256(amount1Delta)); + (bool isExactInput, uint256 amountToPay) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta)); if (isExactInput) { pay(tokenIn, data.payer, msg.sender, amountToPay); } else { @@ -96,15 +97,16 @@ contract SwapRouter is bool zeroForOne = tokenIn < tokenOut; - (int256 amount0, int256 amount1) = getPool(tokenIn, tokenOut, fee).swap( - recipient, - zeroForOne, - amountIn.toInt256(), - sqrtPriceLimitX96 == 0 - ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) - : sqrtPriceLimitX96, - abi.encode(data) - ); + (int256 amount0, int256 amount1) = + getPool(tokenIn, tokenOut, fee).swap( + recipient, + zeroForOne, + amountIn.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encode(data) + ); return uint256(-(zeroForOne ? amount1 : amount0)); } @@ -177,15 +179,16 @@ contract SwapRouter is bool zeroForOne = tokenIn < tokenOut; - (int256 amount0Delta, int256 amount1Delta) = getPool(tokenIn, tokenOut, fee).swap( - recipient, - zeroForOne, - -amountOut.toInt256(), - sqrtPriceLimitX96 == 0 - ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) - : sqrtPriceLimitX96, - abi.encode(data) - ); + (int256 amount0Delta, int256 amount1Delta) = + getPool(tokenIn, tokenOut, fee).swap( + recipient, + zeroForOne, + -amountOut.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encode(data) + ); uint256 amountOutReceived; (amountIn, amountOutReceived) = zeroForOne diff --git a/contracts/dependencies/uniswapv3-periphery/base/BlockTimestamp.sol b/contracts/dependencies/uniswapv3-periphery/base/BlockTimestamp.sol index d7c6cf2f0..ac05cac0a 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/BlockTimestamp.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/BlockTimestamp.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; /// @title Function for getting block timestamp /// @dev Base contract that is overridden for tests diff --git a/contracts/dependencies/uniswapv3-periphery/base/ERC721Permit.sol b/contracts/dependencies/uniswapv3-periphery/base/ERC721Permit.sol index 975df306d..efe0f5445 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/ERC721Permit.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/ERC721Permit.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import '../../openzeppelin/contracts/ERC721Enumerable.sol'; -import '../../openzeppelin/contracts/Address.sol'; +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import '@openzeppelin/contracts/utils/Address.sol'; import '../libraries/ChainId.sol'; import '../interfaces/external/IERC1271.sol'; @@ -11,7 +11,7 @@ import './BlockTimestamp.sol'; /// @title ERC721 with permit /// @notice Nonfungible tokens that support an approve via signature, i.e. permit -abstract contract ERC721Permit is BlockTimestamp, ERC721Enumerable, IERC721Permit { +abstract contract ERC721Permit is BlockTimestamp, ERC721, IERC721Permit { /// @dev Gets the current nonce for a token ID and then increments it, returning the original value function _getAndIncrementNonce(uint256 tokenId) internal virtual returns (uint256); @@ -62,13 +62,14 @@ abstract contract ERC721Permit is BlockTimestamp, ERC721Enumerable, IERC721Permi ) external payable override { require(_blockTimestamp() <= deadline, 'Permit expired'); - bytes32 digest = keccak256( - abi.encodePacked( - '\x19\x01', - DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, _getAndIncrementNonce(tokenId), deadline)) - ) - ); + bytes32 digest = + keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, _getAndIncrementNonce(tokenId), deadline)) + ) + ); address owner = ownerOf(tokenId); require(spender != owner, 'ERC721Permit: approval to current owner'); diff --git a/contracts/dependencies/uniswapv3-periphery/base/LiquidityManagement.sol b/contracts/dependencies/uniswapv3-periphery/base/LiquidityManagement.sol index a129ee315..d0a2388d8 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/LiquidityManagement.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/LiquidityManagement.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; pragma abicoder v2; import '../../uniswapv3-core/interfaces/IUniswapV3Factory.sol'; -import {IUniswapV3Pool} from '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; import '../../uniswapv3-core/interfaces/callback/IUniswapV3MintCallback.sol'; import '../../uniswapv3-core/libraries/TickMath.sol'; @@ -58,11 +57,8 @@ abstract contract LiquidityManagement is IUniswapV3MintCallback, PeripheryImmuta IUniswapV3Pool pool ) { - PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({ - token0: params.token0, - token1: params.token1, - fee: params.fee - }); + PoolAddress.PoolKey memory poolKey = + PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}); pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); diff --git a/contracts/dependencies/uniswapv3-periphery/base/Multicall.sol b/contracts/dependencies/uniswapv3-periphery/base/Multicall.sol index 4d855c25d..5a6384d30 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/Multicall.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/Multicall.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; pragma abicoder v2; import '../interfaces/IMulticall.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/base/PeripheryImmutableState.sol b/contracts/dependencies/uniswapv3-periphery/base/PeripheryImmutableState.sol index 283b8139e..75267a4dc 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/PeripheryImmutableState.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/PeripheryImmutableState.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; import '../interfaces/IPeripheryImmutableState.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/base/PeripheryPayments.sol b/contracts/dependencies/uniswapv3-periphery/base/PeripheryPayments.sol index 3fa0decc8..68ee75f5d 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/PeripheryPayments.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/PeripheryPayments.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; -import '../../openzeppelin/contracts/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '../interfaces/IPeripheryPayments.sol'; import '../interfaces/external/IWETH9.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/base/PeripheryPaymentsWithFee.sol b/contracts/dependencies/uniswapv3-periphery/base/PeripheryPaymentsWithFee.sol index ed7494328..4261ac5a3 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/PeripheryPaymentsWithFee.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/PeripheryPaymentsWithFee.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; -import '../../openzeppelin/contracts/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '../../uniswapv3-core/libraries/LowGasSafeMath.sol'; import './PeripheryPayments.sol'; import '../interfaces/IPeripheryPaymentsWithFee.sol'; @@ -10,6 +11,8 @@ import '../interfaces/external/IWETH9.sol'; import '../libraries/TransferHelper.sol'; abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPaymentsWithFee { + using LowGasSafeMath for uint256; + /// @inheritdoc IPeripheryPaymentsWithFee function unwrapWETH9WithFee( uint256 amountMinimum, @@ -24,7 +27,7 @@ abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPayme if (balanceWETH9 > 0) { IWETH9(WETH9).withdraw(balanceWETH9); - uint256 feeAmount = (balanceWETH9 * feeBips) / 10_000; + uint256 feeAmount = balanceWETH9.mul(feeBips) / 10_000; if (feeAmount > 0) TransferHelper.safeTransferETH(feeRecipient, feeAmount); TransferHelper.safeTransferETH(recipient, balanceWETH9 - feeAmount); } @@ -44,7 +47,7 @@ abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPayme require(balanceToken >= amountMinimum, 'Insufficient token'); if (balanceToken > 0) { - uint256 feeAmount = (balanceToken * feeBips) / 10_000; + uint256 feeAmount = balanceToken.mul(feeBips) / 10_000; if (feeAmount > 0) TransferHelper.safeTransfer(token, feeRecipient, feeAmount); TransferHelper.safeTransfer(token, recipient, balanceToken - feeAmount); } diff --git a/contracts/dependencies/uniswapv3-periphery/base/PeripheryValidation.sol b/contracts/dependencies/uniswapv3-periphery/base/PeripheryValidation.sol index d567515e9..40fadd7ed 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/PeripheryValidation.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/PeripheryValidation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; import './BlockTimestamp.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/base/PoolInitializer.sol b/contracts/dependencies/uniswapv3-periphery/base/PoolInitializer.sol index 3c82b1d18..8b3f34af8 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/PoolInitializer.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/PoolInitializer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; import '../../uniswapv3-core/interfaces/IUniswapV3Factory.sol'; import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/base/SelfPermit.sol b/contracts/dependencies/uniswapv3-periphery/base/SelfPermit.sol index 3d4002c5e..59c8d460e 100644 --- a/contracts/dependencies/uniswapv3-periphery/base/SelfPermit.sol +++ b/contracts/dependencies/uniswapv3-periphery/base/SelfPermit.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; -import '../../openzeppelin/contracts/IERC20.sol'; -import '../../openzeppelin/contracts/draft-IERC20Permit.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@openzeppelin/contracts/drafts/IERC20Permit.sol'; import '../interfaces/ISelfPermit.sol'; import '../interfaces/external/IERC20PermitAllowed.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IERC20Metadata.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IERC20Metadata.sol index 0521267cf..d7bd3e9b3 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IERC20Metadata.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IERC20Metadata.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.0; -import '../../openzeppelin/contracts/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; /// @title IERC20Metadata /// @title Interface for ERC20 Metadata diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IERC721Permit.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IERC721Permit.sol index ab21c9460..744cd3a11 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IERC721Permit.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IERC721Permit.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; -import '../../openzeppelin/contracts/IERC721.sol'; +import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; /// @title ERC721 with permit /// @notice Extension to ERC721 that includes a permit function for signature based approvals diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IMulticall.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IMulticall.sol index 8b59815c3..1ea39c692 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IMulticall.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IMulticall.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; /// @title Multicall interface diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol index cbb9c7c7a..023b26613 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; -import '../../openzeppelin/contracts/IERC721Metadata.sol'; -import '../../openzeppelin/contracts/IERC721Enumerable.sol'; +import '@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol'; +import '@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol'; import './IPoolInitializer.sol'; import './IERC721Permit.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungibleTokenPositionDescriptor.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungibleTokenPositionDescriptor.sol index 8a285e668..b3f27b86b 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungibleTokenPositionDescriptor.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/INonfungibleTokenPositionDescriptor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; import './INonfungiblePositionManager.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryImmutableState.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryImmutableState.sol index 80d208824..b3378052d 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryImmutableState.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryImmutableState.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Immutable state /// @notice Functions that return immutable state of the router diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPayments.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPayments.sol index e6e6030b3..ac74f8c9e 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPayments.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPayments.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; /// @title Periphery Payments /// @notice Functions to ease deposits and withdrawals of ETH diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPaymentsWithFee.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPaymentsWithFee.sol index b85478dde..907e44675 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPaymentsWithFee.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IPeripheryPaymentsWithFee.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; import './IPeripheryPayments.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IPoolInitializer.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IPoolInitializer.sol index 84715722e..d2949b3d6 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IPoolInitializer.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IPoolInitializer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; /// @title Creates and initializes V3 Pools diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoter.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoter.sol index 684203f2a..3410e0cd0 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoter.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; /// @title Quoter Interface diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoterV2.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoterV2.sol index 58b04eefe..3c2961b2c 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoterV2.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IQuoterV2.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; /// @title QuoterV2 Interface diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/ISelfPermit.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/ISelfPermit.sol index 65b9c76d3..0ab8a25af 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/ISelfPermit.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/ISelfPermit.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; /// @title Self Permit /// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol index 6fb25c1b5..0f6a488a4 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; import '../../uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/ITickLens.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/ITickLens.sol index 195d3cd0e..89bac2d41 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/ITickLens.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/ITickLens.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; /// @title Tick Lens diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/IV3Migrator.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/IV3Migrator.sol index 0fe0bf440..6eb6e42fa 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/IV3Migrator.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/IV3Migrator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.7.5; pragma abicoder v2; import './IMulticall.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC1271.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC1271.sol index 3263bb97b..824b03ea4 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC1271.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC1271.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Interface for verifying contract-based account signatures /// @notice Interface that verifies provided signature for the data diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC20PermitAllowed.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC20PermitAllowed.sol index 1b7159391..a817e1cff 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC20PermitAllowed.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IERC20PermitAllowed.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @title Interface for permit /// @notice Interface used by DAI/CHAI for permit diff --git a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IWETH9.sol b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IWETH9.sol index c6a3b1ee3..58ab43eb3 100644 --- a/contracts/dependencies/uniswapv3-periphery/interfaces/external/IWETH9.sol +++ b/contracts/dependencies/uniswapv3-periphery/interfaces/external/IWETH9.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; -import '../../../openzeppelin/contracts/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; /// @title Interface for WETH9 interface IWETH9 is IERC20 { diff --git a/contracts/dependencies/uniswapv3-periphery/lens/Quoter.sol b/contracts/dependencies/uniswapv3-periphery/lens/Quoter.sol new file mode 100644 index 000000000..54678b6ae --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/lens/Quoter.sol @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../../uniswapv3-core/libraries/SafeCast.sol'; +import '../../uniswapv3-core/libraries/TickMath.sol'; +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; +import '../../uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol'; + +import '../interfaces/IQuoter.sol'; +import '../base/PeripheryImmutableState.sol'; +import '../libraries/Path.sol'; +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; + +/// @title Provides quotes for swaps +/// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap +/// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute +/// the swap and check the amounts in the callback. +contract Quoter is IQuoter, IUniswapV3SwapCallback, PeripheryImmutableState { + using Path for bytes; + using SafeCast for uint256; + + /// @dev Transient storage variable used to check a safety condition in exact output swaps. + uint256 private amountOutCached; + + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} + + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) private view returns (IUniswapV3Pool) { + return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); + } + + /// @inheritdoc IUniswapV3SwapCallback + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes memory path + ) external view override { + require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); + + (bool isExactInput, uint256 amountToPay, uint256 amountReceived) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta), uint256(-amount1Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta), uint256(-amount0Delta)); + if (isExactInput) { + assembly { + let ptr := mload(0x40) + mstore(ptr, amountReceived) + revert(ptr, 32) + } + } else { + // if the cache has been populated, ensure that the full output amount has been received + if (amountOutCached != 0) require(amountReceived == amountOutCached); + assembly { + let ptr := mload(0x40) + mstore(ptr, amountToPay) + revert(ptr, 32) + } + } + } + + /// @dev Parses a revert reason that should contain the numeric quote + function parseRevertReason(bytes memory reason) private pure returns (uint256) { + if (reason.length != 32) { + if (reason.length < 68) revert('Unexpected error'); + assembly { + reason := add(reason, 0x04) + } + revert(abi.decode(reason, (string))); + } + return abi.decode(reason, (uint256)); + } + + /// @inheritdoc IQuoter + function quoteExactInputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountIn, + uint160 sqrtPriceLimitX96 + ) public override returns (uint256 amountOut) { + bool zeroForOne = tokenIn < tokenOut; + + try + getPool(tokenIn, tokenOut, fee).swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + amountIn.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encodePacked(tokenIn, fee, tokenOut) + ) + {} catch (bytes memory reason) { + return parseRevertReason(reason); + } + } + + /// @inheritdoc IQuoter + function quoteExactInput(bytes memory path, uint256 amountIn) external override returns (uint256 amountOut) { + while (true) { + bool hasMultiplePools = path.hasMultiplePools(); + + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + + // the outputs of prior swaps become the inputs to subsequent ones + amountIn = quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, 0); + + // decide whether to continue or terminate + if (hasMultiplePools) { + path = path.skipToken(); + } else { + return amountIn; + } + } + } + + /// @inheritdoc IQuoter + function quoteExactOutputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountOut, + uint160 sqrtPriceLimitX96 + ) public override returns (uint256 amountIn) { + bool zeroForOne = tokenIn < tokenOut; + + // if no price limit has been specified, cache the output amount for comparison in the swap callback + if (sqrtPriceLimitX96 == 0) amountOutCached = amountOut; + try + getPool(tokenIn, tokenOut, fee).swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + -amountOut.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encodePacked(tokenOut, fee, tokenIn) + ) + {} catch (bytes memory reason) { + if (sqrtPriceLimitX96 == 0) delete amountOutCached; // clear cache + return parseRevertReason(reason); + } + } + + /// @inheritdoc IQuoter + function quoteExactOutput(bytes memory path, uint256 amountOut) external override returns (uint256 amountIn) { + while (true) { + bool hasMultiplePools = path.hasMultiplePools(); + + (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool(); + + // the inputs of prior swaps become the outputs of subsequent ones + amountOut = quoteExactOutputSingle(tokenIn, tokenOut, fee, amountOut, 0); + + // decide whether to continue or terminate + if (hasMultiplePools) { + path = path.skipToken(); + } else { + return amountOut; + } + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/lens/QuoterV2.sol b/contracts/dependencies/uniswapv3-periphery/lens/QuoterV2.sol new file mode 100644 index 000000000..66ab4ec34 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/lens/QuoterV2.sol @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../../uniswapv3-core/libraries/SafeCast.sol'; +import '../../uniswapv3-core/libraries/TickMath.sol'; +import '../../uniswapv3-core/libraries/TickBitmap.sol'; +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; +import '../../uniswapv3-core/interfaces/callback/IUniswapV3SwapCallback.sol'; + +import '../interfaces/IQuoterV2.sol'; +import '../base/PeripheryImmutableState.sol'; +import '../libraries/Path.sol'; +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; +import '../libraries/PoolTicksCounter.sol'; + +/// @title Provides quotes for swaps +/// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap +/// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute +/// the swap and check the amounts in the callback. +contract QuoterV2 is IQuoterV2, IUniswapV3SwapCallback, PeripheryImmutableState { + using Path for bytes; + using SafeCast for uint256; + using PoolTicksCounter for IUniswapV3Pool; + + /// @dev Transient storage variable used to check a safety condition in exact output swaps. + uint256 private amountOutCached; + + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} + + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) private view returns (IUniswapV3Pool) { + return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); + } + + /// @inheritdoc IUniswapV3SwapCallback + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes memory path + ) external view override { + require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); + + (bool isExactInput, uint256 amountToPay, uint256 amountReceived) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta), uint256(-amount1Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta), uint256(-amount0Delta)); + + IUniswapV3Pool pool = getPool(tokenIn, tokenOut, fee); + (uint160 sqrtPriceX96After, int24 tickAfter, , , , , ) = pool.slot0(); + + if (isExactInput) { + assembly { + let ptr := mload(0x40) + mstore(ptr, amountReceived) + mstore(add(ptr, 0x20), sqrtPriceX96After) + mstore(add(ptr, 0x40), tickAfter) + revert(ptr, 96) + } + } else { + // if the cache has been populated, ensure that the full output amount has been received + if (amountOutCached != 0) require(amountReceived == amountOutCached); + assembly { + let ptr := mload(0x40) + mstore(ptr, amountToPay) + mstore(add(ptr, 0x20), sqrtPriceX96After) + mstore(add(ptr, 0x40), tickAfter) + revert(ptr, 96) + } + } + } + + /// @dev Parses a revert reason that should contain the numeric quote + function parseRevertReason(bytes memory reason) + private + pure + returns ( + uint256 amount, + uint160 sqrtPriceX96After, + int24 tickAfter + ) + { + if (reason.length != 96) { + if (reason.length < 68) revert('Unexpected error'); + assembly { + reason := add(reason, 0x04) + } + revert(abi.decode(reason, (string))); + } + return abi.decode(reason, (uint256, uint160, int24)); + } + + function handleRevert( + bytes memory reason, + IUniswapV3Pool pool, + uint256 gasEstimate + ) + private + view + returns ( + uint256 amount, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 + ) + { + int24 tickBefore; + int24 tickAfter; + (, tickBefore, , , , , ) = pool.slot0(); + (amount, sqrtPriceX96After, tickAfter) = parseRevertReason(reason); + + initializedTicksCrossed = pool.countInitializedTicksCrossed(tickBefore, tickAfter); + + return (amount, sqrtPriceX96After, initializedTicksCrossed, gasEstimate); + } + + function quoteExactInputSingle(QuoteExactInputSingleParams memory params) + public + override + returns ( + uint256 amountOut, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ) + { + bool zeroForOne = params.tokenIn < params.tokenOut; + IUniswapV3Pool pool = getPool(params.tokenIn, params.tokenOut, params.fee); + + uint256 gasBefore = gasleft(); + try + pool.swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + params.amountIn.toInt256(), + params.sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : params.sqrtPriceLimitX96, + abi.encodePacked(params.tokenIn, params.fee, params.tokenOut) + ) + {} catch (bytes memory reason) { + gasEstimate = gasBefore - gasleft(); + return handleRevert(reason, pool, gasEstimate); + } + } + + function quoteExactInput(bytes memory path, uint256 amountIn) + public + override + returns ( + uint256 amountOut, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ) + { + sqrtPriceX96AfterList = new uint160[](path.numPools()); + initializedTicksCrossedList = new uint32[](path.numPools()); + + uint256 i = 0; + while (true) { + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + + // the outputs of prior swaps become the inputs to subsequent ones + (uint256 _amountOut, uint160 _sqrtPriceX96After, uint32 _initializedTicksCrossed, uint256 _gasEstimate) = + quoteExactInputSingle( + QuoteExactInputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: fee, + amountIn: amountIn, + sqrtPriceLimitX96: 0 + }) + ); + + sqrtPriceX96AfterList[i] = _sqrtPriceX96After; + initializedTicksCrossedList[i] = _initializedTicksCrossed; + amountIn = _amountOut; + gasEstimate += _gasEstimate; + i++; + + // decide whether to continue or terminate + if (path.hasMultiplePools()) { + path = path.skipToken(); + } else { + return (amountIn, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate); + } + } + } + + function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) + public + override + returns ( + uint256 amountIn, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ) + { + bool zeroForOne = params.tokenIn < params.tokenOut; + IUniswapV3Pool pool = getPool(params.tokenIn, params.tokenOut, params.fee); + + // if no price limit has been specified, cache the output amount for comparison in the swap callback + if (params.sqrtPriceLimitX96 == 0) amountOutCached = params.amount; + uint256 gasBefore = gasleft(); + try + pool.swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + -params.amount.toInt256(), + params.sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : params.sqrtPriceLimitX96, + abi.encodePacked(params.tokenOut, params.fee, params.tokenIn) + ) + {} catch (bytes memory reason) { + gasEstimate = gasBefore - gasleft(); + if (params.sqrtPriceLimitX96 == 0) delete amountOutCached; // clear cache + return handleRevert(reason, pool, gasEstimate); + } + } + + function quoteExactOutput(bytes memory path, uint256 amountOut) + public + override + returns ( + uint256 amountIn, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ) + { + sqrtPriceX96AfterList = new uint160[](path.numPools()); + initializedTicksCrossedList = new uint32[](path.numPools()); + + uint256 i = 0; + while (true) { + (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool(); + + // the inputs of prior swaps become the outputs of subsequent ones + (uint256 _amountIn, uint160 _sqrtPriceX96After, uint32 _initializedTicksCrossed, uint256 _gasEstimate) = + quoteExactOutputSingle( + QuoteExactOutputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amount: amountOut, + fee: fee, + sqrtPriceLimitX96: 0 + }) + ); + + sqrtPriceX96AfterList[i] = _sqrtPriceX96After; + initializedTicksCrossedList[i] = _initializedTicksCrossed; + amountOut = _amountIn; + gasEstimate += _gasEstimate; + i++; + + // decide whether to continue or terminate + if (path.hasMultiplePools()) { + path = path.skipToken(); + } else { + return (amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate); + } + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/lens/README.md b/contracts/dependencies/uniswapv3-periphery/lens/README.md new file mode 100644 index 000000000..8359711fd --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/lens/README.md @@ -0,0 +1,4 @@ +# lens + +These contracts are not designed to be called on-chain. They simplify +fetching on-chain data from off-chain. diff --git a/contracts/dependencies/uniswapv3-periphery/lens/TickLens.sol b/contracts/dependencies/uniswapv3-periphery/lens/TickLens.sol new file mode 100644 index 000000000..e832509ad --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/lens/TickLens.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; +pragma abicoder v2; + +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; + +import '../interfaces/ITickLens.sol'; + +/// @title Tick Lens contract +contract TickLens is ITickLens { + /// @inheritdoc ITickLens + function getPopulatedTicksInWord(address pool, int16 tickBitmapIndex) + public + view + override + returns (PopulatedTick[] memory populatedTicks) + { + // fetch bitmap + uint256 bitmap = IUniswapV3Pool(pool).tickBitmap(tickBitmapIndex); + + // calculate the number of populated ticks + uint256 numberOfPopulatedTicks; + for (uint256 i = 0; i < 256; i++) { + if (bitmap & (1 << i) > 0) numberOfPopulatedTicks++; + } + + // fetch populated tick data + int24 tickSpacing = IUniswapV3Pool(pool).tickSpacing(); + populatedTicks = new PopulatedTick[](numberOfPopulatedTicks); + for (uint256 i = 0; i < 256; i++) { + if (bitmap & (1 << i) > 0) { + int24 populatedTick = ((int24(tickBitmapIndex) << 8) + int24(i)) * tickSpacing; + (uint128 liquidityGross, int128 liquidityNet, , , , , , ) = IUniswapV3Pool(pool).ticks(populatedTick); + populatedTicks[--numberOfPopulatedTicks] = PopulatedTick({ + tick: populatedTick, + liquidityNet: liquidityNet, + liquidityGross: liquidityGross + }); + } + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/lens/UniswapInterfaceMulticall.sol b/contracts/dependencies/uniswapv3-periphery/lens/UniswapInterfaceMulticall.sol new file mode 100644 index 000000000..54c62c9a1 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/lens/UniswapInterfaceMulticall.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; +pragma abicoder v2; + +/// @notice A fork of Multicall2 specifically tailored for the Uniswap Interface +contract UniswapInterfaceMulticall { + struct Call { + address target; + uint256 gasLimit; + bytes callData; + } + + struct Result { + bool success; + uint256 gasUsed; + bytes returnData; + } + + function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { + timestamp = block.timestamp; + } + + function getEthBalance(address addr) public view returns (uint256 balance) { + balance = addr.balance; + } + + function multicall(Call[] memory calls) public returns (uint256 blockNumber, Result[] memory returnData) { + blockNumber = block.number; + returnData = new Result[](calls.length); + for (uint256 i = 0; i < calls.length; i++) { + (address target, uint256 gasLimit, bytes memory callData) = + (calls[i].target, calls[i].gasLimit, calls[i].callData); + uint256 gasLeftBefore = gasleft(); + (bool success, bytes memory ret) = target.call{gas: gasLimit}(callData); + uint256 gasUsed = gasLeftBefore - gasleft(); + returnData[i] = Result(success, gasUsed, ret); + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/BytesLib.sol b/contracts/dependencies/uniswapv3-periphery/libraries/BytesLib.sol index a2c35cb5a..294ec36ab 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/BytesLib.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/BytesLib.sol @@ -6,7 +6,7 @@ * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; library BytesLib { function slice( @@ -15,66 +15,68 @@ library BytesLib { uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, 'slice_overflow'); + require(_start + _length >= _start, 'slice_overflow'); require(_bytes.length >= _start + _length, 'slice_outOfBounds'); bytes memory tempBytes; assembly { switch iszero(_length) - case 0 { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // The first word of the slice result is potentially a partial - // word read from the original array. To read it, we calculate - // the length of that partial word and start copying that many - // bytes into the array. The first word we copy will start with - // data we don't care about, but the last `lengthmod` bytes will - // land at the beginning of the contents of the new array. When - // we're done copying, we overwrite the full first word with - // the actual length of the slice. - let lengthmod := and(_length, 31) - - // The multiplication in the next line is necessary - // because when slicing multiples of 32 bytes (lengthmod == 0) - // the following copy loop was copying the origin's length - // and then ending prematurely not copying everything it should. - let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) - let end := add(mc, _length) - - for { - // The multiplication in the next line has the same exact purpose - // as the one above. - let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - mstore(mc, mload(cc)) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) } - - mstore(tempBytes, _length) - - //update free-memory pointer - //allocating the array padded to 32 bytes like the compiler does now - mstore(0x40, and(add(mc, 31), not(31))) - } - //if we want a zero-length slice let's just return a zero-length array - default { - tempBytes := mload(0x40) - //zero out the 32 bytes slice we are about to return - //we need to do it because Solidity does not garbage collect - mstore(tempBytes, 0) - - mstore(0x40, add(tempBytes, 0x20)) - } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_start + 20 >= _start, 'toAddress_overflow'); require(_bytes.length >= _start + 20, 'toAddress_outOfBounds'); address tempAddress; diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/CallbackValidation.sol b/contracts/dependencies/uniswapv3-periphery/libraries/CallbackValidation.sol index 7eb19288b..a6f2da251 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/CallbackValidation.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/CallbackValidation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity =0.7.6; import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; import './PoolAddress.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/ChainId.sol b/contracts/dependencies/uniswapv3-periphery/libraries/ChainId.sol index 8c180cfa9..b6da118e3 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/ChainId.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/ChainId.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; /// @title Function for getting the current chain ID library ChainId { /// @dev Gets the current chain ID /// @return chainId The current chain ID - function get() internal view returns (uint256 chainId) { + function get() internal pure returns (uint256 chainId) { assembly { chainId := chainid() } diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/HexStrings.sol b/contracts/dependencies/uniswapv3-periphery/libraries/HexStrings.sol new file mode 100644 index 000000000..8f8228809 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/HexStrings.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; + +library HexStrings { + bytes16 internal constant ALPHABET = '0123456789abcdef'; + + /// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + /// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55 + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = '0'; + buffer[1] = 'x'; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = ALPHABET[value & 0xf]; + value >>= 4; + } + require(value == 0, 'Strings: hex length insufficient'); + return string(buffer); + } + + function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length); + for (uint256 i = buffer.length; i > 0; i--) { + buffer[i - 1] = ALPHABET[value & 0xf]; + value >>= 4; + } + return string(buffer); + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/LiquidityAmounts.sol b/contracts/dependencies/uniswapv3-periphery/libraries/LiquidityAmounts.sol index 521a1b099..738709ffb 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/LiquidityAmounts.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/LiquidityAmounts.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; import '../../uniswapv3-core/libraries/FullMath.sol'; import '../../uniswapv3-core/libraries/FixedPoint96.sol'; @@ -27,9 +27,7 @@ library LiquidityAmounts { ) internal pure returns (uint128 liquidity) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96); - unchecked { - return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96)); - } + return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96)); } /// @notice Computes the amount of liquidity received for a given amount of token1 and price range @@ -44,9 +42,7 @@ library LiquidityAmounts { uint256 amount1 ) internal pure returns (uint128 liquidity) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - unchecked { - return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96)); - } + return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96)); } /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current @@ -88,16 +84,14 @@ library LiquidityAmounts { uint160 sqrtRatioBX96, uint128 liquidity ) internal pure returns (uint256 amount0) { - unchecked { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - return - FullMath.mulDiv( - uint256(liquidity) << FixedPoint96.RESOLUTION, - sqrtRatioBX96 - sqrtRatioAX96, - sqrtRatioBX96 - ) / sqrtRatioAX96; - } + return + FullMath.mulDiv( + uint256(liquidity) << FixedPoint96.RESOLUTION, + sqrtRatioBX96 - sqrtRatioAX96, + sqrtRatioBX96 + ) / sqrtRatioAX96; } /// @notice Computes the amount of token1 for a given amount of liquidity and a price range @@ -112,9 +106,7 @@ library LiquidityAmounts { ) internal pure returns (uint256 amount1) { if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); - unchecked { - return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); - } + return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); } /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/NFTDescriptor.sol b/contracts/dependencies/uniswapv3-periphery/libraries/NFTDescriptor.sol new file mode 100644 index 000000000..5ebdb9d9c --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/NFTDescriptor.sol @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; +pragma abicoder v2; + +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; +import '../../uniswapv3-core/libraries/FullMath.sol'; +import '../../uniswapv3-core/libraries/TickMath.sol'; +import '../../uniswapv3-core/libraries/BitMath.sol'; +import '../../uniswapv3-core/libraries/FullMath.sol'; +import '@openzeppelin/contracts/utils/Strings.sol'; +import '@openzeppelin/contracts/math/SafeMath.sol'; +import '@openzeppelin/contracts/math/SignedSafeMath.sol'; +import 'base64-sol/base64.sol'; +import './HexStrings.sol'; +import './NFTSVG.sol'; + +library NFTDescriptor { + using TickMath for int24; + using Strings for uint256; + using SafeMath for uint256; + using SafeMath for uint160; + using SafeMath for uint8; + using SignedSafeMath for int256; + using HexStrings for uint256; + + uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226; + + struct ConstructTokenURIParams { + uint256 tokenId; + address quoteTokenAddress; + address baseTokenAddress; + string quoteTokenSymbol; + string baseTokenSymbol; + uint8 quoteTokenDecimals; + uint8 baseTokenDecimals; + bool flipRatio; + int24 tickLower; + int24 tickUpper; + int24 tickCurrent; + int24 tickSpacing; + uint24 fee; + address poolAddress; + } + + function constructTokenURI(ConstructTokenURIParams memory params) public pure returns (string memory) { + string memory name = generateName(params, feeToPercentString(params.fee)); + string memory descriptionPartOne = + generateDescriptionPartOne( + escapeQuotes(params.quoteTokenSymbol), + escapeQuotes(params.baseTokenSymbol), + addressToString(params.poolAddress) + ); + string memory descriptionPartTwo = + generateDescriptionPartTwo( + params.tokenId.toString(), + escapeQuotes(params.baseTokenSymbol), + addressToString(params.quoteTokenAddress), + addressToString(params.baseTokenAddress), + feeToPercentString(params.fee) + ); + string memory image = Base64.encode(bytes(generateSVGImage(params))); + + return + string( + abi.encodePacked( + 'data:application/json;base64,', + Base64.encode( + bytes( + abi.encodePacked( + '{"name":"', + name, + '", "description":"', + descriptionPartOne, + descriptionPartTwo, + '", "image": "', + 'data:image/svg+xml;base64,', + image, + '"}' + ) + ) + ) + ) + ); + } + + function escapeQuotes(string memory symbol) internal pure returns (string memory) { + bytes memory symbolBytes = bytes(symbol); + uint8 quotesCount = 0; + for (uint8 i = 0; i < symbolBytes.length; i++) { + if (symbolBytes[i] == '"') { + quotesCount++; + } + } + if (quotesCount > 0) { + bytes memory escapedBytes = new bytes(symbolBytes.length + (quotesCount)); + uint256 index; + for (uint8 i = 0; i < symbolBytes.length; i++) { + if (symbolBytes[i] == '"') { + escapedBytes[index++] = '\\'; + } + escapedBytes[index++] = symbolBytes[i]; + } + return string(escapedBytes); + } + return symbol; + } + + function generateDescriptionPartOne( + string memory quoteTokenSymbol, + string memory baseTokenSymbol, + string memory poolAddress + ) private pure returns (string memory) { + return + string( + abi.encodePacked( + 'This NFT represents a liquidity position in a Uniswap V3 ', + quoteTokenSymbol, + '-', + baseTokenSymbol, + ' pool. ', + 'The owner of this NFT can modify or redeem the position.\\n', + '\\nPool Address: ', + poolAddress, + '\\n', + quoteTokenSymbol + ) + ); + } + + function generateDescriptionPartTwo( + string memory tokenId, + string memory baseTokenSymbol, + string memory quoteTokenAddress, + string memory baseTokenAddress, + string memory feeTier + ) private pure returns (string memory) { + return + string( + abi.encodePacked( + ' Address: ', + quoteTokenAddress, + '\\n', + baseTokenSymbol, + ' Address: ', + baseTokenAddress, + '\\nFee Tier: ', + feeTier, + '\\nToken ID: ', + tokenId, + '\\n\\n', + unicode'⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure token addresses match the expected tokens, as token symbols may be imitated.' + ) + ); + } + + function generateName(ConstructTokenURIParams memory params, string memory feeTier) + private + pure + returns (string memory) + { + return + string( + abi.encodePacked( + 'Uniswap - ', + feeTier, + ' - ', + escapeQuotes(params.quoteTokenSymbol), + '/', + escapeQuotes(params.baseTokenSymbol), + ' - ', + tickToDecimalString( + !params.flipRatio ? params.tickLower : params.tickUpper, + params.tickSpacing, + params.baseTokenDecimals, + params.quoteTokenDecimals, + params.flipRatio + ), + '<>', + tickToDecimalString( + !params.flipRatio ? params.tickUpper : params.tickLower, + params.tickSpacing, + params.baseTokenDecimals, + params.quoteTokenDecimals, + params.flipRatio + ) + ) + ); + } + + struct DecimalStringParams { + // significant figures of decimal + uint256 sigfigs; + // length of decimal string + uint8 bufferLength; + // ending index for significant figures (funtion works backwards when copying sigfigs) + uint8 sigfigIndex; + // index of decimal place (0 if no decimal) + uint8 decimalIndex; + // start index for trailing/leading 0's for very small/large numbers + uint8 zerosStartIndex; + // end index for trailing/leading 0's for very small/large numbers + uint8 zerosEndIndex; + // true if decimal number is less than one + bool isLessThanOne; + // true if string should include "%" + bool isPercent; + } + + function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory) { + bytes memory buffer = new bytes(params.bufferLength); + if (params.isPercent) { + buffer[buffer.length - 1] = '%'; + } + if (params.isLessThanOne) { + buffer[0] = '0'; + buffer[1] = '.'; + } + + // add leading/trailing 0's + for (uint256 zerosCursor = params.zerosStartIndex; zerosCursor < params.zerosEndIndex.add(1); zerosCursor++) { + buffer[zerosCursor] = bytes1(uint8(48)); + } + // add sigfigs + while (params.sigfigs > 0) { + if (params.decimalIndex > 0 && params.sigfigIndex == params.decimalIndex) { + buffer[params.sigfigIndex--] = '.'; + } + buffer[params.sigfigIndex--] = bytes1(uint8(uint256(48).add(params.sigfigs % 10))); + params.sigfigs /= 10; + } + return string(buffer); + } + + function tickToDecimalString( + int24 tick, + int24 tickSpacing, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals, + bool flipRatio + ) internal pure returns (string memory) { + if (tick == (TickMath.MIN_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? 'MIN' : 'MAX'; + } else if (tick == (TickMath.MAX_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? 'MAX' : 'MIN'; + } else { + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + if (flipRatio) { + sqrtRatioX96 = uint160(uint256(1 << 192).div(sqrtRatioX96)); + } + return fixedPointToDecimalString(sqrtRatioX96, baseTokenDecimals, quoteTokenDecimals); + } + } + + function sigfigsRounded(uint256 value, uint8 digits) private pure returns (uint256, bool) { + bool extraDigit; + if (digits > 5) { + value = value.div((10**(digits - 5))); + } + bool roundUp = value % 10 > 4; + value = value.div(10); + if (roundUp) { + value = value + 1; + } + // 99999 -> 100000 gives an extra sigfig + if (value == 100000) { + value /= 10; + extraDigit = true; + } + return (value, extraDigit); + } + + function adjustForDecimalPrecision( + uint160 sqrtRatioX96, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals + ) private pure returns (uint256 adjustedSqrtRatioX96) { + uint256 difference = abs(int256(baseTokenDecimals).sub(int256(quoteTokenDecimals))); + if (difference > 0 && difference <= 18) { + if (baseTokenDecimals > quoteTokenDecimals) { + adjustedSqrtRatioX96 = sqrtRatioX96.mul(10**(difference.div(2))); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, sqrt10X128, 1 << 128); + } + } else { + adjustedSqrtRatioX96 = sqrtRatioX96.div(10**(difference.div(2))); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, 1 << 128, sqrt10X128); + } + } + } else { + adjustedSqrtRatioX96 = uint256(sqrtRatioX96); + } + } + + function abs(int256 x) private pure returns (uint256) { + return uint256(x >= 0 ? x : -x); + } + + // @notice Returns string that includes first 5 significant figures of a decimal number + // @param sqrtRatioX96 a sqrt price + function fixedPointToDecimalString( + uint160 sqrtRatioX96, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals + ) internal pure returns (string memory) { + uint256 adjustedSqrtRatioX96 = adjustForDecimalPrecision(sqrtRatioX96, baseTokenDecimals, quoteTokenDecimals); + uint256 value = FullMath.mulDiv(adjustedSqrtRatioX96, adjustedSqrtRatioX96, 1 << 64); + + bool priceBelow1 = adjustedSqrtRatioX96 < 2**96; + if (priceBelow1) { + // 10 ** 43 is precision needed to retreive 5 sigfigs of smallest possible price + 1 for rounding + value = FullMath.mulDiv(value, 10**44, 1 << 128); + } else { + // leave precision for 4 decimal places + 1 place for rounding + value = FullMath.mulDiv(value, 10**5, 1 << 128); + } + + // get digit count + uint256 temp = value; + uint8 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + // don't count extra digit kept for rounding + digits = digits - 1; + + // address rounding + (uint256 sigfigs, bool extraDigit) = sigfigsRounded(value, digits); + if (extraDigit) { + digits++; + } + + DecimalStringParams memory params; + if (priceBelow1) { + // 7 bytes ( "0." and 5 sigfigs) + leading 0's bytes + params.bufferLength = uint8(uint8(7).add(uint8(43).sub(digits))); + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(uint256(43).sub(digits).add(1)); + params.sigfigIndex = uint8(params.bufferLength.sub(1)); + } else if (digits >= 9) { + // no decimal in price string + params.bufferLength = uint8(digits.sub(4)); + params.zerosStartIndex = 5; + params.zerosEndIndex = uint8(params.bufferLength.sub(1)); + params.sigfigIndex = 4; + } else { + // 5 sigfigs surround decimal + params.bufferLength = 6; + params.sigfigIndex = 5; + params.decimalIndex = uint8(digits.sub(5).add(1)); + } + params.sigfigs = sigfigs; + params.isLessThanOne = priceBelow1; + params.isPercent = false; + + return generateDecimalString(params); + } + + // @notice Returns string as decimal percentage of fee amount. + // @param fee fee amount + function feeToPercentString(uint24 fee) internal pure returns (string memory) { + if (fee == 0) { + return '0%'; + } + uint24 temp = fee; + uint256 digits; + uint8 numSigfigs; + while (temp != 0) { + if (numSigfigs > 0) { + // count all digits preceding least significant figure + numSigfigs++; + } else if (temp % 10 != 0) { + numSigfigs++; + } + digits++; + temp /= 10; + } + + DecimalStringParams memory params; + uint256 nZeros; + if (digits >= 5) { + // if decimal > 1 (5th digit is the ones place) + uint256 decimalPlace = digits.sub(numSigfigs) >= 4 ? 0 : 1; + nZeros = digits.sub(5) < (numSigfigs.sub(1)) ? 0 : digits.sub(5).sub(numSigfigs.sub(1)); + params.zerosStartIndex = numSigfigs; + params.zerosEndIndex = uint8(params.zerosStartIndex.add(nZeros).sub(1)); + params.sigfigIndex = uint8(params.zerosStartIndex.sub(1).add(decimalPlace)); + params.bufferLength = uint8(nZeros.add(numSigfigs.add(1)).add(decimalPlace)); + } else { + // else if decimal < 1 + nZeros = uint256(5).sub(digits); + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(nZeros.add(params.zerosStartIndex).sub(1)); + params.bufferLength = uint8(nZeros.add(numSigfigs.add(2))); + params.sigfigIndex = uint8((params.bufferLength).sub(2)); + params.isLessThanOne = true; + } + params.sigfigs = uint256(fee).div(10**(digits.sub(numSigfigs))); + params.isPercent = true; + params.decimalIndex = digits > 4 ? uint8(digits.sub(4)) : 0; + + return generateDecimalString(params); + } + + function addressToString(address addr) internal pure returns (string memory) { + return (uint256(addr)).toHexString(20); + } + + function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { + NFTSVG.SVGParams memory svgParams = + NFTSVG.SVGParams({ + quoteToken: addressToString(params.quoteTokenAddress), + baseToken: addressToString(params.baseTokenAddress), + poolAddress: params.poolAddress, + quoteTokenSymbol: params.quoteTokenSymbol, + baseTokenSymbol: params.baseTokenSymbol, + feeTier: feeToPercentString(params.fee), + tickLower: params.tickLower, + tickUpper: params.tickUpper, + tickSpacing: params.tickSpacing, + overRange: overRange(params.tickLower, params.tickUpper, params.tickCurrent), + tokenId: params.tokenId, + color0: tokenToColorHex(uint256(params.quoteTokenAddress), 136), + color1: tokenToColorHex(uint256(params.baseTokenAddress), 136), + color2: tokenToColorHex(uint256(params.quoteTokenAddress), 0), + color3: tokenToColorHex(uint256(params.baseTokenAddress), 0), + x1: scale(getCircleCoord(uint256(params.quoteTokenAddress), 16, params.tokenId), 0, 255, 16, 274), + y1: scale(getCircleCoord(uint256(params.baseTokenAddress), 16, params.tokenId), 0, 255, 100, 484), + x2: scale(getCircleCoord(uint256(params.quoteTokenAddress), 32, params.tokenId), 0, 255, 16, 274), + y2: scale(getCircleCoord(uint256(params.baseTokenAddress), 32, params.tokenId), 0, 255, 100, 484), + x3: scale(getCircleCoord(uint256(params.quoteTokenAddress), 48, params.tokenId), 0, 255, 16, 274), + y3: scale(getCircleCoord(uint256(params.baseTokenAddress), 48, params.tokenId), 0, 255, 100, 484) + }); + + return NFTSVG.generateSVG(svgParams); + } + + function overRange( + int24 tickLower, + int24 tickUpper, + int24 tickCurrent + ) private pure returns (int8) { + if (tickCurrent < tickLower) { + return -1; + } else if (tickCurrent > tickUpper) { + return 1; + } else { + return 0; + } + } + + function scale( + uint256 n, + uint256 inMn, + uint256 inMx, + uint256 outMn, + uint256 outMx + ) private pure returns (string memory) { + return (n.sub(inMn).mul(outMx.sub(outMn)).div(inMx.sub(inMn)).add(outMn)).toString(); + } + + function tokenToColorHex(uint256 token, uint256 offset) internal pure returns (string memory str) { + return string((token >> offset).toHexStringNoPrefix(3)); + } + + function getCircleCoord( + uint256 tokenAddress, + uint256 offset, + uint256 tokenId + ) internal pure returns (uint256) { + return (sliceTokenHex(tokenAddress, offset) * tokenId) % 255; + } + + function sliceTokenHex(uint256 token, uint256 offset) internal pure returns (uint256) { + return uint256(uint8(token >> offset)); + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/NFTSVG.sol b/contracts/dependencies/uniswapv3-periphery/libraries/NFTSVG.sol new file mode 100644 index 000000000..213daa86a --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/NFTSVG.sol @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; + +import '@openzeppelin/contracts/utils/Strings.sol'; +import '../../uniswapv3-core/libraries/BitMath.sol'; +import 'base64-sol/base64.sol'; + +/// @title NFTSVG +/// @notice Provides a function for generating an SVG associated with a Uniswap NFT +library NFTSVG { + using Strings for uint256; + + string constant curve1 = 'M1 1C41 41 105 105 145 145'; + string constant curve2 = 'M1 1C33 49 97 113 145 145'; + string constant curve3 = 'M1 1C33 57 89 113 145 145'; + string constant curve4 = 'M1 1C25 65 81 121 145 145'; + string constant curve5 = 'M1 1C17 73 73 129 145 145'; + string constant curve6 = 'M1 1C9 81 65 137 145 145'; + string constant curve7 = 'M1 1C1 89 57.5 145 145 145'; + string constant curve8 = 'M1 1C1 97 49 145 145 145'; + + struct SVGParams { + string quoteToken; + string baseToken; + address poolAddress; + string quoteTokenSymbol; + string baseTokenSymbol; + string feeTier; + int24 tickLower; + int24 tickUpper; + int24 tickSpacing; + int8 overRange; + uint256 tokenId; + string color0; + string color1; + string color2; + string color3; + string x1; + string y1; + string x2; + string y2; + string x3; + string y3; + } + + function generateSVG(SVGParams memory params) internal pure returns (string memory svg) { + /* + address: "0xe8ab59d3bcde16a29912de83a90eb39628cfc163", + msg: "Forged in SVG for Uniswap in 2021 by 0xe8ab59d3bcde16a29912de83a90eb39628cfc163", + sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b", + version: "2" + */ + return + string( + abi.encodePacked( + generateSVGDefs(params), + generateSVGBorderText( + params.quoteToken, + params.baseToken, + params.quoteTokenSymbol, + params.baseTokenSymbol + ), + generateSVGCardMantle(params.quoteTokenSymbol, params.baseTokenSymbol, params.feeTier), + generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), + generateSVGPositionDataAndLocationCurve( + params.tokenId.toString(), + params.tickLower, + params.tickUpper + ), + generateSVGRareSparkle(params.tokenId, params.poolAddress), + '' + ) + ); + } + + function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + '", + '', + '" + ) + ) + ), + '"/>" + ) + ) + ), + '"/>" + ) + ) + ), + '" />', + '" + ) + ) + ), + '" /> ', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ' ', + '', + '', + '' + ) + ); + } + + function generateSVGBorderText( + string memory quoteToken, + string memory baseToken, + string memory quoteTokenSymbol, + string memory baseTokenSymbol + ) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + '', + '', + baseToken, + unicode' • ', + baseTokenSymbol, + ' ', + ' ', + baseToken, + unicode' • ', + baseTokenSymbol, + ' ', + '', + quoteToken, + unicode' • ', + quoteTokenSymbol, + ' ', + quoteToken, + unicode' • ', + quoteTokenSymbol, + ' ' + ) + ); + } + + function generateSVGCardMantle( + string memory quoteTokenSymbol, + string memory baseTokenSymbol, + string memory feeTier + ) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + ' ', + quoteTokenSymbol, + '/', + baseTokenSymbol, + '', + feeTier, + '', + '' + ) + ); + } + + function generageSvgCurve( + int24 tickLower, + int24 tickUpper, + int24 tickSpacing, + int8 overRange + ) private pure returns (string memory svg) { + string memory fade = overRange == 1 ? '#fade-up' : overRange == -1 ? '#fade-down' : '#none'; + string memory curve = getCurve(tickLower, tickUpper, tickSpacing); + svg = string( + abi.encodePacked( + '' + '' + '', + '', + '', + '', + generateSVGCurveCircle(overRange) + ) + ); + } + + function getCurve( + int24 tickLower, + int24 tickUpper, + int24 tickSpacing + ) internal pure returns (string memory curve) { + int24 tickRange = (tickUpper - tickLower) / tickSpacing; + if (tickRange <= 4) { + curve = curve1; + } else if (tickRange <= 8) { + curve = curve2; + } else if (tickRange <= 16) { + curve = curve3; + } else if (tickRange <= 32) { + curve = curve4; + } else if (tickRange <= 64) { + curve = curve5; + } else if (tickRange <= 128) { + curve = curve6; + } else if (tickRange <= 256) { + curve = curve7; + } else { + curve = curve8; + } + } + + function generateSVGCurveCircle(int8 overRange) internal pure returns (string memory svg) { + string memory curvex1 = '73'; + string memory curvey1 = '190'; + string memory curvex2 = '217'; + string memory curvey2 = '334'; + if (overRange == 1 || overRange == -1) { + svg = string( + abi.encodePacked( + '' + ) + ); + } else { + svg = string( + abi.encodePacked( + '', + '' + ) + ); + } + } + + function generateSVGPositionDataAndLocationCurve( + string memory tokenId, + int24 tickLower, + int24 tickUpper + ) private pure returns (string memory svg) { + string memory tickLowerStr = tickToString(tickLower); + string memory tickUpperStr = tickToString(tickUpper); + uint256 str1length = bytes(tokenId).length + 4; + uint256 str2length = bytes(tickLowerStr).length + 10; + uint256 str3length = bytes(tickUpperStr).length + 10; + (string memory xCoord, string memory yCoord) = rangeLocation(tickLower, tickUpper); + svg = string( + abi.encodePacked( + ' ', + '', + 'ID: ', + tokenId, + '', + ' ', + '', + 'Min Tick: ', + tickLowerStr, + '', + ' ', + '', + 'Max Tick: ', + tickUpperStr, + '' + '', + '', + '', + '' + ) + ); + } + + function tickToString(int24 tick) private pure returns (string memory) { + string memory sign = ''; + if (tick < 0) { + tick = tick * -1; + sign = '-'; + } + return string(abi.encodePacked(sign, uint256(tick).toString())); + } + + function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory) { + int24 midPoint = (tickLower + tickUpper) / 2; + if (midPoint < -125_000) { + return ('8', '7'); + } else if (midPoint < -75_000) { + return ('8', '10.5'); + } else if (midPoint < -25_000) { + return ('8', '14.25'); + } else if (midPoint < -5_000) { + return ('10', '18'); + } else if (midPoint < 0) { + return ('11', '21'); + } else if (midPoint < 5_000) { + return ('13', '23'); + } else if (midPoint < 25_000) { + return ('15', '25'); + } else if (midPoint < 75_000) { + return ('18', '26'); + } else if (midPoint < 125_000) { + return ('21', '27'); + } else { + return ('24', '27'); + } + } + + function generateSVGRareSparkle(uint256 tokenId, address poolAddress) private pure returns (string memory svg) { + if (isRare(tokenId, poolAddress)) { + svg = string( + abi.encodePacked( + '', + '', + '' + ) + ); + } else { + svg = ''; + } + } + + function isRare(uint256 tokenId, address poolAddress) internal pure returns (bool) { + bytes32 h = keccak256(abi.encodePacked(tokenId, poolAddress)); + return uint256(h) < type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2); + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/OracleLibrary.sol b/contracts/dependencies/uniswapv3-periphery/libraries/OracleLibrary.sol new file mode 100644 index 000000000..d6226d2dc --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/OracleLibrary.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; + +import '../../uniswapv3-core/libraries/FullMath.sol'; +import '../../uniswapv3-core/libraries/TickMath.sol'; +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; + +/// @title Oracle library +/// @notice Provides functions to integrate with V3 pool oracle +library OracleLibrary { + /// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool + /// @param pool Address of the pool that we want to observe + /// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means + /// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp + /// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp + function consult(address pool, uint32 secondsAgo) + internal + view + returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity) + { + require(secondsAgo != 0, 'BP'); + + uint32[] memory secondsAgos = new uint32[](2); + secondsAgos[0] = secondsAgo; + secondsAgos[1] = 0; + + (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = + IUniswapV3Pool(pool).observe(secondsAgos); + + int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; + uint160 secondsPerLiquidityCumulativesDelta = + secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]; + + arithmeticMeanTick = int24(tickCumulativesDelta / secondsAgo); + // Always round to negative infinity + if (tickCumulativesDelta < 0 && (tickCumulativesDelta % secondsAgo != 0)) arithmeticMeanTick--; + + // We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128 + uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max; + harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32)); + } + + /// @notice Given a tick and a token amount, calculates the amount of token received in exchange + /// @param tick Tick value used to calculate the quote + /// @param baseAmount Amount of token to be converted + /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination + /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination + /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken + function getQuoteAtTick( + int24 tick, + uint128 baseAmount, + address baseToken, + address quoteToken + ) internal pure returns (uint256 quoteAmount) { + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + + // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + quoteAmount = baseToken < quoteToken + ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) + : FullMath.mulDiv(1 << 192, baseAmount, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); + quoteAmount = baseToken < quoteToken + ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) + : FullMath.mulDiv(1 << 128, baseAmount, ratioX128); + } + } + + /// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation + /// @param pool Address of Uniswap V3 pool that we want to observe + /// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool + function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) { + (, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); + require(observationCardinality > 0, 'NI'); + + (uint32 observationTimestamp, , , bool initialized) = + IUniswapV3Pool(pool).observations((observationIndex + 1) % observationCardinality); + + // The next index might not be initialized if the cardinality is in the process of increasing + // In this case the oldest observation is always in index 0 + if (!initialized) { + (observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0); + } + + secondsAgo = uint32(block.timestamp) - observationTimestamp; + } + + /// @notice Given a pool, it returns the tick value as of the start of the current block + /// @param pool Address of Uniswap V3 pool + /// @return The tick that the pool was in at the start of the current block + function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) { + (, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); + + // 2 observations are needed to reliably calculate the block starting tick + require(observationCardinality > 1, 'NEO'); + + // If the latest observation occurred in the past, then no tick-changing trades have happened in this block + // therefore the tick in `slot0` is the same as at the beginning of the current block. + // We don't need to check if this observation is initialized - it is guaranteed to be. + (uint32 observationTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, ) = + IUniswapV3Pool(pool).observations(observationIndex); + if (observationTimestamp != uint32(block.timestamp)) { + return (tick, IUniswapV3Pool(pool).liquidity()); + } + + uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality; + ( + uint32 prevObservationTimestamp, + int56 prevTickCumulative, + uint160 prevSecondsPerLiquidityCumulativeX128, + bool prevInitialized + ) = IUniswapV3Pool(pool).observations(prevIndex); + + require(prevInitialized, 'ONI'); + + uint32 delta = observationTimestamp - prevObservationTimestamp; + tick = int24((tickCumulative - prevTickCumulative) / delta); + uint128 liquidity = + uint128( + (uint192(delta) * type(uint160).max) / + (uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32) + ); + return (tick, liquidity); + } + + /// @notice Information for calculating a weighted arithmetic mean tick + struct WeightedTickData { + int24 tick; + uint128 weight; + } + + /// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick + /// @param weightedTickData An array of ticks and weights + /// @return weightedArithmeticMeanTick The weighted arithmetic mean tick + /// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool tokens. If they do not, + /// extreme care must be taken to ensure that ticks are comparable (including decimal differences). + /// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price. + function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData) + internal + pure + returns (int24 weightedArithmeticMeanTick) + { + // Accumulates the sum of products between each tick and its weight + int256 numerator; + + // Accumulates the sum of the weights + uint256 denominator; + + // Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic + for (uint256 i; i < weightedTickData.length; i++) { + numerator += weightedTickData[i].tick * int256(weightedTickData[i].weight); + denominator += weightedTickData[i].weight; + } + + weightedArithmeticMeanTick = int24(numerator / int256(denominator)); + // Always round to negative infinity + if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--; + } + + /// @notice Returns the "synthetic" tick which represents the price of the first entry in `tokens` in terms of the last + /// @dev Useful for calculating relative prices along routes. + /// @dev There must be one tick for each pairwise set of tokens. + /// @param tokens The token contract addresses + /// @param ticks The ticks, representing the price of each token pair in `tokens` + /// @return syntheticTick The synthetic tick, representing the relative price of the outermost tokens in `tokens` + function getChainedPrice(address[] memory tokens, int24[] memory ticks) + internal + pure + returns (int256 syntheticTick) + { + require(tokens.length - 1 == ticks.length, 'DL'); + for (uint256 i = 1; i <= ticks.length; i++) { + // check the tokens for address sort order, then accumulate the + // ticks into the running synthetic tick, ensuring that intermediate tokens "cancel out" + tokens[i - 1] < tokens[i] ? syntheticTick += ticks[i - 1] : syntheticTick -= ticks[i - 1]; + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/Path.sol b/contracts/dependencies/uniswapv3-periphery/libraries/Path.sol index e92feadd8..c40a875dc 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/Path.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/Path.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; import './BytesLib.sol'; diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/PoolAddress.sol b/contracts/dependencies/uniswapv3-periphery/libraries/PoolAddress.sol index b4696a399..da1fc7216 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/PoolAddress.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/PoolAddress.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -import {UniswapV3Pool} from "../../uniswapv3-core/UniswapV3Pool.sol"; - +pragma solidity ^0.7.6; /// @title Provides functions for deriving a pool address from the factory, tokens, and the fee library PoolAddress { + bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; + /// @notice The identifying key of the pool struct PoolKey { address token0; @@ -34,15 +33,13 @@ library PoolAddress { function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) { require(key.token0 < key.token1); pool = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - hex'ff', - factory, - keccak256(abi.encode(key.token0, key.token1, key.fee)), - keccak256(type(UniswapV3Pool).creationCode) - ) + uint256( + keccak256( + abi.encodePacked( + hex'ff', + factory, + keccak256(abi.encode(key.token0, key.token1, key.fee)), + POOL_INIT_CODE_HASH ) ) ) diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/PoolTicksCounter.sol b/contracts/dependencies/uniswapv3-periphery/libraries/PoolTicksCounter.sol new file mode 100644 index 000000000..e963ed888 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/PoolTicksCounter.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; + +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; + +library PoolTicksCounter { + /// @dev This function counts the number of initialized ticks that would incur a gas cost between tickBefore and tickAfter. + /// When tickBefore and/or tickAfter themselves are initialized, the logic over whether we should count them depends on the + /// direction of the swap. If we are swapping upwards (tickAfter > tickBefore) we don't want to count tickBefore but we do + /// want to count tickAfter. The opposite is true if we are swapping downwards. + function countInitializedTicksCrossed( + IUniswapV3Pool self, + int24 tickBefore, + int24 tickAfter + ) internal view returns (uint32 initializedTicksCrossed) { + int16 wordPosLower; + int16 wordPosHigher; + uint8 bitPosLower; + uint8 bitPosHigher; + bool tickBeforeInitialized; + bool tickAfterInitialized; + + { + // Get the key and offset in the tick bitmap of the active tick before and after the swap. + int16 wordPos = int16((tickBefore / self.tickSpacing()) >> 8); + uint8 bitPos = uint8((tickBefore / self.tickSpacing()) % 256); + + int16 wordPosAfter = int16((tickAfter / self.tickSpacing()) >> 8); + uint8 bitPosAfter = uint8((tickAfter / self.tickSpacing()) % 256); + + // In the case where tickAfter is initialized, we only want to count it if we are swapping downwards. + // If the initializable tick after the swap is initialized, our original tickAfter is a + // multiple of tick spacing, and we are swapping downwards we know that tickAfter is initialized + // and we shouldn't count it. + tickAfterInitialized = + ((self.tickBitmap(wordPosAfter) & (1 << bitPosAfter)) > 0) && + ((tickAfter % self.tickSpacing()) == 0) && + (tickBefore > tickAfter); + + // In the case where tickBefore is initialized, we only want to count it if we are swapping upwards. + // Use the same logic as above to decide whether we should count tickBefore or not. + tickBeforeInitialized = + ((self.tickBitmap(wordPos) & (1 << bitPos)) > 0) && + ((tickBefore % self.tickSpacing()) == 0) && + (tickBefore < tickAfter); + + if (wordPos < wordPosAfter || (wordPos == wordPosAfter && bitPos <= bitPosAfter)) { + wordPosLower = wordPos; + bitPosLower = bitPos; + wordPosHigher = wordPosAfter; + bitPosHigher = bitPosAfter; + } else { + wordPosLower = wordPosAfter; + bitPosLower = bitPosAfter; + wordPosHigher = wordPos; + bitPosHigher = bitPos; + } + } + + // Count the number of initialized ticks crossed by iterating through the tick bitmap. + // Our first mask should include the lower tick and everything to its left. + uint256 mask = type(uint256).max << bitPosLower; + while (wordPosLower <= wordPosHigher) { + // If we're on the final tick bitmap page, ensure we only count up to our + // ending tick. + if (wordPosLower == wordPosHigher) { + mask = mask & (type(uint256).max >> (255 - bitPosHigher)); + } + + uint256 masked = self.tickBitmap(wordPosLower) & mask; + initializedTicksCrossed += countOneBits(masked); + wordPosLower++; + // Reset our mask so we consider all bits on the next iteration. + mask = type(uint256).max; + } + + if (tickAfterInitialized) { + initializedTicksCrossed -= 1; + } + + if (tickBeforeInitialized) { + initializedTicksCrossed -= 1; + } + + return initializedTicksCrossed; + } + + function countOneBits(uint256 x) private pure returns (uint16) { + uint16 bits = 0; + while (x != 0) { + bits++; + x &= (x - 1); + } + return bits; + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/PositionKey.sol b/contracts/dependencies/uniswapv3-periphery/libraries/PositionKey.sol index 6f5790325..80d96a9bb 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/PositionKey.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/PositionKey.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; library PositionKey { /// @dev Returns the key of the position in the core library diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/PositionValue.sol b/contracts/dependencies/uniswapv3-periphery/libraries/PositionValue.sol new file mode 100644 index 000000000..6a9b8df16 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/PositionValue.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; + +import '../../uniswapv3-core/interfaces/IUniswapV3Pool.sol'; +import '../../uniswapv3-core/libraries/FixedPoint128.sol'; +import '../../uniswapv3-core/libraries/TickMath.sol'; +import '../../uniswapv3-core/libraries/Tick.sol'; +import '../interfaces/INonfungiblePositionManager.sol'; +import './LiquidityAmounts.sol'; +import './PoolAddress.sol'; +import './PositionKey.sol'; + +/// @title Returns information about the token value held in a Uniswap V3 NFT +library PositionValue { + /// @notice Returns the total amounts of token0 and token1, i.e. the sum of fees and principal + /// that a given nonfungible position manager token is worth + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total value + /// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts + /// @return amount0 The total amount of token0 including principal and fees + /// @return amount1 The total amount of token1 including principal and fees + function total( + INonfungiblePositionManager positionManager, + uint256 tokenId, + uint160 sqrtRatioX96 + ) internal view returns (uint256 amount0, uint256 amount1) { + (uint256 amount0Principal, uint256 amount1Principal) = principal(positionManager, tokenId, sqrtRatioX96); + (uint256 amount0Fee, uint256 amount1Fee) = fees(positionManager, tokenId); + return (amount0Principal + amount0Fee, amount1Principal + amount1Fee); + } + + /// @notice Calculates the principal (currently acting as liquidity) owed to the token owner in the event + /// that the position is burned + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total principal owed + /// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts + /// @return amount0 The principal amount of token0 + /// @return amount1 The principal amount of token1 + function principal( + INonfungiblePositionManager positionManager, + uint256 tokenId, + uint160 sqrtRatioX96 + ) internal view returns (uint256 amount0, uint256 amount1) { + (, , , , , int24 tickLower, int24 tickUpper, uint128 liquidity, , , , ) = positionManager.positions(tokenId); + + return + LiquidityAmounts.getAmountsForLiquidity( + sqrtRatioX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + liquidity + ); + } + + struct FeeParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint128 liquidity; + uint256 positionFeeGrowthInside0LastX128; + uint256 positionFeeGrowthInside1LastX128; + uint256 tokensOwed0; + uint256 tokensOwed1; + } + + /// @notice Calculates the total fees owed to the token owner + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total fees owed + /// @return amount0 The amount of fees owed in token0 + /// @return amount1 The amount of fees owed in token1 + function fees(INonfungiblePositionManager positionManager, uint256 tokenId) + internal + view + returns (uint256 amount0, uint256 amount1) + { + ( + , + , + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 positionFeeGrowthInside0LastX128, + uint256 positionFeeGrowthInside1LastX128, + uint256 tokensOwed0, + uint256 tokensOwed1 + ) = positionManager.positions(tokenId); + + return + _fees( + positionManager, + FeeParams({ + token0: token0, + token1: token1, + fee: fee, + tickLower: tickLower, + tickUpper: tickUpper, + liquidity: liquidity, + positionFeeGrowthInside0LastX128: positionFeeGrowthInside0LastX128, + positionFeeGrowthInside1LastX128: positionFeeGrowthInside1LastX128, + tokensOwed0: tokensOwed0, + tokensOwed1: tokensOwed1 + }) + ); + } + + function _fees(INonfungiblePositionManager positionManager, FeeParams memory feeParams) + private + view + returns (uint256 amount0, uint256 amount1) + { + (uint256 poolFeeGrowthInside0LastX128, uint256 poolFeeGrowthInside1LastX128) = + _getFeeGrowthInside( + IUniswapV3Pool( + PoolAddress.computeAddress( + positionManager.factory(), + PoolAddress.PoolKey({token0: feeParams.token0, token1: feeParams.token1, fee: feeParams.fee}) + ) + ), + feeParams.tickLower, + feeParams.tickUpper + ); + + amount0 = + FullMath.mulDiv( + poolFeeGrowthInside0LastX128 - feeParams.positionFeeGrowthInside0LastX128, + feeParams.liquidity, + FixedPoint128.Q128 + ) + + feeParams.tokensOwed0; + + amount1 = + FullMath.mulDiv( + poolFeeGrowthInside1LastX128 - feeParams.positionFeeGrowthInside1LastX128, + feeParams.liquidity, + FixedPoint128.Q128 + ) + + feeParams.tokensOwed1; + } + + function _getFeeGrowthInside( + IUniswapV3Pool pool, + int24 tickLower, + int24 tickUpper + ) private view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + (, int24 tickCurrent, , , , , ) = pool.slot0(); + (, , uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128, , , , ) = pool.ticks(tickLower); + (, , uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128, , , , ) = pool.ticks(tickUpper); + + if (tickCurrent < tickLower) { + feeGrowthInside0X128 = lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128; + feeGrowthInside1X128 = lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128; + } else if (tickCurrent < tickUpper) { + uint256 feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128(); + uint256 feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128(); + feeGrowthInside0X128 = feeGrowthGlobal0X128 - lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128; + } else { + feeGrowthInside0X128 = upperFeeGrowthOutside0X128 - lowerFeeGrowthOutside0X128; + feeGrowthInside1X128 = upperFeeGrowthOutside1X128 - lowerFeeGrowthOutside1X128; + } + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/SqrtPriceMathPartial.sol b/contracts/dependencies/uniswapv3-periphery/libraries/SqrtPriceMathPartial.sol new file mode 100644 index 000000000..b7639f6e2 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/SqrtPriceMathPartial.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.6; + +import '../../uniswapv3-core/libraries/FullMath.sol'; +import '../../uniswapv3-core/libraries/UnsafeMath.sol'; +import '../../uniswapv3-core/libraries/FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Exposes two functions from @uniswap/v3-core SqrtPriceMath +/// that use square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMathPartial { + /// @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); + } +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/TokenRatioSortOrder.sol b/contracts/dependencies/uniswapv3-periphery/libraries/TokenRatioSortOrder.sol new file mode 100644 index 000000000..d9a2c9e80 --- /dev/null +++ b/contracts/dependencies/uniswapv3-periphery/libraries/TokenRatioSortOrder.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; + +library TokenRatioSortOrder { + int256 constant NUMERATOR_MOST = 300; + int256 constant NUMERATOR_MORE = 200; + int256 constant NUMERATOR = 100; + + int256 constant DENOMINATOR_MOST = -300; + int256 constant DENOMINATOR_MORE = -200; + int256 constant DENOMINATOR = -100; +} diff --git a/contracts/dependencies/uniswapv3-periphery/libraries/TransferHelper.sol b/contracts/dependencies/uniswapv3-periphery/libraries/TransferHelper.sol index 0312ca6d5..9ee72732a 100644 --- a/contracts/dependencies/uniswapv3-periphery/libraries/TransferHelper.sol +++ b/contracts/dependencies/uniswapv3-periphery/libraries/TransferHelper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity ^0.7.6; -import '../../openzeppelin/contracts/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; library TransferHelper { /// @notice Transfers tokens from the targeted address to the given destination @@ -16,9 +16,8 @@ library TransferHelper { address to, uint256 value ) internal { - (bool success, bytes memory data) = token.call( - abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value) - ); + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF'); } diff --git a/contracts/interfaces/IUniV3NonfungiblePositionManager.sol b/contracts/interfaces/IUniV3NonfungiblePositionManager.sol new file mode 100644 index 000000000..2d6ef029f --- /dev/null +++ b/contracts/interfaces/IUniV3NonfungiblePositionManager.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.17; + +import {IERC721Metadata} from "../dependencies/openzeppelin/contracts/IERC721Metadata.sol"; +import "../dependencies/openzeppelin/contracts/IERC721Enumerable.sol"; + +/// @title Non-fungible token for positions +/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred +/// and authorized. +interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable { + /// @notice Emitted when liquidity is increased for a position NFT + /// @dev Also emitted when a token is minted + /// @param tokenId The ID of the token for which liquidity was increased + /// @param liquidity The amount by which liquidity for the NFT position was increased + /// @param amount0 The amount of token0 that was paid for the increase in liquidity + /// @param amount1 The amount of token1 that was paid for the increase in liquidity + event IncreaseLiquidity( + uint256 indexed tokenId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + /// @notice Emitted when liquidity is decreased for a position NFT + /// @param tokenId The ID of the token for which liquidity was decreased + /// @param liquidity The amount by which liquidity for the NFT position was decreased + /// @param amount0 The amount of token0 that was accounted for the decrease in liquidity + /// @param amount1 The amount of token1 that was accounted for the decrease in liquidity + event DecreaseLiquidity( + uint256 indexed tokenId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + /// @notice Emitted when tokens are collected for a position NFT + /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior + /// @param tokenId The ID of the token for which underlying tokens were collected + /// @param recipient The address of the account that received the collected tokens + /// @param amount0 The amount of token0 owed to the position that was collected + /// @param amount1 The amount of token1 owed to the position that was collected + event Collect( + uint256 indexed tokenId, + address recipient, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Returns the position information associated with a given token ID. + /// @dev Throws if the token ID is not valid. + /// @param tokenId The ID of the token that represents the position + /// @return nonce The nonce for permits + /// @return operator The address that is approved for spending + /// @return token0 The address of the token0 for a specific pool + /// @return token1 The address of the token1 for a specific pool + /// @return fee The fee associated with the pool + /// @return tickLower The lower end of the tick range for the position + /// @return tickUpper The higher end of the tick range for the position + /// @return liquidity The liquidity of the position + /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position + /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position + /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation + /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation + function positions( + uint256 tokenId + ) + external + view + returns ( + uint96 nonce, + address operator, + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + struct MintParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + address recipient; + uint256 deadline; + } + + /// @notice Creates a new position wrapped in a NFT + /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized + /// a method does not exist, i.e. the pool is assumed to be initialized. + /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata + /// @return tokenId The ID of the token that represents the minted position + /// @return liquidity The amount of liquidity for this position + /// @return amount0 The amount of token0 + /// @return amount1 The amount of token1 + function mint( + MintParams calldata params + ) + external + payable + returns ( + uint256 tokenId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + + struct IncreaseLiquidityParams { + uint256 tokenId; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender` + /// @param params tokenId The ID of the token for which liquidity is being increased, + /// amount0Desired The desired amount of token0 to be spent, + /// amount1Desired The desired amount of token1 to be spent, + /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check, + /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check, + /// deadline The time by which the transaction must be included to effect the change + /// @return liquidity The new liquidity amount as a result of the increase + /// @return amount0 The amount of token0 to acheive resulting liquidity + /// @return amount1 The amount of token1 to acheive resulting liquidity + function increaseLiquidity( + IncreaseLiquidityParams calldata params + ) + external + payable + returns (uint128 liquidity, uint256 amount0, uint256 amount1); + + struct DecreaseLiquidityParams { + uint256 tokenId; + uint128 liquidity; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + /// @notice Decreases the amount of liquidity in a position and accounts it to the position + /// @param params tokenId The ID of the token for which liquidity is being decreased, + /// amount The amount by which liquidity will be decreased, + /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity, + /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity, + /// deadline The time by which the transaction must be included to effect the change + /// @return amount0 The amount of token0 accounted to the position's tokens owed + /// @return amount1 The amount of token1 accounted to the position's tokens owed + function decreaseLiquidity( + DecreaseLiquidityParams calldata params + ) external payable returns (uint256 amount0, uint256 amount1); + + struct CollectParams { + uint256 tokenId; + address recipient; + uint128 amount0Max; + uint128 amount1Max; + } + + /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient + /// @param params tokenId The ID of the NFT for which tokens are being collected, + /// recipient The account that should receive the tokens, + /// amount0Max The maximum amount of token0 to collect, + /// amount1Max The maximum amount of token1 to collect + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect( + CollectParams calldata params + ) external payable returns (uint256 amount0, uint256 amount1); + + /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens + /// must be collected first. + /// @param tokenId The ID of the token that is being burned + function burn(uint256 tokenId) external payable; +} diff --git a/contracts/interfaces/IUniV3SwapRouter.sol b/contracts/interfaces/IUniV3SwapRouter.sol new file mode 100644 index 000000000..3e1497d3a --- /dev/null +++ b/contracts/interfaces/IUniV3SwapRouter.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.17; + +/// @title Router token swapping functionality +/// @notice Functions for swapping tokens via Uniswap V3 +interface ISwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another token + /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + /// @return amountOut The amount of the received token + function exactInputSingle( + ExactInputSingleParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + /// @return amountOut The amount of the received token + function exactInput( + ExactInputParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another token + /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + /// @return amountIn The amount of the input token + function exactOutputSingle( + ExactOutputSingleParams calldata params + ) external payable returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + /// @return amountIn The amount of the input token + function exactOutput( + ExactOutputParams calldata params + ) external payable returns (uint256 amountIn); +} diff --git a/contracts/misc/UniswapV3OracleWrapper.sol b/contracts/misc/UniswapV3OracleWrapper.sol deleted file mode 100644 index 445fda85b..000000000 --- a/contracts/misc/UniswapV3OracleWrapper.sol +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {IUniswapV3OracleWrapper} from "../interfaces/IUniswapV3OracleWrapper.sol"; -import {IPoolAddressesProvider} from "../interfaces/IPoolAddressesProvider.sol"; -import {IPriceOracleGetter} from "../interfaces/IPriceOracleGetter.sol"; -import {IUniswapV3Factory} from "../dependencies/uniswapv3-core/interfaces/IUniswapV3Factory.sol"; -import {IUniswapV3PoolState} from "../dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolState.sol"; -import {INonfungiblePositionManager} from "../dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol"; -import {LiquidityAmounts} from "../dependencies/uniswapv3-periphery/libraries/LiquidityAmounts.sol"; -import {TickMath} from "../dependencies/uniswapv3-core/libraries/TickMath.sol"; -import {SqrtLib} from "../dependencies/math/SqrtLib.sol"; -import {FullMath} from "../dependencies/uniswapv3-core/libraries/FullMath.sol"; -import {IERC20Detailed} from "../dependencies/openzeppelin/contracts/IERC20Detailed.sol"; -import {UinswapV3PositionData} from "../interfaces/IUniswapV3PositionInfoProvider.sol"; -import {SafeCast} from "../dependencies/uniswapv3-core/libraries/SafeCast.sol"; -import {FixedPoint96} from "../dependencies/uniswapv3-core/libraries/FixedPoint96.sol"; - -contract UniswapV3OracleWrapper is IUniswapV3OracleWrapper { - using SafeCast for uint256; - - IUniswapV3Factory immutable UNISWAP_V3_FACTORY; - INonfungiblePositionManager immutable UNISWAP_V3_POSITION_MANAGER; - IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; - uint256 internal constant Q128 = 0x100000000000000000000000000000000; - - constructor(address _factory, address _manager, address _addressProvider) { - UNISWAP_V3_FACTORY = IUniswapV3Factory(_factory); - UNISWAP_V3_POSITION_MANAGER = INonfungiblePositionManager(_manager); - ADDRESSES_PROVIDER = IPoolAddressesProvider(_addressProvider); - } - - struct FeeParams { - uint256 feeGrowthOutside0X128Lower; - uint256 feeGrowthOutside1X128Lower; - uint256 feeGrowthOutside0X128Upper; - uint256 feeGrowthOutside1X128Upper; - } - - struct PairOracleData { - uint256 token0Price; - uint256 token1Price; - uint8 token0Decimal; - uint8 token1Decimal; - uint160 sqrtPriceX96; - } - - /** - * @notice get onchain position data from uniswap for the specified tokenId. - */ - function getOnchainPositionData( - uint256 tokenId - ) public view returns (UinswapV3PositionData memory) { - ( - , - , - address token0, - address token1, - uint24 fee, - int24 tickLower, - int24 tickUpper, - uint128 liquidity, - uint256 feeGrowthInside0LastX128, - uint256 feeGrowthInside1LastX128, - uint256 tokensOwed0, - uint256 tokensOwed1 - ) = UNISWAP_V3_POSITION_MANAGER.positions(tokenId); - - IUniswapV3PoolState pool = IUniswapV3PoolState( - UNISWAP_V3_FACTORY.getPool(token0, token1, fee) - ); - (uint160 currentPrice, int24 currentTick, , , , , ) = pool.slot0(); - - return - UinswapV3PositionData({ - token0: token0, - token1: token1, - fee: fee, - tickLower: tickLower, - tickUpper: tickUpper, - currentTick: currentTick, - currentPrice: currentPrice, - liquidity: liquidity, - feeGrowthInside0LastX128: feeGrowthInside0LastX128, - feeGrowthInside1LastX128: feeGrowthInside1LastX128, - tokensOwed0: tokensOwed0, - tokensOwed1: tokensOwed1 - }); - } - - /** - * @notice get onchain liquidity amount for the specified tokenId. - */ - function getLiquidityAmount( - uint256 tokenId - ) external view returns (uint256 token0Amount, uint256 token1Amount) { - UinswapV3PositionData memory positionData = getOnchainPositionData( - tokenId - ); - (token0Amount, token1Amount) = getLiquidityAmountFromPositionData( - positionData - ); - } - - /** - * @notice calculate liquidity amount for the position data. - * @param positionData The specified position data - */ - function getLiquidityAmountFromPositionData( - UinswapV3PositionData memory positionData - ) public pure returns (uint256 token0Amount, uint256 token1Amount) { - (token0Amount, token1Amount) = LiquidityAmounts.getAmountsForLiquidity( - positionData.currentPrice, - TickMath.getSqrtRatioAtTick(positionData.tickLower), - TickMath.getSqrtRatioAtTick(positionData.tickUpper), - positionData.liquidity - ); - } - - /** - * @notice get liquidity provider fee amount for the specified tokenId. - */ - function getLpFeeAmount( - uint256 tokenId - ) external view returns (uint256 token0Amount, uint256 token1Amount) { - UinswapV3PositionData memory positionData = getOnchainPositionData( - tokenId - ); - (token0Amount, token1Amount) = getLpFeeAmountFromPositionData( - positionData - ); - } - - /** - * @notice calculate liquidity provider fee amount for the position data. - * @param positionData The specified position data - */ - function getLpFeeAmountFromPositionData( - UinswapV3PositionData memory positionData - ) public view returns (uint256 token0Amount, uint256 token1Amount) { - (token0Amount, token1Amount) = _getPendingFeeAmounts(positionData); - - token0Amount += positionData.tokensOwed0; - token1Amount += positionData.tokensOwed1; - } - - /** - * @notice Returns the price for the specified tokenId. - */ - function getTokenPrice(uint256 tokenId) public view returns (uint256) { - UinswapV3PositionData memory positionData = getOnchainPositionData( - tokenId - ); - - PairOracleData memory oracleData = _getOracleData(positionData); - - (uint256 liquidityAmount0, uint256 liquidityAmount1) = LiquidityAmounts - .getAmountsForLiquidity( - oracleData.sqrtPriceX96, - TickMath.getSqrtRatioAtTick(positionData.tickLower), - TickMath.getSqrtRatioAtTick(positionData.tickUpper), - positionData.liquidity - ); - - ( - uint256 feeAmount0, - uint256 feeAmount1 - ) = getLpFeeAmountFromPositionData(positionData); - - return - (((liquidityAmount0 + feeAmount0) * oracleData.token0Price) / - 10 ** oracleData.token0Decimal) + - (((liquidityAmount1 + feeAmount1) * oracleData.token1Price) / - 10 ** oracleData.token1Decimal); - } - - function _getOracleData( - UinswapV3PositionData memory positionData - ) internal view returns (PairOracleData memory) { - PairOracleData memory oracleData; - IPriceOracleGetter oracle = IPriceOracleGetter( - ADDRESSES_PROVIDER.getPriceOracle() - ); - oracleData.token0Price = oracle.getAssetPrice(positionData.token0); - oracleData.token1Price = oracle.getAssetPrice(positionData.token1); - - oracleData.token0Decimal = IERC20Detailed(positionData.token0) - .decimals(); - oracleData.token1Decimal = IERC20Detailed(positionData.token1) - .decimals(); - - oracleData.sqrtPriceX96 = ((SqrtLib.sqrt( - ((oracleData.token0Price * - 10 ** - (36 + - oracleData.token1Decimal - - oracleData.token0Decimal)) / (oracleData.token1Price)) - ) << FixedPoint96.RESOLUTION) / 1E18).toUint160(); - - return oracleData; - } - - function _getPendingFeeAmounts( - UinswapV3PositionData memory positionData - ) internal view returns (uint256 token0Amount, uint256 token1Amount) { - IUniswapV3PoolState pool = IUniswapV3PoolState( - UNISWAP_V3_FACTORY.getPool( - positionData.token0, - positionData.token1, - positionData.fee - ) - ); - FeeParams memory feeParams; - - ( - , - , - feeParams.feeGrowthOutside0X128Lower, - feeParams.feeGrowthOutside1X128Lower, - , - , - , - - ) = pool.ticks(positionData.tickLower); - ( - , - , - feeParams.feeGrowthOutside0X128Upper, - feeParams.feeGrowthOutside1X128Upper, - , - , - , - - ) = pool.ticks(positionData.tickUpper); - - uint256 feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128(); - uint256 feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128(); - - unchecked { - // calculate fee growth below - uint256 feeGrowthBelow0X128; - uint256 feeGrowthBelow1X128; - if (positionData.currentTick >= positionData.tickLower) { - feeGrowthBelow0X128 = feeParams.feeGrowthOutside0X128Lower; - feeGrowthBelow1X128 = feeParams.feeGrowthOutside1X128Lower; - } else { - feeGrowthBelow0X128 = - feeGrowthGlobal0X128 - - feeParams.feeGrowthOutside0X128Lower; - feeGrowthBelow1X128 = - feeGrowthGlobal1X128 - - feeParams.feeGrowthOutside1X128Lower; - } - - // calculate fee growth above - uint256 feeGrowthAbove0X128; - uint256 feeGrowthAbove1X128; - if (positionData.currentTick < positionData.tickUpper) { - feeGrowthAbove0X128 = feeParams.feeGrowthOutside0X128Upper; - feeGrowthAbove1X128 = feeParams.feeGrowthOutside1X128Upper; - } else { - feeGrowthAbove0X128 = - feeGrowthGlobal0X128 - - feeParams.feeGrowthOutside0X128Upper; - feeGrowthAbove1X128 = - feeGrowthGlobal1X128 - - feeParams.feeGrowthOutside1X128Upper; - } - uint256 feeGrowthInside0X128; - uint256 feeGrowthInside1X128; - - feeGrowthInside0X128 = - feeGrowthGlobal0X128 - - feeGrowthBelow0X128 - - feeGrowthAbove0X128; - feeGrowthInside1X128 = - feeGrowthGlobal1X128 - - feeGrowthBelow1X128 - - feeGrowthAbove1X128; - - token0Amount = uint128( - FullMath.mulDiv( - feeGrowthInside0X128 - - positionData.feeGrowthInside0LastX128, - positionData.liquidity, - Q128 - ) - ); - - token1Amount = uint128( - FullMath.mulDiv( - feeGrowthInside1X128 - - positionData.feeGrowthInside1LastX128, - positionData.liquidity, - Q128 - ) - ); - } - } -} diff --git a/contracts/misc/UniswapV3TwapOracleWrapper.sol b/contracts/misc/UniswapV3TwapOracleWrapper.sol deleted file mode 100644 index c2ab8f179..000000000 --- a/contracts/misc/UniswapV3TwapOracleWrapper.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {IUniswapV3PoolDerivedState} from "../dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolDerivedState.sol"; -import {IUniswapV3PoolImmutables} from "../dependencies/uniswapv3-core/interfaces/pool/IUniswapV3PoolImmutables.sol"; -import {SafeCast} from "../dependencies/uniswapv3-core/libraries/SafeCast.sol"; -import {ICLSynchronicityPriceAdapter} from "../dependencies/chainlink/ICLSynchronicityPriceAdapter.sol"; -import {TickMath} from "../dependencies/uniswapv3-core/libraries/TickMath.sol"; -import {FullMath} from "../dependencies/uniswapv3-core/libraries/FullMath.sol"; -import {FixedPoint96} from "../dependencies/uniswapv3-core/libraries/FixedPoint96.sol"; -import {IERC20Detailed} from "../dependencies/openzeppelin/contracts/IERC20Detailed.sol"; - -contract UniswapV3TwapOracleWrapper is ICLSynchronicityPriceAdapter { - using SafeCast for uint256; - - address immutable UNISWAP_V3_POOL; - int32 immutable TWAP_WINDOW; - address immutable ASSET; - bool immutable IS_ASSET_RESERVE_ZERO; - uint256 immutable MANTISSA; - - constructor(address _pool, address _baseCurrency, int32 twapWindow) { - UNISWAP_V3_POOL = _pool; - TWAP_WINDOW = twapWindow; - - address token0 = IUniswapV3PoolImmutables(_pool).token0(); - address token1 = IUniswapV3PoolImmutables(_pool).token1(); - IS_ASSET_RESERVE_ZERO = token0 != _baseCurrency; - ASSET = IS_ASSET_RESERVE_ZERO ? token0 : token1; - - MANTISSA = - 10 ** - (IERC20Detailed(token0).decimals() + - 18 - - IERC20Detailed(token1).decimals()); - } - - function latestAnswer() external view returns (int256) { - uint256 priceX96 = _getTwapPriceX96(UNISWAP_V3_POOL, TWAP_WINDOW); - // priceX96 = (amount1 / amount0) << 96 - // price = price0 / price1 = (amount1 / amount0) * (decimal0 / decimal1) - uint256 price = FullMath.mulDiv(priceX96, MANTISSA, FixedPoint96.Q96); - if (IS_ASSET_RESERVE_ZERO) { - return price.toInt256(); - } else { - // price = price0 / price1 * 1e18 - // price_reciprocal = price1 / price0 * 1e18 = 1e36 / price - return (1E36 / price).toInt256(); - } - } - - function _getTwapPriceX96( - address pool, - int32 twapWindow - ) internal view returns (uint256) { - uint32[] memory secondsAgos = new uint32[](2); - secondsAgos[0] = uint32(twapWindow); - secondsAgos[1] = 0; - - (int56[] memory tickCumulatives, ) = IUniswapV3PoolDerivedState(pool) - .observe(secondsAgos); - - int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; - - int24 arithmeticMeanTick = int24(tickCumulativesDelta / twapWindow); - // Always round to negative infinity to make sqrtPriceX96 smaller - // -1111 / 20 = -55 will be round down to -56 - if ( - tickCumulativesDelta < 0 && (tickCumulativesDelta % twapWindow != 0) - ) arithmeticMeanTick--; - - uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick); - - // sqrtPriceX96 = sqrt(priceX96 >> 96) << 96 - // priceX96 = sqrtPriceX96 * sqrtPriceX96 >> 96 - return FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96); - } -} diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 9453a47a4..4afa270e0 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -16,7 +16,7 @@ import {PercentageMath} from "../math/PercentageMath.sol"; import {WadRayMath} from "../math/WadRayMath.sol"; import {DataTypes} from "../types/DataTypes.sol"; import {ReserveLogic} from "./ReserveLogic.sol"; -import {INonfungiblePositionManager} from "../../../dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol"; +import {INonfungiblePositionManager} from "../../../interfaces/IUniV3NonfungiblePositionManager.sol"; import {XTokenType, IXTokenType} from "../../../interfaces/IXTokenType.sol"; import {Helpers} from "../../libraries/helpers/Helpers.sol"; diff --git a/contracts/protocol/libraries/logic/SupplyLogic.sol b/contracts/protocol/libraries/logic/SupplyLogic.sol index 50d7f1c52..b1d82ba9a 100644 --- a/contracts/protocol/libraries/logic/SupplyLogic.sol +++ b/contracts/protocol/libraries/logic/SupplyLogic.sol @@ -5,7 +5,6 @@ import {IERC20} from "../../../dependencies/openzeppelin/contracts/IERC20.sol"; import {IERC721} from "../../../dependencies/openzeppelin/contracts/IERC721.sol"; import {GPv2SafeERC20} from "../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol"; import {IPToken} from "../../../interfaces/IPToken.sol"; -import {INonfungiblePositionManager} from "../../../dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol"; import {INToken} from "../../../interfaces/INToken.sol"; import {INTokenApeStaking} from "../../../interfaces/INTokenApeStaking.sol"; import {ICollateralizableERC721} from "../../../interfaces/ICollateralizableERC721.sol"; diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 94c1c0362..c4ad6f134 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -26,7 +26,7 @@ import {SafeCast} from "../../../dependencies/openzeppelin/contracts/SafeCast.so import {IToken} from "../../../interfaces/IToken.sol"; import {XTokenType, IXTokenType} from "../../../interfaces/IXTokenType.sol"; import {Helpers} from "../helpers/Helpers.sol"; -import {INonfungiblePositionManager} from "../../../dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol"; +import {INonfungiblePositionManager} from "../../../interfaces/IUniV3NonfungiblePositionManager.sol"; import "../../../interfaces/INTokenApeStaking.sol"; /** diff --git a/contracts/protocol/pool/PoolApeStaking.sol b/contracts/protocol/pool/PoolApeStaking.sol index 487cc19c2..6ba861661 100644 --- a/contracts/protocol/pool/PoolApeStaking.sol +++ b/contracts/protocol/pool/PoolApeStaking.sol @@ -23,7 +23,7 @@ import {IAutoCompoundApe} from "../../interfaces/IAutoCompoundApe.sol"; import {PercentageMath} from "../libraries/math/PercentageMath.sol"; import {WadRayMath} from "../libraries/math/WadRayMath.sol"; import {Math} from "../../dependencies/openzeppelin/contracts/Math.sol"; -import {ISwapRouter} from "../../dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol"; +import {ISwapRouter} from "../../interfaces/IUniV3SwapRouter.sol"; import {IPriceOracleGetter} from "../../interfaces/IPriceOracleGetter.sol"; import {Helpers} from "../libraries/helpers/Helpers.sol"; diff --git a/contracts/protocol/pool/PoolBorrowAndStake.sol b/contracts/protocol/pool/PoolBorrowAndStake.sol index 1af514995..14486c753 100644 --- a/contracts/protocol/pool/PoolBorrowAndStake.sol +++ b/contracts/protocol/pool/PoolBorrowAndStake.sol @@ -20,7 +20,7 @@ import "../libraries/logic/BorrowLogic.sol"; import "../../dependencies/openzeppelin/contracts/SafeCast.sol"; import {IAutoCompoundApe} from "../../interfaces/IAutoCompoundApe.sol"; import {Math} from "../../dependencies/openzeppelin/contracts/Math.sol"; -import {ISwapRouter} from "../../dependencies/uniswapv3-periphery/interfaces/ISwapRouter.sol"; +import {ISwapRouter} from "../../interfaces/IUniV3SwapRouter.sol"; import {IPriceOracleGetter} from "../../interfaces/IPriceOracleGetter.sol"; import {Helpers} from "../libraries/helpers/Helpers.sol"; diff --git a/contracts/protocol/tokenization/NTokenUniswapV3.sol b/contracts/protocol/tokenization/NTokenUniswapV3.sol index a7cc8cfc6..cd868bea8 100644 --- a/contracts/protocol/tokenization/NTokenUniswapV3.sol +++ b/contracts/protocol/tokenization/NTokenUniswapV3.sol @@ -13,7 +13,7 @@ import {WadRayMath} from "../libraries/math/WadRayMath.sol"; import {IPool} from "../../interfaces/IPool.sol"; import {NToken} from "./NToken.sol"; import {DataTypes} from "../libraries/types/DataTypes.sol"; -import {INonfungiblePositionManager} from "../../dependencies/uniswapv3-periphery/interfaces/INonfungiblePositionManager.sol"; +import {INonfungiblePositionManager} from "../../interfaces/IUniV3NonfungiblePositionManager.sol"; import {IWETH} from "../../misc/interfaces/IWETH.sol"; import {XTokenType} from "../../interfaces/IXTokenType.sol"; import {INTokenUniswapV3} from "../../interfaces/INTokenUniswapV3.sol"; diff --git a/hardhat.config.ts b/hardhat.config.ts index 55eb6d5e8..d73d2f9c6 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,6 +27,9 @@ import { MOONBASE_ETHERSCAN_KEY, LINEA_ETHERSCAN_KEY, LINEA_GOERLI_ETHERSCAN_KEY, + SEPOLIA_ETHERSCAN_KEY, + ARBITRUM_SEPOLIA_ETHERSCAN_KEY, + PARAX_DEV_ETHERSCAN_KEY, } from "./helpers/hardhat-constants"; import {accounts} from "./wallets"; import {accounts as evmAccounts} from "./evm-wallets"; @@ -74,12 +77,7 @@ const hardhatConfig: HardhatUserConfig = { alphaSort: true, runOnCompile: false, disambiguatePaths: false, - except: [ - "Mock*", - "ApeCoinStaking", - "SwapRouter", - "NonfungiblePositionManager", - ], + except: ["Mock*"], strict: true, }, paths: { @@ -109,6 +107,18 @@ const hardhatConfig: HardhatUserConfig = { evmVersion: "london", }, }, + { + version: "0.7.6", + settings: { + optimizer: { + enabled: true, + runs: 800, + }, + metadata: { + bytecodeHash: "none", + }, + }, + }, ], }, typechain: { @@ -190,6 +200,11 @@ const hardhatConfig: HardhatUserConfig = { url: NETWORKS_RPC_URL[eEthereumNetwork.goerli], accounts: DEPLOYER, }, + sepolia: { + chainId: CHAINS_ID[eEthereumNetwork.sepolia], + url: NETWORKS_RPC_URL[eEthereumNetwork.sepolia], + accounts: DEPLOYER, + }, arbitrum: { chainId: CHAINS_ID[eEthereumNetwork.arbitrum], url: NETWORKS_RPC_URL[eEthereumNetwork.arbitrum], @@ -200,6 +215,16 @@ const hardhatConfig: HardhatUserConfig = { url: NETWORKS_RPC_URL[eEthereumNetwork.arbitrumGoerli], accounts: DEPLOYER, }, + arbitrumSepolia: { + chainId: CHAINS_ID[eEthereumNetwork.arbitrumSepolia], + url: NETWORKS_RPC_URL[eEthereumNetwork.arbitrumSepolia], + accounts: DEPLOYER, + }, + paraxDev: { + chainId: CHAINS_ID[eEthereumNetwork.paraxDev], + url: NETWORKS_RPC_URL[eEthereumNetwork.paraxDev], + accounts: DEPLOYER, + }, polygon: { chainId: CHAINS_ID[eEthereumNetwork.polygon], url: NETWORKS_RPC_URL[eEthereumNetwork.polygon], @@ -257,8 +282,11 @@ const hardhatConfig: HardhatUserConfig = { localhost: ETHERSCAN_KEY, mainnet: ETHERSCAN_KEY, goerli: GOERLI_ETHERSCAN_KEY, + sepolia: SEPOLIA_ETHERSCAN_KEY, arbitrum: ARBITRUM_ETHERSCAN_KEY, arbitrumGoerli: ARBITRUM_GOERLI_ETHERSCAN_KEY, + arbitrumSepolia: ARBITRUM_SEPOLIA_ETHERSCAN_KEY, + paraxDev: PARAX_DEV_ETHERSCAN_KEY, polygon: POLYGON_ETHERSCAN_KEY, polygonMumbai: POLYGON_MUMBAI_ETHERSCAN_KEY, polygonZkevm: POLYGON_ZKEVM_ETHERSCAN_KEY, @@ -271,8 +299,11 @@ const hardhatConfig: HardhatUserConfig = { customChains: [ eEthereumNetwork.localhost, eEthereumNetwork.goerli, + eEthereumNetwork.sepolia, eEthereumNetwork.arbitrum, eEthereumNetwork.arbitrumGoerli, + eEthereumNetwork.arbitrumSepolia, + eEthereumNetwork.paraxDev, eEthereumNetwork.polygon, eEthereumNetwork.polygonZkevm, eEthereumNetwork.polygonMumbai, diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index 50717531b..51d2cf966 100644 --- a/helper-hardhat-config.ts +++ b/helper-hardhat-config.ts @@ -9,6 +9,7 @@ import { FORK_BLOCK_NUMBER, FORK_CHAINID, GOERLI_CHAINID, + SEPOLIA_CHAINID, HARDHAT_CHAINID, INFURA_KEY, L1_RPC_URL, @@ -27,6 +28,8 @@ import { TENDERLY_FORK_ID, ZKSYNC_CHAINID, ZKSYNC_GOERLI_CHAINID, + ARBITRUM_SEPOLIA_CHAINID, + PARAX_DEV_CHAINID, } from "./helpers/hardhat-constants"; dotenv.config(); @@ -55,6 +58,12 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork = { (ALCHEMY_KEY ? `https://eth-goerli.alchemyapi.io/v2/${ALCHEMY_KEY}` : `https://goerli.infura.io/v3/${INFURA_KEY}`), + [eEthereumNetwork.sepolia]: + L1_RPC_URL || + RPC_URL || + (ALCHEMY_KEY + ? `https://eth-sepolia.alchemyapi.io/v2/${ALCHEMY_KEY}` + : `https://sepolia.infura.io/v3/${INFURA_KEY}`), [eEthereumNetwork.mainnet]: L1_RPC_URL || RPC_URL || @@ -77,6 +86,14 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork = { L2_RPC_URL || RPC_URL || `https://arb-goerli.g.alchemy.com/v2/${ALCHEMY_KEY}`, + [eEthereumNetwork.arbitrumSepolia]: + L2_RPC_URL || + RPC_URL || + `https://arb-sepolia.g.alchemy.com/v2/${ALCHEMY_KEY}`, + [eEthereumNetwork.paraxDev]: + L2_RPC_URL || + RPC_URL || + `https://rpc-surprised-harlequin-bonobo-fvcy2k9oqh.t.conduit.xyz`, [eEthereumNetwork.polygon]: RPC_URL || `https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`, [eEthereumNetwork.polygonMumbai]: @@ -110,6 +127,7 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork = { export const CHAINS_ID: iParamsPerNetwork = { [eEthereumNetwork.mainnet]: MAINNET_CHAINID, [eEthereumNetwork.goerli]: GOERLI_CHAINID, + [eEthereumNetwork.sepolia]: SEPOLIA_CHAINID, [eEthereumNetwork.hardhat]: FORK ? FORK_CHAINID : HARDHAT_CHAINID, [eEthereumNetwork.anvil]: HARDHAT_CHAINID, [eEthereumNetwork.ganache]: undefined, @@ -119,6 +137,8 @@ export const CHAINS_ID: iParamsPerNetwork = { [eEthereumNetwork.moonbase]: MOONBASE_CHAINID, [eEthereumNetwork.arbitrum]: ARBITRUM_ONE_CHAINID, [eEthereumNetwork.arbitrumGoerli]: ARBITRUM_GOERLI_CHAINID, + [eEthereumNetwork.arbitrumSepolia]: ARBITRUM_SEPOLIA_CHAINID, + [eEthereumNetwork.paraxDev]: PARAX_DEV_CHAINID, [eEthereumNetwork.polygon]: POLYGON_CHAINID, [eEthereumNetwork.polygonMumbai]: POLYGON_MUMBAI_CHAINID, [eEthereumNetwork.polygonZkevm]: POLYGON_ZKEVM_CHAINID, @@ -132,6 +152,7 @@ export const CHAINS_ID: iParamsPerNetwork = { export const BLOCK_TO_FORK: iParamsPerNetwork = { [eEthereumNetwork.mainnet]: undefined, [eEthereumNetwork.goerli]: undefined, + [eEthereumNetwork.sepolia]: undefined, [eEthereumNetwork.hardhat]: undefined, [eEthereumNetwork.anvil]: undefined, [eEthereumNetwork.ganache]: undefined, @@ -141,6 +162,8 @@ export const BLOCK_TO_FORK: iParamsPerNetwork = { [eEthereumNetwork.moonbase]: undefined, [eEthereumNetwork.arbitrum]: undefined, [eEthereumNetwork.arbitrumGoerli]: undefined, + [eEthereumNetwork.arbitrumSepolia]: undefined, + [eEthereumNetwork.paraxDev]: undefined, [eEthereumNetwork.polygon]: undefined, [eEthereumNetwork.polygonMumbai]: undefined, [eEthereumNetwork.polygonZkevm]: undefined, diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index b825dbb2f..5c7dfc6e7 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -156,6 +156,7 @@ import { PoolAAPositionMover__factory, PoolBorrowAndStake__factory, PoolBorrowAndStake, + QuoterV2, } from "../types"; import { getACLManager, @@ -957,14 +958,13 @@ export const deployPoolComponents = async ( const config = getParaSpaceConfig(); const treasuryAddress = config.Treasury; - const cApe = await getAutoCompoundApe(); const poolApeStaking = allTokens.APE ? ((await withSaveAndVerify( await getContractFactory("PoolApeStaking", apeStakingLibraries), eContractid.PoolApeStakingImpl, [ provider, - cApe.address, + (await getAutoCompoundApe()).address, allTokens.APE.address, allTokens.USDC.address, (await getUniswapV3SwapRouter()).address, @@ -987,7 +987,7 @@ export const deployPoolComponents = async ( ? ((await withSaveAndVerify( await getContractFactory("PoolBorrowAndStake", BorrowAndStakeLibraries), eContractid.PoolBorrowAndStakeImpl, - [provider, cApe.address, allTokens.APE.address], + [provider, (await getAutoCompoundApe()).address, allTokens.APE.address], verify, false, BorrowAndStakeLibraries, @@ -2094,6 +2094,17 @@ export const deployUniswapV3Factory = async (args: [], verify?: boolean) => verify ) as Promise; +export const deployUniswapV3QuoterV2 = async ( + args: [string, string], + verify?: boolean +) => + withSaveAndVerify( + await getContractFactory("QuoterV2"), + eContractid.QuoterV2, + [...args], + verify + ) as Promise; + export const deployNonfungibleTokenPositionDescriptor = async ( args: [string, string], verify?: boolean diff --git a/helpers/hardhat-constants.ts b/helpers/hardhat-constants.ts index ef66a0b05..f3cae37b7 100644 --- a/helpers/hardhat-constants.ts +++ b/helpers/hardhat-constants.ts @@ -22,13 +22,16 @@ const getPrivateKeyfromEncryptedJson = ( export const HARDHAT_CHAINID = 31337; export const GOERLI_CHAINID = 5; +export const SEPOLIA_CHAINID = 11155111; export const FORK_CHAINID = 522; export const MAINNET_CHAINID = 1; export const PARALLEL_CHAINID = 1592; +export const PARAX_DEV_CHAINID = 2982896226593698; export const MOONBEAM_CHAINID = 1284; export const MOONBASE_CHAINID = 1287; export const ARBITRUM_ONE_CHAINID = 42161; export const ARBITRUM_GOERLI_CHAINID = 421613; +export const ARBITRUM_SEPOLIA_CHAINID = 421613; export const POLYGON_CHAINID = 137; export const POLYGON_ZKEVM_CHAINID = 1101; export const POLYGON_ZKEVM_GOERLI_CHAINID = 1442; @@ -67,10 +70,16 @@ export const DB_PATH = process.env.DB_PATH ?? ":memory:"; export const ETHERSCAN_KEY = process.env.ETHERSCAN_KEY || ""; export const GOERLI_ETHERSCAN_KEY = process.env.GOERLI_ETHERSCAN_KEY || ETHERSCAN_KEY; +export const SEPOLIA_ETHERSCAN_KEY = + process.env.SEPOLIA_ETHERSCAN_KEY || ETHERSCAN_KEY; export const ARBITRUM_ETHERSCAN_KEY = process.env.ARBITRUM_ETHERSCAN_KEY || ETHERSCAN_KEY; export const ARBITRUM_GOERLI_ETHERSCAN_KEY = process.env.ARBITRUM_GOERLI_ETHERSCAN_KEY || ARBITRUM_ETHERSCAN_KEY; +export const ARBITRUM_SEPOLIA_ETHERSCAN_KEY = + process.env.ARBITRUM_SEPOLIA_ETHERSCAN_KEY || ARBITRUM_ETHERSCAN_KEY; +export const PARAX_DEV_ETHERSCAN_KEY = + process.env.PARAX_DEV_ETHERSCAN_KEY || ETHERSCAN_KEY; export const POLYGON_ETHERSCAN_KEY = process.env.POLYGON_ETHERSCAN_KEY || ETHERSCAN_KEY; export const POLYGON_MUMBAI_ETHERSCAN_KEY = @@ -104,6 +113,9 @@ export const ETHERSCAN_NETWORKS = [ "mainnet", "goerli", "arbitrum", + "arbitrumGoerli", + "arbitrumSepolia", + "paraxDev", "polygon", "matic", "polygonMumbai", @@ -122,6 +134,9 @@ export const ETHERSCAN_APIS = { goerli: "https://api-goerli.etherscan.io/api", arbitrum: "https://api.arbiscan.io/api", arbitrumGoerli: "https://api-goerli.arbiscan.io/api", + arbitrumSepolia: "https://api-sepolia.arbiscan.io/api", + paraxDev: + "https://explorerl2new-surprised-harlequin-bonobo-fvcy2k9oqh.t.conduit.xyz/api", polygon: "https://api.polygonscan.com/api", matic: "https://api.polygonscan.com/api", polygonMumbai: "https://api-mumbai.polygonscan.com/api", @@ -141,8 +156,11 @@ export const BROWSER_URLS = { goerli: "https://goerli.etherscan.io", arbitrum: "https://arbiscan.io", arbitrumGoerli: "https://goerli.arbiscan.io", + arbitrumSepolia: "https://sepolia.arbiscan.io", polygonZkevm: "https://zkevm.polygonscan.com", polygonZkevmGoerli: "https://testnet-zkevm.polygonscan.com", + paraxDev: + "https://explorerl2new-surprised-harlequin-bonobo-fvcy2k9oqh.t.conduit.xyz", polygon: "https://polygonscan.com", matic: "https://polygonscan.com", polygonMumbai: "https://mumbai.polygonscan.com", @@ -217,7 +235,7 @@ export const MULTI_SIG_NONCE = process.env.MULTI_SIG_NONCE ? parseInt(process.env.MULTI_SIG_NONCE) : undefined; export const MULTI_SEND_CHUNK_SIZE = parseInt( - process.env.MULTI_SEND_CHUNK_SIZE || "30" + process.env.MULTI_SEND_CHUNK_SIZE || "45" ); export const VERSION = version; @@ -498,5 +516,6 @@ export const XTOKEN_TYPE_UPGRADE_WHITELIST = .split(/\s?,\s?/) .map((x) => +x); export const XTOKEN_SYMBOL_UPGRADE_WHITELIST = - process.env.XTOKEN_SYMBOL_UPGRADE_WHITELIST?.trim() - .split(/\s?,\s?/); + process.env.XTOKEN_SYMBOL_UPGRADE_WHITELIST?.trim().split(/\s?,\s?/); + +export const STACKUP_KEY = process.env.STACKUP_KEY || ""; diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index a8cb6c807..70508561f 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -420,7 +420,8 @@ export const initReservesByHelper = async ( variableDebtTokenToUse = stKSMVariableDebtTokenImplementationAddress; } else if ( reserveSymbol === ERC20TokenContractId.aWETH || - reserveSymbol === ERC20TokenContractId.awstETH + reserveSymbol === ERC20TokenContractId.awstETH || + reserveSymbol === ERC20TokenContractId.aUSDC ) { if (!pTokenATokenImplementationAddress) { pTokenATokenImplementationAddress = ( diff --git a/helpers/misc-utils.ts b/helpers/misc-utils.ts index 011ff7f3a..fad421a01 100644 --- a/helpers/misc-utils.ts +++ b/helpers/misc-utils.ts @@ -28,6 +28,9 @@ import { MOONBASE_CHAINID, LINEA_CHAINID, LINEA_GOERLI_CHAINID, + SEPOLIA_CHAINID, + ARBITRUM_SEPOLIA_CHAINID, + PARAX_DEV_CHAINID, } from "./hardhat-constants"; import {ConstructorArgs, eContractid, tEthereumAddress} from "./types"; import dotenv from "dotenv"; @@ -57,7 +60,10 @@ export const isPublicTestnet = (): boolean => { return ( [ GOERLI_CHAINID, + SEPOLIA_CHAINID, ARBITRUM_GOERLI_CHAINID, + ARBITRUM_SEPOLIA_CHAINID, + PARAX_DEV_CHAINID, ZKSYNC_GOERLI_CHAINID, POLYGON_ZKEVM_GOERLI_CHAINID, POLYGON_MUMBAI_CHAINID, @@ -66,7 +72,10 @@ export const isPublicTestnet = (): boolean => { ].includes(DRE.network.config.chainId!) || [ eEthereumNetwork.goerli, + eEthereumNetwork.sepolia, eEthereumNetwork.arbitrumGoerli, + eEthereumNetwork.arbitrumSepolia, + eEthereumNetwork.paraxDev, eEthereumNetwork.zksyncGoerli, eEthereumNetwork.polygonZkevmGoerli, eEthereumNetwork.polygonMumbai, diff --git a/helpers/types.ts b/helpers/types.ts index 5e6c1842e..883e97a73 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -84,6 +84,7 @@ export type ParaSpaceLibraryAddresses = Libraries; export enum eEthereumNetwork { ropsten = "ropsten", goerli = "goerli", + sepolia = "sepolia", mainnet = "mainnet", hardhat = "hardhat", tenderlyMain = "tenderlyMain", @@ -95,6 +96,8 @@ export enum eEthereumNetwork { moonbase = "moonbase", arbitrum = "arbitrum", arbitrumGoerli = "arbitrumGoerli", + arbitrumSepolia = "arbitrumSepolia", + paraxDev = "paraxDev", polygon = "polygon", polygonMumbai = "polygonMumbai", polygonZkevm = "polygonZkevm", @@ -222,6 +225,7 @@ export enum eContractid { MoonBirdHelper = "MoonBirdHelper", UniswapV3 = "UniswapV3", UniswapV3Factory = "UniswapV3Factory", + QuoterV2 = "QuoterV2", UniswapV3SwapRouter = "UniswapV3SwapRouter", NFTDescriptor = "NFTDescriptor", NonfungibleTokenPositionDescriptor = "NonfungibleTokenPositionDescriptor", @@ -455,6 +459,7 @@ export interface iAssetBase { DAI: T; WETH: T; USDC: T; + aUSDC: T; USDT: T; FRAX: T; WBTC: T; @@ -588,6 +593,7 @@ export enum ERC20TokenContractId { DAI = "DAI", WETH = "WETH", USDC = "USDC", + aUSDC = "aUSDC", USDT = "USDT", FRAX = "FRAX", WBTC = "WBTC", @@ -745,6 +751,7 @@ export type iParamsPerNetworkAll = iEthereumParamsPerNetwork; export interface iEthereumParamsPerNetwork { [eEthereumNetwork.goerli]: T; + [eEthereumNetwork.sepolia]: T; [eEthereumNetwork.mainnet]: T; [eEthereumNetwork.hardhat]: T; [eEthereumNetwork.anvil]: T; @@ -755,6 +762,8 @@ export interface iEthereumParamsPerNetwork { [eEthereumNetwork.moonbase]: T; [eEthereumNetwork.arbitrum]: T; [eEthereumNetwork.arbitrumGoerli]: T; + [eEthereumNetwork.arbitrumSepolia]: T; + [eEthereumNetwork.paraxDev]: T; [eEthereumNetwork.polygon]: T; [eEthereumNetwork.polygonMumbai]: T; [eEthereumNetwork.polygonZkevm]: T; @@ -947,7 +956,7 @@ export interface ICommonConfiguration { Governance: IGovernanceConfig; - AccountAbstraction: IAccountAbstraction; + AccountAbstraction: IAccountAbstraction | undefined; } export interface IParaSpaceConfiguration extends ICommonConfiguration { diff --git a/market-config/index.ts b/market-config/index.ts index 5eecaa4cf..e20c29968 100644 --- a/market-config/index.ts +++ b/market-config/index.ts @@ -1,5 +1,5 @@ import {ZERO_ADDRESS} from "../helpers/constants"; -import {MULTI_SEND, MULTI_SIG} from "../helpers/hardhat-constants"; +import {MULTI_SEND, MULTI_SIG, STACKUP_KEY} from "../helpers/hardhat-constants"; import { eEthereumNetwork, ERC20TokenContractId, @@ -78,6 +78,7 @@ import { strategyEXRP, strategyuBAYC, strategyuPPG, + strategyAUSDC, } from "./reservesConfigs"; export const CommonConfig: Pick< @@ -137,10 +138,7 @@ export const CommonConfig: Pick< }, // ParaSpaceV1 ParaSpaceV1: undefined, - AccountAbstraction: { - rpcUrl: `https://api.stackup.sh/v1/node/${process.env.STACKUP_KEY}`, - paymasterUrl: `https://api.stackup.sh/v1/paymaster/${process.env.STACKUP_KEY}`, - }, + AccountAbstraction: undefined, }; export const HardhatConfig: IParaSpaceConfiguration = { @@ -631,6 +629,82 @@ export const ArbitrumGoerliConfig: IParaSpaceConfiguration = { Oracle: ArbitrumOracleConfig, }; +export const ArbitrumSepoliaConfig: IParaSpaceConfiguration = { + // BASIC INFO + ...CommonConfig, + ParaSpaceTeam: "0x018281853eCC543Aa251732e8FDaa7323247eBeB", + Treasury: "0x018281853eCC543Aa251732e8FDaa7323247eBeB", + YogaLabs: {}, + Uniswap: {}, + Tokens: {}, + Marketplace: {}, + Chainlink: {}, + BendDAO: {}, + Stakefish: {}, + // RESERVE ASSETS - CONFIG, ASSETS, BORROW RATES, + ReservesConfig: { + DAI: strategyDAI, + USDC: strategyUSDC, + USDT: strategyUSDT, + FRAX: strategyFRAX, + WETH: strategyWETH, + WBTC: strategyWBTC, + PUNK: strategyPUNK, + BLUR: strategyBLUR, + DOODLE: strategyDoodles, + WPUNKS: strategyWPunks, + MOONBIRD: strategyMoonbird, + MEEBITS: strategyMeebits, + AZUKI: strategyAzuki, + OTHR: strategyOthr, + CLONEX: strategyClonex, + BLOCKS: strategyBLOCKS, + SEWER: strategySEWER, + PPG: strategyPudgyPenguins, + }, +}; +export const ParaxDevConfig: IParaSpaceConfiguration = { + // BASIC INFO + ...CommonConfig, + ParaSpaceTeam: "0x018281853eCC543Aa251732e8FDaa7323247eBeB", + Treasury: "0x018281853eCC543Aa251732e8FDaa7323247eBeB", + ParaSpaceAdmin: "0x28abAC3E3F1fbC8Aa4Ded74C1589026038f889d8", + EmergencyAdmins: ["0x28abAC3E3F1fbC8Aa4Ded74C1589026038f889d8"], + RiskAdmin: "0x28abAC3E3F1fbC8Aa4Ded74C1589026038f889d8", + GatewayAdmin: "0x28abAC3E3F1fbC8Aa4Ded74C1589026038f889d8", + YogaLabs: {}, + Uniswap: {}, + Tokens: { + aUSDC: "0x259CdA67f5a3836aEBb207b94f0b57f548921631", + }, + Marketplace: {}, + Chainlink: {}, + BendDAO: {}, + Stakefish: {}, + // RESERVE ASSETS - CONFIG, ASSETS, BORROW RATES, + ReservesConfig: { + DAI: strategyDAI, + USDC: strategyUSDC, + USDT: strategyUSDT, + FRAX: strategyFRAX, + WETH: strategyWETH, + WBTC: strategyWBTC, + PUNK: strategyPUNK, + BLUR: strategyBLUR, + DOODLE: strategyDoodles, + WPUNKS: strategyWPunks, + MOONBIRD: strategyMoonbird, + MEEBITS: strategyMeebits, + AZUKI: strategyAzuki, + OTHR: strategyOthr, + CLONEX: strategyClonex, + BLOCKS: strategyBLOCKS, + SEWER: strategySEWER, + PPG: strategyPudgyPenguins, + aUSDC: strategyAUSDC, + }, +}; + export const ZkSyncGoerliConfig: IParaSpaceConfiguration = { // BASIC INFO ...CommonConfig, @@ -1009,6 +1083,10 @@ export const MainnetConfig: IParaSpaceConfiguration = { TimeLockV1: "0x59B72FdB45B3182c8502cC297167FE4f821f332d", P2PPairStakingV1: "0xf090Eb4c2B63e7B26E8Bb09e6Fc0cC3A7586263B", }, + AccountAbstraction: { + rpcUrl: `https://api.stackup.sh/v1/node/${STACKUP_KEY}`, + paymasterUrl: `https://api.stackup.sh/v1/paymaster/${STACKUP_KEY}`, + }, }; export const ParaSpaceConfigs: Partial< @@ -1022,6 +1100,8 @@ export const ParaSpaceConfigs: Partial< [eEthereumNetwork.goerli]: GoerliConfig, [eEthereumNetwork.mainnet]: MainnetConfig, [eEthereumNetwork.arbitrumGoerli]: ArbitrumGoerliConfig, + [eEthereumNetwork.arbitrumSepolia]: ArbitrumSepoliaConfig, + [eEthereumNetwork.paraxDev]: ParaxDevConfig, [eEthereumNetwork.arbitrum]: ArbitrumConfig, [eEthereumNetwork.polygon]: PolygonConfig, [eEthereumNetwork.polygonMumbai]: PolygonMumbaiConfig, diff --git a/market-config/mocks.ts b/market-config/mocks.ts index 6d4fdc362..19c131648 100644 --- a/market-config/mocks.ts +++ b/market-config/mocks.ts @@ -6,6 +6,7 @@ export const MOCK_CHAINLINK_AGGREGATORS_PRICES = { // ERC20 DAI: parseEther("0.000908578801039414").toString(), USDC: parseEther("0.000915952223931999").toString(), + aUSDC: parseEther("0.000477910115122588").toString(), USDT: parseEther("0.000915952223931999").toString(), FRAX: parseEther("0.000915952223931999").toString(), WETH: parseEther("1").toString(), @@ -72,6 +73,7 @@ export const MOCK_CHAINLINK_AGGREGATORS_USD_PRICES = { // ERC20 DAI: parseUnits("1", 8).toString(), USDC: parseUnits("1", 8).toString(), + aUSDC: parseUnits("1", 8).toString(), USDT: parseUnits("1", 8).toString(), FRAX: parseUnits("1", 8).toString(), WETH: parseUnits("1896", 8).toString(), diff --git a/market-config/rateStrategies.ts b/market-config/rateStrategies.ts index 3d3cc4b67..2c8b2ddaf 100644 --- a/market-config/rateStrategies.ts +++ b/market-config/rateStrategies.ts @@ -17,6 +17,14 @@ export const rateStrategyUSDC: IInterestRateStrategyParams = { variableRateSlope2: utils.parseUnits("0.60", 27).toString(), }; +export const rateStrategyaUSDC: IInterestRateStrategyParams = { + name: "rateStrategyaUSDC", + optimalUsageRatio: utils.parseUnits("0.9", 27).toString(), + baseVariableBorrowRate: utils.parseUnits("0", 27).toString(), + variableRateSlope1: utils.parseUnits("0.04", 27).toString(), + variableRateSlope2: utils.parseUnits("0.60", 27).toString(), +}; + export const rateStrategyUSDT: IInterestRateStrategyParams = { name: "rateStrategyUSDT", optimalUsageRatio: utils.parseUnits("0.9", 27).toString(), diff --git a/market-config/reservesConfigs.ts b/market-config/reservesConfigs.ts index 70c0ea388..e498a748d 100644 --- a/market-config/reservesConfigs.ts +++ b/market-config/reservesConfigs.ts @@ -28,6 +28,7 @@ import { rateStrategyAAVE, rateStrategyAPE, rateStrategyARB, + rateStrategyaUSDC, rateStrategyBAL, rateStrategyBLUR, rateStrategyCRV, @@ -114,6 +115,7 @@ import { timeLockStrategyEXRP, timeLockStrategyuPPG, timeLockStrategyuBAYC, + timeLockStrategyaUSDC, } from "./timeLockStrategies"; export const strategyDAI: IReserveParams = { @@ -148,6 +150,22 @@ export const strategyUSDC: IReserveParams = { supplyCap: "0", }; +export const strategyAUSDC: IReserveParams = { + strategy: rateStrategyaUSDC, + auctionStrategy: auctionStrategyZero, + timeLockStrategy: timeLockStrategyaUSDC, + baseLTVAsCollateral: "8700", + liquidationThreshold: "8900", + liquidationProtocolFeePercentage: "0", + liquidationBonus: "10450", + borrowingEnabled: true, + reserveDecimals: "6", + xTokenImpl: eContractid.PTokenATokenImpl, + reserveFactor: "1000", + borrowCap: "0", + supplyCap: "0", +}; + export const strategyUSDT: IReserveParams = { strategy: rateStrategyUSDT, auctionStrategy: auctionStrategyZero, diff --git a/market-config/timeLockStrategies.ts b/market-config/timeLockStrategies.ts index 14fa32282..fe705e005 100644 --- a/market-config/timeLockStrategies.ts +++ b/market-config/timeLockStrategies.ts @@ -13,6 +13,18 @@ export const timeLockStrategyUSDC: ITimeLockStrategyParams = { period: "86400", }; +export const timeLockStrategyaUSDC: ITimeLockStrategyParams = { + name: "timeLockStrategyaUSDC", + minThreshold: parseUnits("105000", 6).toString(), + midThreshold: parseUnits("400000", 6).toString(), + minWaitTime: "12", + midWaitTime: "7200", + maxWaitTime: "21600", + poolPeriodWaitTime: "600", + poolPeriodLimit: parseUnits("1875000", 6).toString(), + period: "86400", +}; + export const timeLockStrategyUSDT: ITimeLockStrategyParams = { name: "timeLockStrategyUSDT", minThreshold: parseUnits("105000", 6).toString(), diff --git a/package.json b/package.json index 2b9547652..ec0500471 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@nomiclabs/hardhat-ethers": "^2.0.0", "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "2.0.3", - "@openzeppelin/contracts": "4.9.3", + "@openzeppelin/contracts": "3.4.2-solc-0.7", "@openzeppelin/contracts-upgradeable": "4.9.3", "@openzeppelin/hardhat-upgrades": "^1.21.0", "@prb/math": "^2.5.0", @@ -47,12 +47,12 @@ "@safe-global/safe-ethers-lib": "^1.7.0", "@safe-global/safe-service-client": "^1.5.0", "@tenderly/hardhat-tenderly": "1.1.0-beta.5", - "@typechain/ethers-v5": "^11.0.0", - "@typechain/hardhat": "^6.1.2", + "@typechain/ethers-v5": "^11.1.2", + "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.2.11", "@types/lowdb": "1.0.9", "@types/mocha": "^9.1.1", - "@types/node": "^18.0.6", + "@types/node": "^20.9.2", "@typescript-eslint/eslint-plugin": "5.33.0", "@typescript-eslint/parser": "5.33.0", "@uniswap/v3-periphery": "1.4.1", @@ -79,7 +79,7 @@ "hardhat-contract-sizer": "^2.0.3", "hardhat-deploy": "^0.11.30", "hardhat-gas-reporter": "^1.0.9", - "hardhat-typechain": "^0.3.3", + "hardhat-typechain": "^0.3.5", "husky": "^8.0.1", "lodash": "^4.17.21", "lowdb": "1.0.0", diff --git a/scripts/deployments/steps/10_allAggregators.ts b/scripts/deployments/steps/10_allAggregators.ts index 39469b580..0280a8b39 100644 --- a/scripts/deployments/steps/10_allAggregators.ts +++ b/scripts/deployments/steps/10_allAggregators.ts @@ -11,9 +11,11 @@ import { getAllTokens, getFirstSigner, getPoolAddressesProvider, - getPriceOracle, } from "../../../helpers/contracts-getters"; -import {getEthersSignersAddresses} from "../../../helpers/contracts-helpers"; +import { + getContractAddressInDb, + getEthersSignersAddresses, +} from "../../../helpers/contracts-helpers"; import {GLOBAL_OVERRIDES} from "../../../helpers/hardhat-constants"; import {getParaSpaceConfig, waitForTx} from "../../../helpers/misc-utils"; import { @@ -23,6 +25,7 @@ import { import { ERC20TokenContractId, ERC721TokenContractId, + eContractid, } from "../../../helpers/types"; export const deployNftOracle = async (verify = false) => { @@ -69,7 +72,6 @@ export const step_10 = async (verify = false) => { try { const allTokens = await getAllTokens(); const addressesProvider = await getPoolAddressesProvider(); - const fallbackOracle = await getPriceOracle(); const paraSpaceConfig = getParaSpaceConfig(); const oracleConfig = paraSpaceConfig.Oracle; const chainlinkConfig = paraSpaceConfig.Chainlink; @@ -93,7 +95,7 @@ export const step_10 = async (verify = false) => { addressesProvider.address, tokens, aggregators, - fallbackOracle.address, + (await getContractAddressInDb(eContractid.PriceOracle)) || ZERO_ADDRESS, oracleConfig.BaseCurrency == ZERO_ADDRESS ? oracleConfig.BaseCurrency : allTokens[oracleConfig.BaseCurrency].address, diff --git a/scripts/deployments/steps/24_accountAbstraction.ts b/scripts/deployments/steps/24_accountAbstraction.ts index 46399bdba..e95edad2a 100644 --- a/scripts/deployments/steps/24_accountAbstraction.ts +++ b/scripts/deployments/steps/24_accountAbstraction.ts @@ -1,3 +1,4 @@ +import {ZERO_ADDRESS} from "../../../helpers/constants"; import {deployAccountFactory} from "../../../helpers/contracts-deployments"; import {getParaSpaceConfig, isLocalTestnet} from "../../../helpers/misc-utils"; import {Client} from "userop"; @@ -6,10 +7,14 @@ export const step_24 = async (verify = false) => { const paraSpaceConfig = getParaSpaceConfig(); try { if (!isLocalTestnet()) { - const client = Client.init(paraSpaceConfig.AccountAbstraction.rpcUrl); - - const entryPoint = (await client).entryPoint.address; - await deployAccountFactory(entryPoint, verify); + await deployAccountFactory( + paraSpaceConfig.AccountAbstraction?.rpcUrl + ? ( + await Client.init(paraSpaceConfig.AccountAbstraction.rpcUrl) + ).entryPoint.address + : ZERO_ADDRESS, + verify + ); } } catch (error) { console.error(error); diff --git a/scripts/dev/1.ad-hoc.ts b/scripts/dev/1.ad-hoc.ts index 9da115afd..bbb38a48a 100644 --- a/scripts/dev/1.ad-hoc.ts +++ b/scripts/dev/1.ad-hoc.ts @@ -1,7 +1,33 @@ import rawBRE from "hardhat"; +import {getWETH} from "../../helpers/contracts-getters"; +import { + deployNonfungiblePositionManager, + deployNonfungibleTokenPositionDescriptor, + deployUniswapSwapRouter, + deployUniswapV3Factory, + deployUniswapV3QuoterV2, +} from "../../helpers/contracts-deployments"; const adHoc = async () => { console.time("ad-hoc"); + + const weth = await getWETH(); + const positionDescriptor = await deployNonfungibleTokenPositionDescriptor( + [ + weth.address, + // 'ETH' as a bytes32 string + "0x4554480000000000000000000000000000000000000000000000000000000000", + ], + false + ); + const factory = await deployUniswapV3Factory([], false); + await deployUniswapV3QuoterV2([factory.address, weth.address], false); + await deployUniswapSwapRouter([factory.address, weth.address], false); + await deployNonfungiblePositionManager( + [factory.address, weth.address, positionDescriptor.address], + false + ); + console.timeEnd("ad-hoc"); }; diff --git a/scripts/upgrade/accountAbstraction.ts b/scripts/upgrade/accountAbstraction.ts index 832bf4b48..a261cbd97 100644 --- a/scripts/upgrade/accountAbstraction.ts +++ b/scripts/upgrade/accountAbstraction.ts @@ -4,17 +4,19 @@ import {getAccountRegistry} from "../../helpers/contracts-getters"; import {dryRunEncodedData} from "../../helpers/contracts-helpers"; import {DRY_RUN, GLOBAL_OVERRIDES} from "../../helpers/hardhat-constants"; import {getParaSpaceConfig, waitForTx} from "../../helpers/misc-utils"; +import {ZERO_ADDRESS} from "../../helpers/constants"; export const upgradeAccountAbstraction = async (verify = false) => { console.time("deploy AccountAbstraction"); const paraSpaceConfig = getParaSpaceConfig(); - const client = Client.init(paraSpaceConfig.AccountAbstraction.rpcUrl); const accountRegistry = await getAccountRegistry(); const account = await deployAccount( - ( - await client - ).entryPoint.address, + paraSpaceConfig.AccountAbstraction?.rpcUrl + ? ( + await Client.init(paraSpaceConfig.AccountAbstraction.rpcUrl) + ).entryPoint.address + : ZERO_ADDRESS, verify ); diff --git a/scripts/upgrade/ntoken.ts b/scripts/upgrade/ntoken.ts index ba3ca36f6..21713a50b 100644 --- a/scripts/upgrade/ntoken.ts +++ b/scripts/upgrade/ntoken.ts @@ -76,12 +76,14 @@ export const upgradeNToken = async (verify = false) => { continue; } - if (XTOKEN_SYMBOL_UPGRADE_WHITELIST && !XTOKEN_SYMBOL_UPGRADE_WHITELIST.includes(symbol)) { + if ( + XTOKEN_SYMBOL_UPGRADE_WHITELIST && + !XTOKEN_SYMBOL_UPGRADE_WHITELIST.includes(symbol) + ) { console.log(symbol + "not in XTOKEN_SYMBOL_UPGRADE_WHITELIST, skip..."); continue; } - if (xTokenType == XTokenType.NTokenBAYC) { if (!nTokenBAYCImplementationAddress) { console.log("deploy NTokenBAYC implementation"); diff --git a/tasks/dev/reserveConfigurator.ts b/tasks/dev/reserveConfigurator.ts index d045689c1..6aaebcc3a 100644 --- a/tasks/dev/reserveConfigurator.ts +++ b/tasks/dev/reserveConfigurator.ts @@ -4,9 +4,9 @@ import {DRY_RUN} from "../../helpers/hardhat-constants"; import {waitForTx} from "../../helpers/misc-utils"; task("set-ltv", "Set LTV") - .addPositionalParam("asset", "asset") + .addPositionalParam("assets", "assets") .addPositionalParam("ltv", "ltv") - .setAction(async ({asset, ltv}, DRE) => { + .setAction(async ({assets, ltv}, DRE) => { await DRE.run("set-DRE"); const {dryRunEncodedData} = await import("../../helpers/contracts-helpers"); const { @@ -19,40 +19,42 @@ task("set-ltv", "Set LTV") const configurator = await getPoolConfiguratorProxy(); const [reservesData] = await ui.getReservesData(provider.address); - const reserveData = reservesData.find( - (x) => x.underlyingAsset === utils.getAddress(asset) - ); - if (!reserveData) { - return; - } + for (const asset of assets.split(",")) { + const reserveData = reservesData.find( + (x) => x.underlyingAsset === utils.getAddress(asset) + ); + if (!reserveData) { + return; + } - const encodedData = configurator.interface.encodeFunctionData( - "configureReserveAsCollateral", - [ - reserveData.underlyingAsset, - ltv, - reserveData.reserveLiquidationThreshold, - reserveData.reserveLiquidationBonus, - ] - ); - if (DRY_RUN) { - await dryRunEncodedData(configurator.address, encodedData); - } else { - await waitForTx( - await configurator.configureReserveAsCollateral( + const encodedData = configurator.interface.encodeFunctionData( + "configureReserveAsCollateral", + [ reserveData.underlyingAsset, ltv, reserveData.reserveLiquidationThreshold, - reserveData.reserveLiquidationBonus - ) + reserveData.reserveLiquidationBonus, + ] ); + if (DRY_RUN) { + await dryRunEncodedData(configurator.address, encodedData); + } else { + await waitForTx( + await configurator.configureReserveAsCollateral( + reserveData.underlyingAsset, + ltv, + reserveData.reserveLiquidationThreshold, + reserveData.reserveLiquidationBonus + ) + ); + } } }); task("set-liquidation-threshold", "Set liquidation threshold") - .addPositionalParam("asset", "asset") + .addPositionalParam("assets", "assets") .addPositionalParam("liquidationThreshold", "liquidation threshold") - .setAction(async ({asset, liquidationThreshold}, DRE) => { + .setAction(async ({assets, liquidationThreshold}, DRE) => { await DRE.run("set-DRE"); const {dryRunEncodedData} = await import("../../helpers/contracts-helpers"); const { @@ -65,40 +67,42 @@ task("set-liquidation-threshold", "Set liquidation threshold") const configurator = await getPoolConfiguratorProxy(); const [reservesData] = await ui.getReservesData(provider.address); - const reserveData = reservesData.find( - (x) => x.underlyingAsset === utils.getAddress(asset) - ); - if (!reserveData) { - return; - } + for (const asset of assets.split(",")) { + const reserveData = reservesData.find( + (x) => x.underlyingAsset === utils.getAddress(asset) + ); + if (!reserveData) { + return; + } - const encodedData = configurator.interface.encodeFunctionData( - "configureReserveAsCollateral", - [ - reserveData.underlyingAsset, - reserveData.baseLTVasCollateral, - liquidationThreshold, - reserveData.reserveLiquidationBonus, - ] - ); - if (DRY_RUN) { - await dryRunEncodedData(configurator.address, encodedData); - } else { - await waitForTx( - await configurator.configureReserveAsCollateral( + const encodedData = configurator.interface.encodeFunctionData( + "configureReserveAsCollateral", + [ reserveData.underlyingAsset, reserveData.baseLTVasCollateral, liquidationThreshold, - reserveData.reserveLiquidationBonus - ) + reserveData.reserveLiquidationBonus, + ] ); + if (DRY_RUN) { + await dryRunEncodedData(configurator.address, encodedData); + } else { + await waitForTx( + await configurator.configureReserveAsCollateral( + reserveData.underlyingAsset, + reserveData.baseLTVasCollateral, + liquidationThreshold, + reserveData.reserveLiquidationBonus + ) + ); + } } }); task("set-reserve-factor", "Set reserve factor") - .addPositionalParam("asset", "asset") + .addPositionalParam("assets", "assets") .addPositionalParam("reserveFactor", "reserve factor") - .setAction(async ({asset, reserveFactor}, DRE) => { + .setAction(async ({assets, reserveFactor}, DRE) => { await DRE.run("set-DRE"); const {dryRunEncodedData} = await import("../../helpers/contracts-helpers"); const { @@ -111,26 +115,28 @@ task("set-reserve-factor", "Set reserve factor") const configurator = await getPoolConfiguratorProxy(); const [reservesData] = await ui.getReservesData(provider.address); - const reserveData = reservesData.find( - (x) => x.underlyingAsset === utils.getAddress(asset) - ); - if (!reserveData) { - return; - } + for (const asset of assets.split(",")) { + const reserveData = reservesData.find( + (x) => x.underlyingAsset === utils.getAddress(asset) + ); + if (!reserveData) { + return; + } - const encodedData = configurator.interface.encodeFunctionData( - "setReserveFactor", - [reserveData.underlyingAsset, reserveFactor] - ); - if (DRY_RUN) { - await dryRunEncodedData(configurator.address, encodedData); - } else { - await waitForTx( - await configurator.setReserveFactor( - reserveData.underlyingAsset, - reserveFactor - ) + const encodedData = configurator.interface.encodeFunctionData( + "setReserveFactor", + [reserveData.underlyingAsset, reserveFactor] ); + if (DRY_RUN) { + await dryRunEncodedData(configurator.address, encodedData); + } else { + await waitForTx( + await configurator.setReserveFactor( + reserveData.underlyingAsset, + reserveFactor + ) + ); + } } }); @@ -216,6 +222,46 @@ task("set-auction-strategy", "Set auction strategy") } }); +task("set-timelock-strategy", "Set timelock strategy") + .addPositionalParam("assets", "assets") + .addPositionalParam("timeLockStrategyAddress", "time lock strategy address") + .setAction(async ({assets, timeLockStrategyAddress}, DRE) => { + await DRE.run("set-DRE"); + const {dryRunEncodedData} = await import("../../helpers/contracts-helpers"); + const { + getPoolConfiguratorProxy, + getPoolAddressesProvider, + getUiPoolDataProvider, + } = await import("../../helpers/contracts-getters"); + const ui = await getUiPoolDataProvider(); + const provider = await getPoolAddressesProvider(); + const configurator = await getPoolConfiguratorProxy(); + const [reservesData] = await ui.getReservesData(provider.address); + + for (const asset of assets.split(",")) { + const reserveData = reservesData.find( + (x) => x.underlyingAsset === utils.getAddress(asset) + ); + if (!reserveData) { + continue; + } + const encodedData = configurator.interface.encodeFunctionData( + "setReserveTimeLockStrategyAddress", + [reserveData.underlyingAsset, timeLockStrategyAddress] + ); + if (DRY_RUN) { + await dryRunEncodedData(configurator.address, encodedData); + } else { + await waitForTx( + await configurator.setReserveTimeLockStrategyAddress( + reserveData.underlyingAsset, + timeLockStrategyAddress + ) + ); + } + } + }); + task("set-supply-cap", "Set supply cap") .addPositionalParam("asset", "asset") .addPositionalParam("supplyCap", "new supply cap") diff --git a/yarn.lock b/yarn.lock index ee72519f1..32cf47289 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1518,13 +1518,6 @@ __metadata: languageName: node linkType: hard -"@openzeppelin/contracts@npm:4.9.3": - version: 4.9.3 - resolution: "@openzeppelin/contracts@npm:4.9.3" - checksum: 4932063e733b35fa7669b9fe2053f69b062366c5c208b0c6cfa1ac451712100c78acff98120c3a4b88d94154c802be05d160d71f37e7d74cadbe150964458838 - languageName: node - linkType: hard - "@openzeppelin/defender-base-client@npm:^1.46.0": version: 1.48.0 resolution: "@openzeppelin/defender-base-client@npm:1.48.0" @@ -1610,7 +1603,7 @@ __metadata: "@nomiclabs/hardhat-ethers": ^2.0.0 "@nomiclabs/hardhat-etherscan": ^3.1.7 "@nomiclabs/hardhat-waffle": 2.0.3 - "@openzeppelin/contracts": 4.9.3 + "@openzeppelin/contracts": 3.4.2-solc-0.7 "@openzeppelin/contracts-upgradeable": 4.9.3 "@openzeppelin/hardhat-upgrades": ^1.21.0 "@prb/math": ^2.5.0 @@ -1619,12 +1612,12 @@ __metadata: "@safe-global/safe-ethers-lib": ^1.7.0 "@safe-global/safe-service-client": ^1.5.0 "@tenderly/hardhat-tenderly": 1.1.0-beta.5 - "@typechain/ethers-v5": ^11.0.0 - "@typechain/hardhat": ^6.1.2 + "@typechain/ethers-v5": ^11.1.2 + "@typechain/hardhat": ^9.1.0 "@types/chai": ^4.2.11 "@types/lowdb": 1.0.9 "@types/mocha": ^9.1.1 - "@types/node": ^18.0.6 + "@types/node": ^20.9.2 "@typescript-eslint/eslint-plugin": 5.33.0 "@typescript-eslint/parser": 5.33.0 "@uniswap/v3-periphery": 1.4.1 @@ -1651,7 +1644,7 @@ __metadata: hardhat-contract-sizer: ^2.0.3 hardhat-deploy: ^0.11.30 hardhat-gas-reporter: ^1.0.9 - hardhat-typechain: ^0.3.3 + hardhat-typechain: ^0.3.5 husky: ^8.0.1 lodash: ^4.17.21 lowdb: 1.0.0 @@ -2061,9 +2054,9 @@ __metadata: languageName: node linkType: hard -"@typechain/ethers-v5@npm:^11.0.0": - version: 11.1.1 - resolution: "@typechain/ethers-v5@npm:11.1.1" +"@typechain/ethers-v5@npm:^11.1.2": + version: 11.1.2 + resolution: "@typechain/ethers-v5@npm:11.1.2" dependencies: lodash: ^4.17.15 ts-essentials: ^7.0.1 @@ -2071,9 +2064,9 @@ __metadata: "@ethersproject/abi": ^5.0.0 "@ethersproject/providers": ^5.0.0 ethers: ^5.1.3 - typechain: ^8.3.1 + typechain: ^8.3.2 typescript: ">=4.3.0" - checksum: af4f3198c4f76e38cf45f992482329b172e1c668f5a5c4743764ab5011d928cdc66a1feb9c02dba80a14fb3124bc37f39a6956f5323c0ac32f919e09cfb2a3b8 + checksum: 4f2d458306094c8e57bc0e05c86c1def210bd8b2cadd1062218cbc35fe3f6ac98aca08646c935a6fa5e57599be9fc0ddd2d4f7cf960fe28e27bf3ed58906c71f languageName: node linkType: hard @@ -2089,19 +2082,17 @@ __metadata: languageName: node linkType: hard -"@typechain/hardhat@npm:^6.1.2": - version: 6.1.6 - resolution: "@typechain/hardhat@npm:6.1.6" +"@typechain/hardhat@npm:^9.1.0": + version: 9.1.0 + resolution: "@typechain/hardhat@npm:9.1.0" dependencies: fs-extra: ^9.1.0 peerDependencies: - "@ethersproject/abi": ^5.4.7 - "@ethersproject/providers": ^5.4.7 - "@typechain/ethers-v5": ^10.2.1 - ethers: ^5.4.7 + "@typechain/ethers-v6": ^0.5.1 + ethers: ^6.1.0 hardhat: ^2.9.9 - typechain: ^8.1.1 - checksum: f214bebf7860956230478cb92696ba757829cfd9dc65ac99c3bc7e539378310318d92b009054186f446595c8ffc1a81e9c6d028da0eb04253253049ea1b6e8d3 + typechain: ^8.3.2 + checksum: a05998ce89bb4a297f233f4489e4af2a3ad0abcedd75392096745a378d7c5bab9650fcd5dd778c9deaf790bf16e3a849b3ed91a6ae916173df6dfc9e574efcbc languageName: node linkType: hard @@ -2298,10 +2289,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^18.0.6": - version: 18.17.8 - resolution: "@types/node@npm:18.17.8" - checksum: ebb71526368c9c58f03e2c2408bfda4aa686c13d84226e2c9b48d9c4aee244fb82e672aaf4aa8ccb6e4993b4274d5f4b2b3d52d0a2e57ab187ae653903376411 +"@types/node@npm:^20.9.2": + version: 20.9.2 + resolution: "@types/node@npm:20.9.2" + dependencies: + undici-types: ~5.26.4 + checksum: 5bbb8fb2248fc5c5c4071d9809fb9af85997677c07124d65665202b53283a3b7bdff26fb844e9ee407e3847dfce6399c2b01e3329ea44a4b720647b1b987c678 languageName: node linkType: hard @@ -8515,7 +8508,7 @@ __metadata: languageName: node linkType: hard -"hardhat-typechain@npm:^0.3.3": +"hardhat-typechain@npm:^0.3.5": version: 0.3.5 resolution: "hardhat-typechain@npm:0.3.5" peerDependencies: @@ -15363,6 +15356,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "undici@npm:^5.14.0": version: 5.23.0 resolution: "undici@npm:5.23.0"