From 340f29ac6382f908bf409ae30e1f6308f7541c2f Mon Sep 17 00:00:00 2001 From: JulesCrown Date: Sat, 25 Nov 2023 22:06:38 +0100 Subject: [PATCH] wip --- .gitmodules | 3 - lib/v3-periphery | 1 - remappings.txt | 2 +- src/LiquidityManager.sol | 116 +++++++++++++++++++++------------------ 4 files changed, 65 insertions(+), 57 deletions(-) delete mode 160000 lib/v3-periphery diff --git a/.gitmodules b/.gitmodules index 1fc5fe8..3ab5580 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,6 @@ [submodule "lib/v3-core"] path = lib/v3-core url = https://github.com/Uniswap/v3-core -[submodule "lib/v3-periphery"] - path = lib/v3-periphery - url = https://github.com/Uniswap/v3-periphery [submodule "lib/v3-periphery-foundry"] path = lib/v3-periphery-foundry url = https://github.com/gakonst/v3-periphery-foundry diff --git a/lib/v3-periphery b/lib/v3-periphery deleted file mode 160000 index 80f26c8..0000000 --- a/lib/v3-periphery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058 diff --git a/remappings.txt b/remappings.txt index d7a8767..74084b9 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,3 @@ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @uniswap/v3-core/=lib/v3-core/ -@uniswap/v3-periphery/=lib/v3-periphery/ +@uniswap/v3-periphery/=lib/v3-periphery-foundry/ diff --git a/src/LiquidityManager.sol b/src/LiquidityManager.sol index 30f6156..275e8f1 100644 --- a/src/LiquidityManager.sol +++ b/src/LiquidityManager.sol @@ -1,27 +1,29 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; -import {BloodX} from "src/BloodX.sol"; -import "@uniswap/v3-core/contracts/libraries/TickMath.sol"; -import "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol"; -import "@uniswap/v3-periphery/contracts/libraries/PoolAddress.sol"; -import "@uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol"; -import '@uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol'; -import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {BloodX} from 'src/BloodX.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol'; +import '@uniswap/v3-periphery/contracts/libraries/PoolAddress.sol'; +import '@uniswap/v3-periphery/contracts/libraries/PositionKey.sol'; +import '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; /** * @title LiquidityManager - A contract that supports the $bloodX ecosystem. It * protects the communities liquidity while allowing a manager role to * take strategic liqudity positions. */ -contract LiquidityManager is PeripheryImmutableState { +contract LiquidityManager { // default fee of 1% uint24 constant FEE = uint24(10_000); - /// @notice WETH9, which is an underlying pool token. - address immutable WETH; + // the address of the Uniswap V3 factory + address public immutable factory; + // the address of WETH9 + address public immutable WETH9; struct AddLiquidityParams { address token0; @@ -54,48 +56,59 @@ contract LiquidityManager is PeripheryImmutableState { uint256 feeGrowthInside1LastX128; } - struct FeesOwed { - // how many uncollected tokens are owed - // updated by each position, as of the last computation - uint128 tokensOwed; - uint128 ethOwed; - } - - mapping(address => FeesOwed) private _feesOwed; + // uint256 = uint128 tokensOwed + uint128 ethOwed + mapping(address => uint256) private _feesOwed; /// @dev The token ID position data - mapping(bytes32 => TokenPosition) private _positions; - - constructor( - address _factory, - address _WETH9 - ) { - WETH = _weth; - } + mapping(bytes26 => TokenPosition) private _positions; modifier checkDeadline(uint256 deadline) { require(block.timestamp <= deadline, 'Transaction too old'); _; } - function getToken(address token0, address token1) internal view returns (address) { - bool token0isWeth = (token0 == WETH); - require(token0isWeth || token1 == WETH, "token error"); - return (token0isWeth) ? token1 : token0; + /// @notice Emitted when liquidity is increased for a position + /// @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(address indexed token, uint128 liquidity, uint256 amount0, uint256 amount1); + /// @notice Emitted when liquidity is decreased for a position + /// @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(address indexed token, uint128 liquidity, uint256 amount0, uint256 amount1); + + constructor( + address _factory, + address _WETH9 + ) { + factory = _factory; + WETH9 = _WETH9; + } + + function getToken(address token0, address token1) internal view returns (bool token0isWeth, address token) { + token0isWeth = (token0 == WETH9); + require(token0isWeth || token1 == WETH9, "token error"); + token = (token0isWeth) ? token1 : token0; } function getFeesOwed(address token) public view returns (uint128 tokensOwed, uint128 ethOwed) { - (tokensOwed, ethOwed) = _feesOwed[token]; + uint256 feesOwed = _feesOwed[token]; + return (uint128(feesOwed >> 128), uint128(feesOwed)); } function updateFeesOwed(bool token0isEth, address token, uint128 tokensOwed0, uint128 tokensOwed1) internal { - FeesOwed storage feesOwed = _feesOwed[token]; - feesOwed = (feesOwed.tokensOwed += token0isEth ? tokensOwed1 : tokensOwed0, - feesOwed.ethOwed += token0isEth ? tokensOwed0 : tokensOwed1); + (uint128 tokensOwed, uint128 ethOwed) = getFeesOwed(token); + tokensOwed += token0isEth ? tokensOwed1 : tokensOwed0; + ethOwed += token0isEth ? tokensOwed0 : tokensOwed1; + _feesOwed[token] = uint256(tokensOwed) << 128 + ethOwed; } - function posKey(address token, uint24 tickLower, uint24 tickUpper) internal pure returns (uint256) { - return uint160(token) << 48 + tickLower << 24 + tickUpper; + function posKey(address token, int24 tickLower, int24 tickUpper) internal pure returns (bytes26 _posKey) { + bytes memory _posKeyBytes = abi.encodePacked(token, tickLower, tickUpper); + assembly { + _posKey := mload(add(_posKeyBytes, 26)) + } } function positions(address token, int24 tickLower, int24 tickUpper) external view @@ -117,12 +130,13 @@ contract LiquidityManager is PeripheryImmutableState { uint256 amount0Owed, uint256 amount1Owed, bytes calldata data - ) external override { + ) external { PoolAddress.PoolKey memory poolKey = abi.decode(data, (PoolAddress.PoolKey)); - CallbackValidation.verifyCallback(factory, poolKey); + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + require(msg.sender == address(pool)); - if (amount0Owed > 0) ERC20(poolKey.token0).transfer(msg.sender, amount0Owed); - if (amount1Owed > 0) ERC20(poolKey.token1).transfer(msg.sender, amount1Owed); + if (amount0Owed > 0) IERC20(poolKey.token0).transfer(msg.sender, amount0Owed); + if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed); } /// @notice Add liquidity to an initialized pool @@ -137,7 +151,7 @@ contract LiquidityManager is PeripheryImmutableState { { PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: FEE}); - pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); // compute the liquidity amount { @@ -159,7 +173,7 @@ contract LiquidityManager is PeripheryImmutableState { params.tickLower, params.tickUpper, liquidity, - abi.encode(PoolAddress.PoolKey(poolKey)) + abi.encode(poolKey) ); require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check'); @@ -171,7 +185,7 @@ contract LiquidityManager is PeripheryImmutableState { (bool token0isWeth, address token) = getToken(params.token0, params.token1); - TokenPosition memory position = _positions[posKey(token, tickLower, tickUpper)]; + TokenPosition memory position = _positions[posKey(token, params.tickLower, params.tickUpper)]; if (liquidity == 0) { // create entry position = TokenPosition({ @@ -181,7 +195,7 @@ contract LiquidityManager is PeripheryImmutableState { }); } else { // update entry - updateTokensOwed(token0isWeth, token, uint128( + updateFeesOwed(token0isWeth, token, uint128( FullMath.mulDiv( feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, position.liquidity, @@ -200,26 +214,24 @@ contract LiquidityManager is PeripheryImmutableState { position.liquidity += liquidity; } - emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1); + emit IncreaseLiquidity(token, liquidity, amount0, amount1); } function decreaseLiquidity(DecreaseLiquidityParams calldata params) external - payable - override checkDeadline(params.deadline) returns (uint256 amount0, uint256 amount1) { require(params.liquidity > 0); (bool token0isWeth, address token) = getToken(params.token0, params.token1); - TokenPosition memory position = _positions[posKey(token, tickLower, tickUpper)]; + TokenPosition memory position = _positions[posKey(token, params.tickLower, params.tickUpper)]; uint128 positionLiquidity = position.liquidity; require(positionLiquidity >= params.liquidity); PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: FEE}); - pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); (amount0, amount1) = pool.burn(params.tickLower, params.tickUpper, params.liquidity); @@ -229,7 +241,7 @@ contract LiquidityManager is PeripheryImmutableState { // this is now updated to the current transaction (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); - updateTokensOwed(token0isWeth, token, uint128(amount0) + + updateFeesOwed(token0isWeth, token, uint128(amount0) + uint128( FullMath.mulDiv( feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, @@ -250,6 +262,6 @@ contract LiquidityManager is PeripheryImmutableState { // subtraction is safe because we checked positionLiquidity is gte params.liquidity position.liquidity = positionLiquidity - params.liquidity; - emit DecreaseLiquidity(params.tokenId, params.liquidity, amount0, amount1); + emit DecreaseLiquidity(token, params.liquidity, amount0, amount1); } } \ No newline at end of file