new deployment with public position counter
This commit is contained in:
parent
4e895f57e9
commit
2314ff51c1
8 changed files with 236 additions and 328 deletions
195
onchain/src/BaseLineLP.sol
Normal file
195
onchain/src/BaseLineLP.sol
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@uniswap-v3-periphery/libraries/PositionKey.sol";
|
||||
import "@uniswap-v3-core/libraries/FixedPoint128.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
import "@aperture/uni-v3-lib/TickMath.sol";
|
||||
import "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
||||
import "@aperture/uni-v3-lib/PoolAddress.sol";
|
||||
import "@aperture/uni-v3-lib/CallbackValidation.sol";
|
||||
import "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import "./interfaces/IWETH9.sol";
|
||||
import {Harb} from "./Harb.sol";
|
||||
|
||||
/**
|
||||
* @title LiquidityManager - A contract that supports the harb ecosystem. It
|
||||
* protects the communities liquidity while allowing a manager role to
|
||||
* take strategic liqudity positions.
|
||||
*/
|
||||
contract BaseLineLP {
|
||||
// default fee of 1%
|
||||
uint24 constant FEE = uint24(10_000);
|
||||
|
||||
// the address of the Uniswap V3 factory
|
||||
address immutable factory;
|
||||
IWETH9 immutable weth;
|
||||
Harb immutable harb;
|
||||
IUniswapV3Pool immutable pool;
|
||||
PoolKey private poolKey;
|
||||
bool immutable token0isWeth;
|
||||
|
||||
|
||||
struct TokenPosition {
|
||||
// the liquidity of the position
|
||||
uint128 liquidity;
|
||||
int24 tickLower;
|
||||
int24 tickUpper;
|
||||
// the fee growth of the aggregate position as of the last action on the individual position
|
||||
uint256 feeGrowthInside0LastX128;
|
||||
uint256 feeGrowthInside1LastX128;
|
||||
}
|
||||
|
||||
TokenPosition public floor;
|
||||
TokenPosition public anchor;
|
||||
TokenPosition public discovery;
|
||||
|
||||
modifier checkDeadline(uint256 deadline) {
|
||||
require(block.timestamp <= deadline, "Transaction too old");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @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(int24 indexed tickLower, int24 indexed tickUpper, 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 ethReceived The amount of WETH that was accounted for the decrease in liquidity
|
||||
event PositionLiquidated(int24 indexed tickLower, int24 indexed tickUpper, uint128 liquidity, uint256 ethReceived);
|
||||
|
||||
constructor(address _factory, address _WETH9, address _harb) {
|
||||
factory = _factory;
|
||||
weth = IWETH9(_WETH9);
|
||||
poolKey = PoolAddress.getPoolKey(_WETH9, _harb, FEE);
|
||||
pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
|
||||
harb = Harb(_harb);
|
||||
token0isWeth = _WETH9 < _harb;
|
||||
}
|
||||
|
||||
function posKey(int24 tickLower, int24 tickUpper) internal pure returns (bytes6 _posKey) {
|
||||
bytes memory _posKeyBytes = abi.encodePacked(tickLower, tickUpper);
|
||||
assembly {
|
||||
_posKey := mload(add(_posKeyBytes, 6))
|
||||
}
|
||||
}
|
||||
|
||||
function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external {
|
||||
CallbackValidation.verifyCallback(factory, poolKey);
|
||||
if (amount0Owed > 0) IERC20(poolKey.token0).transfer(msg.sender, amount0Owed);
|
||||
if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed);
|
||||
}
|
||||
|
||||
function createPosition(int24 tickLower, int24 tickUpper, uint256 amount) internal {
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
|
||||
uint128 liquidity = LiquidityAmounts.getLiquidityForAmount1(
|
||||
sqrtRatioAX96, sqrtRatioBX96, amount / 20
|
||||
);
|
||||
pool.mint(address(this), tickLower, tickUpper, liquidity, abi.encode(poolKey));
|
||||
// read position and start tracking in storage
|
||||
bytes32 positionKey = PositionKey.compute(address(this), tickLower, tickUpper);
|
||||
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128,,) = pool.positions(positionKey);
|
||||
discovery = TokenPosition({
|
||||
liquidity: liquidity,
|
||||
tickLower: tickLower,
|
||||
tickUpper: tickUpper,
|
||||
feeGrowthInside0LastX128: feeGrowthInside0LastX128,
|
||||
feeGrowthInside1LastX128: feeGrowthInside1LastX128
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// called once at the beginning
|
||||
function deployLiquidity(int24 startTick, uint256 amount) external {
|
||||
require(floor.liquidity == 0, "already set up");
|
||||
require(anchor.liquidity == 0, "already set up");
|
||||
require(discovery.liquidity == 0, "already set up");
|
||||
harb.mint(amount);
|
||||
int24 tickLower;
|
||||
int24 tickUpper;
|
||||
|
||||
// create floor
|
||||
if (token0isWeth) {
|
||||
tickLower = startTick;
|
||||
tickUpper = startTick + 200;
|
||||
createPosition(tickLower, tickUpper, amount / 10);
|
||||
} else {
|
||||
tickLower = startTick - 200;
|
||||
tickUpper = startTick;
|
||||
createPosition(tickLower, tickUpper, amount / 10);
|
||||
}
|
||||
|
||||
// create anchor
|
||||
if (token0isWeth) {
|
||||
tickLower += 201;
|
||||
tickUpper += 601;
|
||||
createPosition(tickLower, tickUpper, amount / 20);
|
||||
} else {
|
||||
tickLower -= 601;
|
||||
tickUpper -= 201;
|
||||
createPosition(tickLower, tickUpper, amount / 10);
|
||||
}
|
||||
|
||||
// create discovery
|
||||
if (token0isWeth) {
|
||||
tickLower += 601;
|
||||
tickUpper += 11001;
|
||||
createPosition(tickLower, tickUpper, harb.balanceOf(address(this)));
|
||||
} else {
|
||||
tickLower -= 11001;
|
||||
tickUpper -= 601;
|
||||
createPosition(tickLower, tickUpper, harb.balanceOf(address(this)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getInverseAmountsForLiquidity(
|
||||
uint160 sqrtRatioX96,
|
||||
uint160 sqrtRatioAX96,
|
||||
uint160 sqrtRatioBX96,
|
||||
uint128 liquidity
|
||||
) internal pure returns (uint256 amount0, uint256 amount1) {
|
||||
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
|
||||
|
||||
if (sqrtRatioX96 <= sqrtRatioAX96) {
|
||||
amount1 = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
|
||||
} else if (sqrtRatioX96 < sqrtRatioBX96) {
|
||||
amount1 = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
|
||||
amount0 = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
|
||||
} else {
|
||||
amount0 = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateCapacity() public view returns (uint256 capacity) {
|
||||
capacity = 0;
|
||||
(, int24 currentTick, , , , , ) = pool.slot0();
|
||||
uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(currentTick);
|
||||
// handle floor
|
||||
{
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(floor.tickLower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(floor.tickUpper);
|
||||
(uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, floor.liquidity);
|
||||
capacity += token0isWeth ? amount1 : amount0;
|
||||
}
|
||||
|
||||
// handle anchor
|
||||
{
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(anchor.tickLower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(anchor.tickUpper);
|
||||
(uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, anchor.liquidity);
|
||||
capacity += token0isWeth ? amount1 : amount0;
|
||||
}
|
||||
|
||||
// handle discovery
|
||||
{
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(discovery.tickLower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(discovery.tickUpper);
|
||||
(uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, discovery.liquidity);
|
||||
capacity += token0isWeth ? amount1 : amount0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue