diff --git a/contracts/CoverPool.sol b/contracts/CoverPool.sol index d8d9b8a0..deec64cf 100644 --- a/contracts/CoverPool.sol +++ b/contracts/CoverPool.sol @@ -30,6 +30,11 @@ contract CoverPool is _; } + modifier factoryOnly() { + _onlyFactory(); + _; + } + modifier canoncialOnly() { _onlyCanoncialClones(); _; @@ -42,8 +47,20 @@ contract CoverPool is factory = factory_; } + function initialize() + external + { + Ticks.initialize( + tickMap, + pool0, + pool1, + globalState, + ICoverPool(address(this)).immutables() + ); + } + function mint( - MintParams memory params + MintCoverParams memory params ) external override nonReentrant(globalState) canoncialOnly @@ -83,7 +100,7 @@ contract CoverPool is } function burn( - BurnParams memory params + BurnCoverParams memory params ) external override nonReentrant(globalState) canoncialOnly @@ -190,7 +207,7 @@ contract CoverPool is } function snapshot( - SnapshotParams memory params + SnapshotCoverParams memory params ) external view override returns ( CoverPosition memory ) { @@ -256,13 +273,24 @@ contract CoverPool is tickSpread(), twapLength(), auctionLength(), - blockTime(), + sampleInterval(), token0Decimals(), token1Decimals(), minAmountLowerPriced() ); } + function syncLatestTick() external view returns (int24) { + return Epochs.syncLatestTick( + globalState, + immutables() + ); + } + + function syncGlobalState() external view returns (GlobalState memory) { + return globalState; + } + function priceBounds( int16 tickSpacing ) external pure returns (uint160, uint160) { @@ -302,7 +330,9 @@ contract CoverPool is constants.poolToken, constants.inputPool, constants.bounds.min, - constants.bounds.max, + constants.bounds.max + ); + bytes memory value2 = abi.encodePacked( constants.minAmountPerAuction, constants.genesisTime, constants.minPositionWidth, @@ -310,16 +340,20 @@ contract CoverPool is constants.twapLength, constants.auctionLength ); - bytes memory value2 = abi.encodePacked( - constants.blockTime, + bytes memory value3 = abi.encodePacked( + constants.sampleInterval, constants.token0Decimals, constants.token1Decimals, constants.minAmountLowerPriced ); - return abi.encodePacked(value1, value2); + return abi.encodePacked(value1, value2, value3); } function _onlyOwner() private view { if (msg.sender != owner()) revert OwnerOnly(); } + + function _onlyFactory() private view { + if (msg.sender != factory) revert FactoryOnly(); + } } diff --git a/contracts/CoverPoolFactory.sol b/contracts/CoverPoolFactory.sol index df19080e..9f2abde3 100644 --- a/contracts/CoverPoolFactory.sol +++ b/contracts/CoverPoolFactory.sol @@ -32,7 +32,7 @@ contract CoverPoolFactory is ) { // validate token pair if (params.tokenIn == params.tokenOut || params.tokenIn == address(0) || params.tokenOut == address(0)) { - revert InvalidTokenAddress(); + require(false, "InvalidTokenAddress()"); } CoverImmutables memory constants; constants.owner = owner; @@ -46,7 +46,7 @@ contract CoverPoolFactory is uint8 token1Decimals = ERC20(constants.token1).decimals(); if (token0Decimals > 18 || token1Decimals > 18 || token0Decimals < 6 || token1Decimals < 6) { - revert InvalidTokenDecimals(); + require(false, "InvalidTokenDecimals()"); } constants.token0Decimals = token0Decimals; constants.token1Decimals = token1Decimals; @@ -55,37 +55,40 @@ contract CoverPoolFactory is // get twap source { ( - address poolImpl, - address tokenImpl, - address twapSource - ) = ICoverPoolManager(owner).poolTypes(params.poolType); - if (poolImpl == address(0) || twapSource == address(0)) revert PoolTypeNotFound(); - constants.poolImpl = poolImpl; - constants.poolToken = tokenImpl; - constants.source = ITwapSource(twapSource); + address _poolImpl, + address _tokenImpl, + address _twapSource + ) = ICoverPoolManager(owner).poolTypes(params.poolTypeId); + if (_poolImpl == address(0) || _twapSource == address(0)) + require(false, "PoolTypeNotFound()"); + constants.poolImpl = _poolImpl; + constants.poolToken = _tokenImpl; + constants.source = ITwapSource(_twapSource); } // get volatility tier config { VolatilityTier memory config = ICoverPoolManager(owner).volatilityTiers( - params.poolType, + params.poolTypeId, params.feeTier, params.tickSpread, params.twapLength ); - if (config.auctionLength == 0) revert VolatilityTierNotSupported(); + if (config.auctionLength == 0) + require(false, "VolatilityTierNotSupported()"); constants.minAmountPerAuction = config.minAmountPerAuction; constants.auctionLength = config.auctionLength; - constants.blockTime = config.blockTime; + constants.sampleInterval = config.sampleInterval; constants.minPositionWidth = config.minPositionWidth; constants.minAmountLowerPriced = config.minAmountLowerPriced; } // record genesis time constants.tickSpread = params.tickSpread; constants.twapLength = params.twapLength; - constants.genesisTime = uint32(block.timestamp); + constants.genesisTime = uint32(block.timestamp); // get reference pool constants.inputPool = ITwapSource(constants.source).getPool(constants.token0, constants.token1, params.feeTier); - + if (constants.inputPool == address(0)) + require (false, "InputPoolDoesNotExist()"); // generate key for pool bytes32 key = keccak256( abi.encode( @@ -120,6 +123,9 @@ contract CoverPoolFactory is data: encodeCover(constants) }); + // intialize twap source + ICoverPool(pool).initialize(); + poolToken = constants.poolToken; coverPools[key] = pool; @@ -129,45 +135,13 @@ contract CoverPoolFactory is constants.inputPool, constants.token0, constants.token1, - params.poolType, params.feeTier, params.tickSpread, - params.twapLength + params.twapLength, + params.poolTypeId ); } - function createCoverPoolAndMint( - CoverPoolParams memory params, - ICoverPool.MintParams[] memory mintCoverParams - ) external returns ( - address pool, - address poolToken - ) { - // check if pool exists - ( - pool, - poolToken - ) = getCoverPool( - params - ); - // create if pool doesn't exist - if (pool == address(0)) { - ( - pool, - poolToken - ) = createCoverPool( - params - ); - } - // mint initial cover positions - for (uint i = 0; i < mintCoverParams.length;) { - ICoverPool(pool).mint(mintCoverParams[i]); - unchecked { - ++i; - } - } - } - function getCoverPool( CoverPoolParams memory params ) public view override returns ( @@ -182,7 +156,7 @@ contract CoverPoolFactory is address poolImpl, address tokenImpl, address source - ) = ICoverPoolManager(owner).poolTypes(params.poolType); + ) = ICoverPoolManager(owner).poolTypes(params.poolTypeId); address inputPool = ITwapSource(source).getPool(token0, token1, params.feeTier); // generate key for pool @@ -207,6 +181,76 @@ contract CoverPoolFactory is ); } + function syncLatestTick( + CoverPoolParams memory params + ) external view returns ( + int24 latestTick, + bool inputPoolExists, + bool twapReady + ) { + if (params.tokenIn == params.tokenOut || + params.tokenIn == address(0) || + params.tokenOut == address(0)) { + return (0, false, false); + } + CoverImmutables memory constants; + // set lexographical token address ordering + constants.token0 = params.tokenIn < params.tokenOut ? params.tokenIn : params.tokenOut; + constants.token1 = params.tokenIn < params.tokenOut ? params.tokenOut : params.tokenIn; + // get twap source + { + ( + ,, + address _twapSource + ) = ICoverPoolManager(owner).poolTypes(params.poolTypeId); + if (_twapSource == address(0)) + return (0, false, false); + constants.source = ITwapSource(constants.source); + } + constants.inputPool = ITwapSource(constants.source).getPool(constants.token0, constants.token1, params.feeTier); + + if (constants.inputPool == address(0)) + return (0, false, false); + + inputPoolExists = true; + + // generate key for pool + bytes32 key = keccak256(abi.encode( + constants.token0, + constants.token1, + constants.source, + constants.inputPool, + params.tickSpread, + params.twapLength + )); + + // validate erc20 decimals + { + uint8 token0Decimals = ERC20(constants.token0).decimals(); + uint8 token1Decimals = ERC20(constants.token1).decimals(); + if (token0Decimals > 18 || token1Decimals > 18 + || token0Decimals < 6 || token1Decimals < 6) { + return (0, true, false); + } + } + // get volatility tier config + { + VolatilityTier memory config = ICoverPoolManager(owner).volatilityTiers( + params.poolTypeId, + params.feeTier, + params.tickSpread, + params.twapLength + ); + if (config.auctionLength == 0) + return (0, true, false); + constants.sampleInterval = config.sampleInterval; + } + constants.tickSpread = params.tickSpread; + constants.twapLength = params.twapLength; + + (latestTick, twapReady) = ITwapSource(constants.source).syncLatestTick(constants, coverPools[key]); + } + function encodeCover( CoverImmutables memory constants ) private pure returns (bytes memory) { @@ -218,7 +262,9 @@ contract CoverPoolFactory is constants.poolToken, constants.inputPool, constants.bounds.min, - constants.bounds.max, + constants.bounds.max + ); + bytes memory value2 = abi.encodePacked( constants.minAmountPerAuction, constants.genesisTime, constants.minPositionWidth, @@ -226,12 +272,12 @@ contract CoverPoolFactory is constants.twapLength, constants.auctionLength ); - bytes memory value2 = abi.encodePacked( - constants.blockTime, + bytes memory value3 = abi.encodePacked( + constants.sampleInterval, constants.token0Decimals, constants.token1Decimals, constants.minAmountLowerPriced ); - return abi.encodePacked(value1, value2); + return abi.encodePacked(value1, value2, value3); } } diff --git a/contracts/base/events/CoverPoolEvents.sol b/contracts/base/events/CoverPoolEvents.sol index 8e01ff49..045c2f0c 100644 --- a/contracts/base/events/CoverPoolEvents.sol +++ b/contracts/base/events/CoverPoolEvents.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.13; abstract contract CoverPoolEvents { - event Mint( + event Mint( address indexed to, int24 lower, int24 upper, diff --git a/contracts/base/events/CoverPoolFactoryEvents.sol b/contracts/base/events/CoverPoolFactoryEvents.sol index 9c4087f9..da6ec96c 100644 --- a/contracts/base/events/CoverPoolFactoryEvents.sol +++ b/contracts/base/events/CoverPoolFactoryEvents.sol @@ -7,9 +7,9 @@ abstract contract CoverPoolFactoryEvents { address indexed inputPool, address token0, address token1, - bytes32 indexed poolType, uint16 fee, int16 tickSpread, - uint16 twapLength + uint16 twapLength, + uint16 indexed poolTypeId ); } diff --git a/contracts/base/events/CoverPoolManagerEvents.sol b/contracts/base/events/CoverPoolManagerEvents.sol index 21d39dcf..5b9e1d9a 100644 --- a/contracts/base/events/CoverPoolManagerEvents.sol +++ b/contracts/base/events/CoverPoolManagerEvents.sol @@ -4,23 +4,24 @@ pragma solidity 0.8.13; abstract contract CoverPoolManagerEvents { event FactoryChanged(address indexed previousFactory, address indexed newFactory); event VolatilityTierEnabled( - bytes32 poolType, + uint16 poolTypeId, uint16 feeTier, int16 tickSpread, uint16 twapLength, uint128 minAmountPerAuction, uint16 auctionLength, - uint16 blockTime, + uint16 sampleInterval, uint16 syncFee, uint16 fillFee, int16 minPositionWidth, bool minLowerPriced ); event PoolTypeEnabled( - bytes32 poolType, + bytes32 poolTypeName, address implAddress, address sourceAddress, - address factoryAddress + address factoryAddress, + uint16 poolTypeId ); event FeeToTransfer(address indexed previousFeeTo, address indexed newFeeTo); event OwnerTransfer(address indexed previousOwner, address indexed newOwner); diff --git a/contracts/base/events/TwapSourceEvents.sol b/contracts/base/events/TwapSourceEvents.sol new file mode 100644 index 00000000..11eab712 --- /dev/null +++ b/contracts/base/events/TwapSourceEvents.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.13; + +abstract contract TwapSourceEvents { + event SampleCountInitialized ( + address indexed coverPool, + uint16 sampleCount, + uint16 sampleCountMax, + uint16 sampleCountRequired + ); +} diff --git a/contracts/base/storage/CoverPoolImmutables.sol b/contracts/base/storage/CoverPoolImmutables.sol index 2ee2a265..6e604826 100644 --- a/contracts/base/storage/CoverPoolImmutables.sol +++ b/contracts/base/storage/CoverPoolImmutables.sol @@ -60,7 +60,7 @@ contract CoverPoolImmutables is Clone { return _getArgUint16(186); } - function blockTime() public pure returns (uint16) { + function sampleInterval() public pure returns (uint16) { return _getArgUint16(188); } diff --git a/contracts/interfaces/callbacks/ICoverPoolSwapCallback.sol b/contracts/interfaces/callbacks/ICoverPoolCallback.sol similarity index 58% rename from contracts/interfaces/callbacks/ICoverPoolSwapCallback.sol rename to contracts/interfaces/callbacks/ICoverPoolCallback.sol index 4bf4f022..bd33aa50 100644 --- a/contracts/interfaces/callbacks/ICoverPoolSwapCallback.sol +++ b/contracts/interfaces/callbacks/ICoverPoolCallback.sol @@ -1,6 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.13; +/// @title Callback for mints +/// @notice Any contract that calls the `mint` function must implement this interface. +interface ICoverPoolMintCallback { + /// @notice Called to `msg.sender` after executing a mint. + /// @param amount0Delta The amount of token0 either received by (positive) or sent from (negative) the user. + /// @param amount1Delta The amount of token1 either received by (positive) or sent from (negative) the user. + function coverPoolMintCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} + + /// @title Callback for swaps /// @notice Any contract that calls the `swap` function must implement this interface. interface ICoverPoolSwapCallback { diff --git a/contracts/interfaces/callbacks/ILimitPoolSwapCallback.sol b/contracts/interfaces/callbacks/ILimitPoolCallback.sol similarity index 58% rename from contracts/interfaces/callbacks/ILimitPoolSwapCallback.sol rename to contracts/interfaces/callbacks/ILimitPoolCallback.sol index fb0a99ac..f98d56c7 100644 --- a/contracts/interfaces/callbacks/ILimitPoolSwapCallback.sol +++ b/contracts/interfaces/callbacks/ILimitPoolCallback.sol @@ -1,6 +1,19 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.13; +/// @title Callback for mints +/// @notice Any contract that calls the `mint` function must implement this interface. +interface ILimitPoolMintCallback { + /// @notice Called to `msg.sender` after executing a mint. + /// @param amount0Delta The amount of token0 either received by (positive) or sent from (negative) the user. + /// @param amount1Delta The amount of token1 either received by (positive) or sent from (negative) the user. + function limitPoolMintCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} + /// @title Callback for swaps /// @notice Any contract that calls the `swap` function must implement this interface. interface ILimitPoolSwapCallback { diff --git a/contracts/interfaces/cover/ICoverPool.sol b/contracts/interfaces/cover/ICoverPool.sol index d1cc2167..27fb8430 100644 --- a/contracts/interfaces/cover/ICoverPool.sol +++ b/contracts/interfaces/cover/ICoverPool.sol @@ -11,46 +11,9 @@ import '../structs/PoolsharkStructs.sol'; */ interface ICoverPool is CoverPoolStructs { /** - * @custom:struct MintParams + * @notice Initializes the TWAP source */ - struct MintParams { - /** - * @custom:field to - * @notice Address for the receiver of the minted position - */ - address to; - - /** - * @custom:field amount - * @notice Token amount to be deposited into the minted position - */ - uint128 amount; - - /** - * @custom:field positionId - * @notice 0 if creating a new position; id of previous if adding liquidity - */ - uint32 positionId; - - /** - * @custom:field lower - * @notice The lower price tick for the position range - */ - int24 lower; - - /** - * @custom:field upper - * @notice The upper price tick for the position range - */ - int24 upper; - - /** - * @custom:field zeroForOne - * @notice True if depositing token0, the first token address in lexographical order - * @notice False if depositing token1, the second token address in lexographical order - */ - bool zeroForOne; - } + function initialize() external; /** * @notice Deposits `amountIn` of asset to be auctioned off each time price range is crossed further into. @@ -59,60 +22,12 @@ interface ICoverPool is CoverPoolStructs { the user's liquidity within each tick spacing is auctioned off. * @dev The position will be shrunk onto the correct side of latestTick. * @dev The position will be minted with the `to` address as the owner. - * @param params The parameters for the function. See MintParams above. + * @param params The parameters for the function. See MintCoverParams. */ function mint( - MintParams memory params + MintCoverParams memory params ) external; - /** - * @custom:struct BurnParams - */ - struct BurnParams { - /** - * @custom:field to - * @notice Address for the receiver of the collected position amounts - */ - address to; - - /** - * @custom:field burnPercent - * @notice Percent of the remaining liquidity to be removed - * @notice 1e38 represents 100% - * @notice 5e37 represents 50% - * @notice 1e37 represents 10% - */ - uint128 burnPercent; - - /** - * @custom:field positionId - * @notice 0 if creating a new position; id of previous if adding liquidity - */ - uint32 positionId; - - /** - * @custom:field claim - * @notice The most recent tick crossed in this range - * @notice if `zeroForOne` is true, claim tick progresses from upper => lower - * @notice if `zeroForOne` is false, claim tick progresses from lower => upper - */ - int24 claim; - - /** - * @custom:field zeroForOne - * @notice True if deposited token0, the first token address in lexographical order - * @notice False if deposited token1, the second token address in lexographical order - */ - bool zeroForOne; - - /** - * @custom:field sync - * @notice True will sync the pool latestTick - * @notice False will skip syncing latestTick - */ - bool sync; - } - /** * @notice Withdraws the input token and returns any filled and/or unfilled amounts to the 'to' address specified. * - E.g. User supplies 1 WETH in the range 1500 USDC per WETH to 1400 USDC per WETH @@ -121,10 +36,10 @@ interface ICoverPool is CoverPoolStructs { * @dev The position will be shrunk based on the claim tick passed. * @dev The position amounts will be returned to the `to` address specified. * @dev The `sync` flag can be set to false so users can exit safely without syncing latestTick. - * @param params The parameters for the function. See BurnParams above. + * @param params The parameters for the function. See BurnCoverParams. */ function burn( - BurnParams memory params + BurnCoverParams memory params ) external; /** @@ -134,7 +49,7 @@ interface ICoverPool is CoverPoolStructs { The pool price represents token1 per token0. The pool price will decrease if `zeroForOne` is true. The pool price will increase if `zeroForOne` is false. - * @param params The parameters for the function. See SwapParams above. + * @param params The parameters for the function. See SwapParams. * @return amount0Delta The amount of token0 spent (negative) or received (positive) by the user * @return amount1Delta The amount of token1 spent (negative) or received (positive) by the user */ @@ -165,47 +80,6 @@ interface ICoverPool is CoverPoolStructs { uint256 priceAfter ); - /** - * @custom:struct SnapshotParams - */ - struct SnapshotParams { - /** - * @custom:field to - * @notice Address of the position owner - */ - address owner; - - /** - * @custom:field positionId - * @notice id of position - */ - uint32 positionId; - - /** - * @custom:field burnPercent - * @notice Percent of the remaining liquidity to be removed - * @notice 1e38 represents 100% - * @notice 5e37 represents 50% - * @notice 1e37 represents 10% - */ - uint128 burnPercent; - - /** - * @custom:field claim - * @notice The most recent tick crossed in this range - * @notice if `zeroForOne` is true, claim tick progresses from upper => lower - * @notice if `zeroForOne` is false, claim tick progresses from lower => upper - */ - int24 claim; - - /** - * @custom:field zeroForOne - * @notice True if deposited token0, the first token address in lexographical order - * @notice False if deposited token1, the second token address in lexographical order - */ - bool zeroForOne; - } - /** * @notice Snapshots the current state of an existing position. * @param params The parameters for the function. See SwapParams above. @@ -213,7 +87,7 @@ interface ICoverPool is CoverPoolStructs { * @dev positions amounts reflected will be collected by the user if `burn` is called */ function snapshot( - SnapshotParams memory params + SnapshotCoverParams memory params ) external view returns ( CoverPosition memory position ); @@ -242,10 +116,20 @@ interface ICoverPool is CoverPoolStructs { CoverImmutables memory constants ); + function syncLatestTick( + ) external view returns ( + int24 newLatestTick + ); + function priceBounds( int16 tickSpacing ) external pure returns ( uint160 minPrice, uint160 maxPrice ); + + function syncGlobalState( + ) external view returns ( + GlobalState memory state + ); } diff --git a/contracts/interfaces/cover/ICoverPoolFactory.sol b/contracts/interfaces/cover/ICoverPoolFactory.sol index b9412b71..af40484d 100644 --- a/contracts/interfaces/cover/ICoverPoolFactory.sol +++ b/contracts/interfaces/cover/ICoverPoolFactory.sol @@ -6,12 +6,12 @@ import '../../base/storage/CoverPoolFactoryStorage.sol'; abstract contract ICoverPoolFactory is CoverPoolFactoryStorage { struct CoverPoolParams { - bytes32 poolType; address tokenIn; address tokenOut; uint16 feeTier; int16 tickSpread; uint16 twapLength; + uint16 poolTypeId; } /** diff --git a/contracts/interfaces/cover/ICoverPoolManager.sol b/contracts/interfaces/cover/ICoverPoolManager.sol index bb57accf..a896d4b1 100644 --- a/contracts/interfaces/cover/ICoverPoolManager.sol +++ b/contracts/interfaces/cover/ICoverPoolManager.sol @@ -8,14 +8,14 @@ interface ICoverPoolManager is CoverPoolStructs { function owner() external view returns (address); function feeTo() external view returns (address); function poolTypes( - bytes32 poolType + uint16 poolTypeId ) external view returns ( address poolImpl, address tokenImpl, address twapImpl ); function volatilityTiers( - bytes32 implName, + uint16 poolTypeId, uint16 feeTier, int16 tickSpread, uint16 twapLength diff --git a/contracts/interfaces/external/poolshark/range/IRangePool.sol b/contracts/interfaces/external/poolshark/range/IRangePool.sol deleted file mode 100644 index bac966ff..00000000 --- a/contracts/interfaces/external/poolshark/range/IRangePool.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.13; - -interface IRangePool { - - struct ProtocolFees { - uint128 token0; - uint128 token1; - } - - struct SampleState { - uint16 index; - uint16 length; - uint16 lengthNext; - } - - function sample( - uint32[] memory secondsAgo - ) external view returns ( - int56[] memory tickSecondsAccum, - uint160[] memory secondsPerLiquidityAccum, - uint160 averagePrice, - uint128 averageLiquidity, - int24 averageTick - ); - - function increaseSampleLength( - uint16 sampleLengthNext - ) external; - - function poolState() external view returns ( - uint8, - uint16, - int24, - int56, - uint160, - uint160, - uint128, - uint128, - uint200, - uint200, - SampleState memory, - ProtocolFees memory - ); -} diff --git a/contracts/interfaces/external/poolshark/range/IRangePoolFactory.sol b/contracts/interfaces/external/poolshark/range/IRangePoolFactory.sol deleted file mode 100644 index d5ada067..00000000 --- a/contracts/interfaces/external/poolshark/range/IRangePoolFactory.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPLv3 -pragma solidity 0.8.13; - -interface IRangePoolFactory { - function getRangePool( - address fromToken, - address destToken, - uint16 fee - ) external view returns (address); -} diff --git a/contracts/interfaces/external/poolshark/range/IRangePoolManager.sol b/contracts/interfaces/external/poolshark/range/IRangePoolManager.sol deleted file mode 100644 index 6c8b3ef7..00000000 --- a/contracts/interfaces/external/poolshark/range/IRangePoolManager.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.13; - -interface IRangePoolManager { - function owner() external view returns (address); - function feeTo() external view returns (address); - function protocolFees(address pool) external view returns (uint16); - function feeTiers(uint16 swapFee) external view returns (int24); -} diff --git a/contracts/interfaces/limit/ILimitPool.sol b/contracts/interfaces/limit/ILimitPool.sol index 48f4e579..54cb05a7 100644 --- a/contracts/interfaces/limit/ILimitPool.sol +++ b/contracts/interfaces/limit/ILimitPool.sol @@ -4,20 +4,65 @@ pragma solidity 0.8.13; import '../structs/PoolsharkStructs.sol'; interface ILimitPool is PoolsharkStructs { - function immutables() external view returns (LimitImmutables memory); - - function swap( - SwapParams memory params + function initialize( + uint160 startPrice + ) external; + + function mintLimit( + MintLimitParams memory params + ) external; + + function burnLimit( + BurnLimitParams memory params + ) external; + + function snapshotLimit( + SnapshotLimitParams memory params + ) external view returns( + uint128, + uint128 + ); + + function fees( + FeesParams memory params ) external returns ( - int256 amount0, - int256 amount1 + uint128 token0Fees, + uint128 token1Fees + ); + + function immutables( + ) external view returns( + LimitImmutables memory + ); + + function priceBounds( + int16 tickSpacing + ) external pure returns ( + uint160 minPrice, + uint160 maxPrice ); - function quote( - QuoteParams memory params + function sample( + uint32[] memory secondsAgo ) external view returns ( - int256 inAmount, - int256 outAmount, - uint160 priceAfter + int56[] memory tickSecondsAccum, + uint160[] memory secondsPerLiquidityAccum, + uint160 averagePrice, + uint128 averageLiquidity, + int24 averageTick + ); + + function increaseSampleCount( + uint16 newSampleCountMax + ) external; + + function globalState() external view returns ( + RangePoolState memory pool, + LimitPoolState memory pool0, + LimitPoolState memory pool1, + uint128 liquidityGlobal, + uint32 positionIdNext, + uint32 epoch, + uint8 unlocked ); -} +} \ No newline at end of file diff --git a/contracts/interfaces/limit/ILimitPoolFactory.sol b/contracts/interfaces/limit/ILimitPoolFactory.sol new file mode 100644 index 00000000..679e6f3c --- /dev/null +++ b/contracts/interfaces/limit/ILimitPoolFactory.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.13; + +import '../structs/PoolsharkStructs.sol'; + +abstract contract ILimitPoolFactory is PoolsharkStructs { + function createLimitPool( + LimitPoolParams memory params + ) external virtual returns ( + address pool, + address poolToken + ); + + function getLimitPool( + address tokenIn, + address tokenOut, + uint16 swapFee, + uint16 poolTypeId + ) external view virtual returns ( + address pool, + address poolToken + ); +} diff --git a/contracts/interfaces/limit/ILimitPoolManager.sol b/contracts/interfaces/limit/ILimitPoolManager.sol new file mode 100644 index 00000000..7ca7f5b2 --- /dev/null +++ b/contracts/interfaces/limit/ILimitPoolManager.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.13; + +/// @notice LimitPoolManager interface +interface ILimitPoolManager { + function owner() external view returns (address); + function feeTo() external view returns (address); + function poolTypes( + uint16 poolType + ) external view returns ( + address poolImpl, + address tokenImpl + ); + function feeTiers( + uint16 swapFee + ) external view returns ( + int16 tickSpacing + ); +} diff --git a/contracts/interfaces/modules/sources/ITwapSource.sol b/contracts/interfaces/modules/sources/ITwapSource.sol index 20a335ab..9cc72992 100644 --- a/contracts/interfaces/modules/sources/ITwapSource.sol +++ b/contracts/interfaces/modules/sources/ITwapSource.sol @@ -18,6 +18,20 @@ interface ITwapSource { int24 averageTick ); + function calculateAverageTicks( + PoolsharkStructs.CoverImmutables memory constants + ) external view returns ( + int24[4] memory averageTicks + ); + + function syncLatestTick( + PoolsharkStructs.CoverImmutables memory constants, + address coverPool + ) external view returns ( + int24 latestTick, + bool twapReady + ); + function getPool( address tokenA, address tokenB, @@ -33,5 +47,5 @@ interface ITwapSource { ); function factory() - external view returns (address); + external view returns (address); } diff --git a/contracts/interfaces/range/IRangePool.sol b/contracts/interfaces/range/IRangePool.sol new file mode 100644 index 00000000..ce0f3d08 --- /dev/null +++ b/contracts/interfaces/range/IRangePool.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.13; + +import '../structs/PoolsharkStructs.sol'; + +interface IRangePool is PoolsharkStructs { + function mintRange( + MintRangeParams memory mintParams + ) external; + + function burnRange( + BurnRangeParams memory burnParams + ) external; + + function swap( + SwapParams memory params + ) external returns ( + int256 amount0, + int256 amount1 + ); + + function quote( + QuoteParams memory params + ) external view returns ( + uint256 inAmount, + uint256 outAmount, + uint160 priceAfter + ); + + function snapshotRange( + uint32 positionId + ) external view returns( + int56 tickSecondsAccum, + uint160 secondsPerLiquidityAccum, + uint128 feesOwed0, + uint128 feesOwed1 + ); + + function increaseSampleCount( + uint16 newSampleCountMax + ) external; +} diff --git a/contracts/interfaces/structs/CoverPoolStructs.sol b/contracts/interfaces/structs/CoverPoolStructs.sol index 8501458f..1abb7854 100644 --- a/contracts/interfaces/structs/CoverPoolStructs.sol +++ b/contracts/interfaces/structs/CoverPoolStructs.sol @@ -66,7 +66,7 @@ interface CoverPoolStructs is PoolsharkStructs { struct VolatilityTier { uint128 minAmountPerAuction; // based on 18 decimals and then converted based on token decimals uint16 auctionLength; - uint16 blockTime; // average block time where 1e3 is 1 second + uint16 sampleInterval; // average block time where 1e3 is 1 second uint16 syncFee; uint16 fillFee; int16 minPositionWidth; diff --git a/contracts/interfaces/structs/PoolsharkStructs.sol b/contracts/interfaces/structs/PoolsharkStructs.sol index 698146c9..565e21f4 100644 --- a/contracts/interfaces/structs/PoolsharkStructs.sol +++ b/contracts/interfaces/structs/PoolsharkStructs.sol @@ -32,7 +32,7 @@ interface PoolsharkStructs { int16 tickSpread; uint16 twapLength; uint16 auctionLength; - uint16 blockTime; + uint16 sampleInterval; uint8 token0Decimals; uint8 token1Decimals; bool minAmountLowerPriced; @@ -50,6 +50,229 @@ interface PoolsharkStructs { uint160 priceAfter; } + struct LimitPoolParams { + address tokenIn; + address tokenOut; + uint160 startPrice; + uint16 swapFee; + uint16 poolTypeId; + } + + struct MintLimitParams { + address to; + uint128 amount; + uint96 mintPercent; + uint32 positionId; + int24 lower; + int24 upper; + bool zeroForOne; + bytes callbackData; + } + + struct BurnLimitParams { + address to; + uint128 burnPercent; + uint32 positionId; + int24 claim; + bool zeroForOne; + } + + struct MintRangeParams { + address to; + int24 lower; + int24 upper; + uint32 positionId; + uint128 amount0; + uint128 amount1; + bytes callbackData; + } + + struct BurnRangeParams { + address to; + uint32 positionId; + uint128 burnPercent; + } + + struct SnapshotLimitParams { + address owner; + uint128 burnPercent; + uint32 positionId; + int24 claim; + bool zeroForOne; + } + + struct FeesParams { + uint16 protocolSwapFee0; + uint16 protocolSwapFee1; + uint16 protocolFillFee0; + uint16 protocolFillFee1; + uint8 setFeesFlags; + } + + struct LimitPoolState { + uint160 price; /// @dev Starting price current + uint128 liquidity; /// @dev Liquidity currently active + uint128 protocolFees; + uint16 protocolFillFee; + int24 tickAtPrice; + } + + struct RangePoolState { + SampleState samples; + uint200 feeGrowthGlobal0; + uint200 feeGrowthGlobal1; + uint160 secondsPerLiquidityAccum; + uint160 price; /// @dev Starting price current + uint128 liquidity; /// @dev Liquidity currently active + int56 tickSecondsAccum; + int24 tickAtPrice; + uint16 protocolSwapFee0; + uint16 protocolSwapFee1; + } + + struct SampleState { + uint16 index; + uint16 count; + uint16 countMax; + } + + /** + * @custom:struct MintCoverParams + */ + struct MintCoverParams { + /** + * @custom:field to + * @notice Address for the receiver of the minted position + */ + address to; + + /** + * @custom:field amount + * @notice Token amount to be deposited into the minted position + */ + uint128 amount; + + /** + * @custom:field positionId + * @notice 0 if creating a new position; id of previous if adding liquidity + */ + uint32 positionId; + + /** + * @custom:field lower + * @notice The lower price tick for the position range + */ + int24 lower; + + /** + * @custom:field upper + * @notice The upper price tick for the position range + */ + int24 upper; + + /** + * @custom:field zeroForOne + * @notice True if depositing token0, the first token address in lexographical order + * @notice False if depositing token1, the second token address in lexographical order + */ + bool zeroForOne; + + /** + * @custom:field callbackData + * @notice callback data which gets passed back to msg.sender at the end of a `mint` call + */ + bytes callbackData; + } + + /** + * @custom:struct BurnCoverParams + */ + struct BurnCoverParams { + /** + * @custom:field to + * @notice Address for the receiver of the collected position amounts + */ + address to; + + /** + * @custom:field burnPercent + * @notice Percent of the remaining liquidity to be removed + * @notice 1e38 represents 100% + * @notice 5e37 represents 50% + * @notice 1e37 represents 10% + */ + uint128 burnPercent; + + /** + * @custom:field positionId + * @notice 0 if creating a new position; id of previous if adding liquidity + */ + uint32 positionId; + + /** + * @custom:field claim + * @notice The most recent tick crossed in this range + * @notice if `zeroForOne` is true, claim tick progresses from upper => lower + * @notice if `zeroForOne` is false, claim tick progresses from lower => upper + */ + int24 claim; + + /** + * @custom:field zeroForOne + * @notice True if deposited token0, the first token address in lexographical order + * @notice False if deposited token1, the second token address in lexographical order + */ + bool zeroForOne; + + /** + * @custom:field sync + * @notice True will sync the pool latestTick + * @notice False will skip syncing latestTick + */ + bool sync; + } + + /** + * @custom:struct SnapshotCoverParams + */ + struct SnapshotCoverParams { + /** + * @custom:field to + * @notice Address of the position owner + */ + address owner; + + /** + * @custom:field positionId + * @notice id of position + */ + uint32 positionId; + + /** + * @custom:field burnPercent + * @notice Percent of the remaining liquidity to be removed + * @notice 1e38 represents 100% + * @notice 5e37 represents 50% + * @notice 1e37 represents 10% + */ + uint128 burnPercent; + + /** + * @custom:field claim + * @notice The most recent tick crossed in this range + * @notice if `zeroForOne` is true, claim tick progresses from upper => lower + * @notice if `zeroForOne` is false, claim tick progresses from lower => upper + */ + int24 claim; + + /** + * @custom:field zeroForOne + * @notice True if deposited token0, the first token address in lexographical order + * @notice False if deposited token1, the second token address in lexographical order + */ + bool zeroForOne; + } + /** * @custom:struct QuoteParams */ diff --git a/contracts/libraries/Epochs.sol b/contracts/libraries/Epochs.sol index c07f00be..8606beab 100644 --- a/contracts/libraries/Epochs.sol +++ b/contracts/libraries/Epochs.sol @@ -144,6 +144,15 @@ library Epochs { return (state, cache.syncFees, pool0, pool1); } + function syncLatestTick( + CoverPoolStructs.GlobalState memory state, + PoolsharkStructs.CoverImmutables memory constants + ) external view returns ( + int24 newLatestTick + ) { + (newLatestTick,) = _syncTick(state, constants); + } + function syncLatest( mapping(int24 => CoverPoolStructs.Tick) storage ticks, CoverPoolStructs.TickMap storage tickMap, @@ -372,7 +381,7 @@ library Epochs { else auctionsElapsed = type(int32).max - 1; - // if 3/4 of twapLength or auctionLength has passed allow for latestTick move + // if 3/4 of auctionLength or auctionLength has passed allow for latestTick move if (timeElapsed > 3 * constants.twapLength / 4 || timeElapsed > constants.auctionLength) auctionsElapsed += 1; if (auctionsElapsed < 1) { diff --git a/contracts/libraries/Positions.sol b/contracts/libraries/Positions.sol index af48d934..f61f403d 100644 --- a/contracts/libraries/Positions.sol +++ b/contracts/libraries/Positions.sol @@ -49,11 +49,11 @@ library Positions { function resize( CoverPoolStructs.CoverPosition memory position, - ICoverPool.MintParams memory params, + PoolsharkStructs.MintCoverParams memory params, CoverPoolStructs.GlobalState memory state, PoolsharkStructs.CoverImmutables memory constants ) internal pure returns ( - ICoverPool.MintParams memory, + PoolsharkStructs.MintCoverParams memory, uint256 ) { diff --git a/contracts/libraries/pool/BurnCall.sol b/contracts/libraries/pool/BurnCall.sol index 093474e6..94004c78 100644 --- a/contracts/libraries/pool/BurnCall.sol +++ b/contracts/libraries/pool/BurnCall.sol @@ -33,7 +33,7 @@ library BurnCall { CoverPoolStructs.GlobalState storage globalState, CoverPoolStructs.PoolState storage pool0, CoverPoolStructs.PoolState storage pool1, - ICoverPool.BurnParams memory params, + PoolsharkStructs.BurnCoverParams memory params, CoverPoolStructs.BurnCache memory cache ) external returns (CoverPoolStructs.BurnCache memory) { cache.position = positions[params.positionId]; diff --git a/contracts/libraries/pool/MintCall.sol b/contracts/libraries/pool/MintCall.sol index 9276a218..f8179c39 100644 --- a/contracts/libraries/pool/MintCall.sol +++ b/contracts/libraries/pool/MintCall.sol @@ -2,11 +2,15 @@ pragma solidity 0.8.13; import '../../interfaces/structs/CoverPoolStructs.sol'; +import '../../interfaces/callbacks/ICoverPoolCallback.sol'; +import '../../interfaces/IERC20Minimal.sol'; import '../Positions.sol'; import '../utils/PositionTokens.sol'; import '../utils/Collect.sol'; +import 'hardhat/console.sol'; library MintCall { + using SafeCast for uint128; event Mint( address indexed to, int24 lower, @@ -28,13 +32,10 @@ library MintCall { CoverPoolStructs.GlobalState storage globalState, CoverPoolStructs.PoolState storage pool0, CoverPoolStructs.PoolState storage pool1, - ICoverPool.MintParams memory params, + PoolsharkStructs.MintCoverParams memory params, CoverPoolStructs.MintCache memory cache ) external returns (CoverPoolStructs.MintCache memory) { if (params.positionId > 0) { - if (PositionTokens.balanceOf(cache.constants, msg.sender, params.positionId) == 0) - // check for balance held - require(false, 'PositionNotFound()'); // load existing position cache.position = positions[params.positionId]; } @@ -59,10 +60,7 @@ library MintCall { // save global state to protect against reentrancy save(cache, globalState, pool0, pool1); // params.amount must be > 0 here - SafeTransfers.transferIn(params.zeroForOne ? cache.constants.token0 - : cache.constants.token1, - params.amount - ); + (cache.state, cache.position) = Positions.add( cache.position, ticks, @@ -80,7 +78,11 @@ library MintCall { cache.constants ); positions[params.positionId] = cache.position; + + // save state for reentrancy protection save(cache, globalState, pool0, pool1); + + // collect sync fees Collect.mint( cache, CoverPoolStructs.CollectParams( @@ -93,6 +95,18 @@ library MintCall { params.zeroForOne ) ); + + // check balance and execute callback + uint256 balanceStart = balance(params, cache); + ICoverPoolMintCallback(msg.sender).coverPoolMintCallback( + params.zeroForOne ? -int256(uint256(params.amount)) : int256(0), + params.zeroForOne ? int256(0) : -int256(uint256(params.amount)), + params.callbackData + ); + + // check balance requirements after callback + if (balance(params, cache) < balanceStart + params.amount) + require(false, 'MintInputAmountTooLow()'); return cache; } @@ -126,4 +140,23 @@ library MintCall { pool1.amountInDeltaMaxClaimed = cache.pool1.amountInDeltaMaxClaimed; pool1.amountOutDeltaMaxClaimed = cache.pool1.amountOutDeltaMaxClaimed; } + + function balance( + PoolsharkStructs.MintCoverParams memory params, + CoverPoolStructs.MintCache memory cache + ) private view returns (uint256) { + ( + bool success, + bytes memory data + ) = (params.zeroForOne ? cache.constants.token0 + : cache.constants.token1) + .staticcall( + abi.encodeWithSelector( + IERC20Minimal.balanceOf.selector, + address(this) + ) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } } diff --git a/contracts/libraries/pool/SwapCall.sol b/contracts/libraries/pool/SwapCall.sol index 4e9ddcb5..160fa282 100644 --- a/contracts/libraries/pool/SwapCall.sol +++ b/contracts/libraries/pool/SwapCall.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.13; import '../../interfaces/structs/CoverPoolStructs.sol'; import '../../interfaces/IERC20Minimal.sol'; -import '../../interfaces/callbacks/ICoverPoolSwapCallback.sol'; +import '../../interfaces/callbacks/ICoverPoolCallback.sol'; import '../Epochs.sol'; import '../Positions.sol'; import '../utils/Collect.sol'; diff --git a/contracts/libraries/sources/PoolsharkLimitSource.sol b/contracts/libraries/sources/PoolsharkLimitSource.sol new file mode 100644 index 00000000..13620cf0 --- /dev/null +++ b/contracts/libraries/sources/PoolsharkLimitSource.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.13; + +import '../../interfaces/limit/ILimitPoolManager.sol'; +import '../../interfaces/limit/ILimitPoolFactory.sol'; +import '../../interfaces/limit/ILimitPool.sol'; +import '../../base/events/TwapSourceEvents.sol'; +import '../../interfaces/cover/ICoverPool.sol'; +import '../../interfaces/structs/CoverPoolStructs.sol'; +import '../../interfaces/modules/sources/ITwapSource.sol'; +import '../math/ConstantProduct.sol'; + +contract PoolsharkLimitSource is ITwapSource, TwapSourceEvents { + error WaitUntilBelowMaxTick(); + error WaitUntilAboveMinTick(); + + // poolType on limitPoolFactory + uint16 public immutable poolType; + address public immutable limitPoolFactory; + address public immutable limitPoolManager; + uint16 public constant oneSecond = 1000; + + constructor( + address _limitPoolFactory, + address _limitPoolManager, + uint16 _poolType + ) { + limitPoolFactory = _limitPoolFactory; + limitPoolManager = _limitPoolManager; + poolType = _poolType; + } + + function initialize( + PoolsharkStructs.CoverImmutables memory constants + ) external returns ( + uint8 initializable, + int24 startingTick + ) + { + // get the number of samples covered by the twapLength + uint16 samplesRequired = uint16(constants.twapLength) * oneSecond / constants.sampleInterval; + ( + uint16 sampleCount, + uint16 sampleCountMax + ) = _getSampleCount(constants.inputPool); + + emit SampleCountInitialized ( + msg.sender, + sampleCount, + sampleCountMax, + samplesRequired + ); + + if (sampleCountMax < samplesRequired) { + _increaseSampleCount(constants.inputPool, samplesRequired); + return (0, 0); + } else if (sampleCount < samplesRequired) { + return (0, 0); + } + // ready to initialize + initializable = 1; + int24[4] memory averageTicks = calculateAverageTicks(constants); + // take the average of the 4 samples as a starting tick + startingTick = (averageTicks[0] + averageTicks[1] + averageTicks[2] + averageTicks[3]) / 4; + } + + function factory() external view returns (address) { + return limitPoolFactory; + } + + function feeTierTickSpacing( + uint16 feeTier + ) external view returns ( + int24 + ) + { + return int24(ILimitPoolManager(limitPoolManager).feeTiers(feeTier)); + } + + function getPool( + address token0, + address token1, + uint16 feeTier + ) external view returns( + address pool + ) { + (pool,) = ILimitPoolFactory(limitPoolFactory).getLimitPool(token0, token1, feeTier, poolType); + } + + function calculateAverageTick( + PoolsharkStructs.CoverImmutables memory constants, + int24 latestTick + ) external view returns ( + int24 averageTick + ) + { + int24[4] memory averageTicks = calculateAverageTicks(constants); + int24 minTickVariance = ConstantProduct.maxTick(constants.tickSpread) * 2; + for (uint i; i < 4; i++) { + int24 absTickVariance = latestTick - averageTicks[i] >= 0 ? latestTick - averageTicks[i] + : averageTicks[i] - latestTick; + if (absTickVariance <= minTickVariance) { + /// @dev - averageTick has the least possible variance from latestTick + minTickVariance = absTickVariance; + averageTick = averageTicks[i]; + } + } + } + + function calculateAverageTicks( + PoolsharkStructs.CoverImmutables memory constants + ) public view returns ( + int24[4] memory averageTicks + ) + { + uint32[] memory secondsAgos = new uint32[](4); + /// @dev - take 4 samples + /// @dev - twapLength must be >= 5 * sampleInterval + uint32 timeDelta = constants.sampleInterval / oneSecond == 0 ? 2 + : constants.sampleInterval * 2 / oneSecond; + secondsAgos[0] = 0; + secondsAgos[1] = timeDelta; + secondsAgos[2] = constants.twapLength - timeDelta; + secondsAgos[3] = constants.twapLength; + (int56[] memory tickSecondsAccum,,,,) = ILimitPool(constants.inputPool).sample(secondsAgos); + + /// @dev take the smallest absolute value of 4 samples + averageTicks[0] = int24(((tickSecondsAccum[0] - tickSecondsAccum[2]) / (int32(secondsAgos[2] - secondsAgos[0])))); + averageTicks[1] = int24(((tickSecondsAccum[0] - tickSecondsAccum[3]) / (int32(secondsAgos[3] - secondsAgos[0])))); + averageTicks[2] = int24(((tickSecondsAccum[1] - tickSecondsAccum[2]) / (int32(secondsAgos[2] - secondsAgos[1])))); + averageTicks[3] = int24(((tickSecondsAccum[1] - tickSecondsAccum[3]) / (int32(secondsAgos[3] - secondsAgos[1])))); + + // make sure all samples fit within min/max bounds + int24 minAverageTick = ConstantProduct.minTick(constants.tickSpread) + constants.tickSpread; + int24 maxAverageTick = ConstantProduct.maxTick(constants.tickSpread) - constants.tickSpread; + for (uint i; i < 4; i++) { + if (averageTicks[i] < minAverageTick) + averageTicks[i] = minAverageTick; + if (averageTicks[i] > maxAverageTick) + averageTicks[i] = maxAverageTick; + } + } + + function syncLatestTick( + PoolsharkStructs.CoverImmutables memory constants, + address coverPool + ) external view returns ( + int24 latestTick, + bool twapReady + ) { + if (constants.inputPool == address(0)) + return (0, false); + ( + uint16 sampleCount, + uint16 sampleCountMax + ) = _getSampleCount(constants.inputPool); + + // check twap readiness + uint16 samplesRequired = uint16(constants.twapLength) * + oneSecond / constants.sampleInterval; + if (sampleCountMax < samplesRequired) { + return (0, false); + } else if (sampleCount < samplesRequired) { + return (0, false); + } + // ready to initialize + twapReady = true; + + // if pool exists check unlocked state + uint8 unlockedState = 0; + if (coverPool != address(0)) { + CoverPoolStructs.GlobalState memory state = ICoverPool(coverPool).syncGlobalState(); + unlockedState = state.unlocked; + } + if (unlockedState == 0) { + // pool uninitialized + int24[4] memory averageTicks = calculateAverageTicks(constants); + // take the average of the 4 samples as a starting tick + latestTick = (averageTicks[0] + averageTicks[1] + averageTicks[2] + averageTicks[3]) / 4; + latestTick = (latestTick / int24(constants.tickSpread)) * int24(constants.tickSpread); + } else { + // pool initialized + latestTick = ICoverPool(coverPool).syncLatestTick(); + } + } + + function _getSampleCount( + address pool + ) internal view returns ( + uint16 sampleCount, + uint16 sampleCountMax + ) + { + ( + ILimitPool.RangePoolState memory poolState, + ,,,,, + ) = ILimitPool(pool).globalState(); + + sampleCount = poolState.samples.count; + sampleCountMax = poolState.samples.countMax; + } + + function _increaseSampleCount(address pool, uint32 blockCount) internal { + ILimitPool(pool).increaseSampleCount(uint16(blockCount)); + } +} diff --git a/contracts/libraries/sources/PoolsharkRangeSource.sol b/contracts/libraries/sources/PoolsharkRangeSource.sol deleted file mode 100644 index 386626bb..00000000 --- a/contracts/libraries/sources/PoolsharkRangeSource.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.13; - -import '../../interfaces/external/poolshark/range/IRangePoolManager.sol'; -import '../../interfaces/external/poolshark/range/IRangePoolFactory.sol'; -import '../../interfaces/external/poolshark/range/IRangePool.sol'; -import '../../interfaces/structs/CoverPoolStructs.sol'; -import '../../interfaces/modules/sources/ITwapSource.sol'; -import '../math/ConstantProduct.sol'; - -contract PoolsharkRangeSource is ITwapSource { - error WaitUntilBelowMaxTick(); - error WaitUntilAboveMinTick(); - - address public immutable rangePoolFactory; - address public immutable rangePoolManager; - /// @dev - set for Arbitrum mainnet - uint32 public constant oneSecond = 1000; - - constructor( - address _rangePoolFactory, - address _rangePoolManager - ) { - rangePoolFactory = _rangePoolFactory; - rangePoolManager = _rangePoolManager; - } - - function initialize( - PoolsharkStructs.CoverImmutables memory constants - ) external returns ( - uint8 initializable, - int24 startingTick - ) - { - // get the number of blocks covered by the twapLength - uint32 blockCount = uint32(constants.twapLength) * oneSecond / constants.blockTime; - ( - bool sampleCountEnough, - bool sampleLengthEnough - ) = _isPoolSamplesEnough( - constants.inputPool, - blockCount - ); - if (!sampleLengthEnough) { - _increaseSampleLength(constants.inputPool, blockCount); - return (0, 0); - } else if (!sampleCountEnough) { - return (0, 0); - } - // ready to initialize if we get here - initializable = 1; - int24[4] memory averageTicks = _calculateAverageTicks(constants); - // take the average of the 4 samples as a starting tick - startingTick = (averageTicks[0] + averageTicks[1] + averageTicks[2] + averageTicks[3]) / 4; - } - - function factory() external view returns (address) { - return rangePoolFactory; - } - - function feeTierTickSpacing( - uint16 feeTier - ) external view returns ( - int24 - ) - { - return IRangePoolManager(rangePoolManager).feeTiers(feeTier); - } - - function getPool( - address token0, - address token1, - uint16 feeTier - ) external view returns( - address pool - ) { - return IRangePoolFactory(rangePoolFactory).getRangePool(token0, token1, feeTier); - } - - function calculateAverageTick( - PoolsharkStructs.CoverImmutables memory constants, - int24 latestTick - ) external view returns ( - int24 averageTick - ) - { - int24[4] memory averageTicks = _calculateAverageTicks(constants); - int24 minTickVariance = ConstantProduct.maxTick(constants.tickSpread) * 2; - for (uint i; i < 4; i++) { - int24 absTickVariance = latestTick - averageTicks[i] >= 0 ? latestTick - averageTicks[i] - : averageTicks[i] - latestTick; - if (absTickVariance <= minTickVariance) { - /// @dev - averageTick has the least possible variance from latestTick - minTickVariance = absTickVariance; - averageTick = averageTicks[i]; - } - } - } - - function _calculateAverageTicks( - PoolsharkStructs.CoverImmutables memory constants - ) internal view returns ( - int24[4] memory averageTicks - ) - { - uint32[] memory secondsAgos = new uint32[](4); - /// @dev - take 4 samples - /// @dev - twapLength must be >= 5 * blockTime - uint32 timeDelta = constants.blockTime / oneSecond == 0 ? 2 - : constants.blockTime * 2 / oneSecond; - secondsAgos[0] = 0; - secondsAgos[1] = timeDelta; - secondsAgos[2] = constants.twapLength - timeDelta; - secondsAgos[3] = constants.twapLength; - (int56[] memory tickSecondsAccum,,,,) = IRangePool(constants.inputPool).sample(secondsAgos); - - /// @dev take the smallest absolute value of 4 samples - averageTicks[0] = int24(((tickSecondsAccum[0] - tickSecondsAccum[2]) / (int32(secondsAgos[2] - secondsAgos[0])))); - averageTicks[1] = int24(((tickSecondsAccum[0] - tickSecondsAccum[3]) / (int32(secondsAgos[3] - secondsAgos[0])))); - averageTicks[2] = int24(((tickSecondsAccum[1] - tickSecondsAccum[2]) / (int32(secondsAgos[2] - secondsAgos[1])))); - averageTicks[3] = int24(((tickSecondsAccum[1] - tickSecondsAccum[3]) / (int32(secondsAgos[3] - secondsAgos[1])))); - - // make sure all samples fit within min/max bounds - int24 minAverageTick = ConstantProduct.minTick(constants.tickSpread) + constants.tickSpread; - int24 maxAverageTick = ConstantProduct.maxTick(constants.tickSpread) - constants.tickSpread; - for (uint i; i < 4; i++) { - if (averageTicks[i] < minAverageTick) - averageTicks[i] = minAverageTick; - if (averageTicks[i] > maxAverageTick) - averageTicks[i] = maxAverageTick; - } - } - - function _isPoolSamplesEnough( - address pool, - uint32 blockCount - ) internal view returns ( - bool, - bool - ) - { - ( - ,,,,,,,,,, - IRangePool.SampleState memory samples, - ) = IRangePool(pool).poolState(); - return (samples.length >= blockCount, samples.lengthNext >= blockCount); - } - - function _increaseSampleLength(address pool, uint32 blockCount) internal { - IRangePool(pool).increaseSampleLength(uint16(blockCount)); - } -} diff --git a/contracts/libraries/sources/UniswapV3Source.sol b/contracts/libraries/sources/UniswapV3Source.sol index 7c7b557a..626829ef 100644 --- a/contracts/libraries/sources/UniswapV3Source.sol +++ b/contracts/libraries/sources/UniswapV3Source.sol @@ -3,16 +3,18 @@ pragma solidity 0.8.13; import '../../interfaces/external/uniswap/v3/IUniswapV3Factory.sol'; import '../../interfaces/external/uniswap/v3/IUniswapV3Pool.sol'; +import '../../base/events/TwapSourceEvents.sol'; +import '../../interfaces/cover/ICoverPool.sol'; import '../../interfaces/structs/CoverPoolStructs.sol'; import '../../interfaces/modules/sources/ITwapSource.sol'; import '../math/ConstantProduct.sol'; -contract UniswapV3Source is ITwapSource { +contract UniswapV3Source is ITwapSource, TwapSourceEvents { error WaitUntilBelowMaxTick(); error WaitUntilAboveMinTick(); address public immutable uniV3Factory; - uint32 public constant oneSecond = 1000; + uint16 public constant oneSecond = 1000; constructor( address _uniV3Factory @@ -28,23 +30,32 @@ contract UniswapV3Source is ITwapSource { ) { // get the number of blocks covered by the twapLength - uint32 blockCount = uint32(constants.twapLength) * oneSecond / constants.blockTime; + uint16 blockCount = constants.twapLength * oneSecond / constants.sampleInterval; ( - bool observationsCountEnough, - bool observationsLengthEnough - ) = _isPoolObservationsEnough( - constants.inputPool, - blockCount - ); - if (!observationsLengthEnough) { + uint16 cardinality, + uint16 cardinalityNext + ) = _getObservationsCardinality(constants.inputPool); + if (cardinalityNext < blockCount) { _increaseV3Observations(constants.inputPool, blockCount); + emit SampleCountInitialized( + msg.sender, + cardinality, + cardinalityNext, + blockCount + ); return (0, 0); - } else if (!observationsCountEnough) { + } else if (cardinality < blockCount) { return (0, 0); } + emit SampleCountInitialized( + msg.sender, + cardinality, + cardinalityNext, + blockCount + ); // ready to initialize if we get here initializable = 1; - int24[4] memory averageTicks = _calculateAverageTicks(constants); + int24[4] memory averageTicks = calculateAverageTicks(constants); // take the average of the 4 samples as a starting tick startingTick = (averageTicks[0] + averageTicks[1] + averageTicks[2] + averageTicks[3]) / 4; } @@ -79,7 +90,7 @@ contract UniswapV3Source is ITwapSource { int24 averageTick ) { - int24[4] memory averageTicks = _calculateAverageTicks(constants); + int24[4] memory averageTicks = calculateAverageTicks(constants); int24 minTickVariance = ConstantProduct.maxTick(constants.tickSpread) * 2; for (uint i; i < 4; i++) { int24 absTickVariance = latestTick - averageTicks[i] >= 0 ? latestTick - averageTicks[i] @@ -92,17 +103,17 @@ contract UniswapV3Source is ITwapSource { } } - function _calculateAverageTicks( + function calculateAverageTicks( PoolsharkStructs.CoverImmutables memory constants - ) internal view returns ( + ) public view returns ( int24[4] memory averageTicks ) { uint32[] memory secondsAgos = new uint32[](4); /// @dev - take 4 samples - /// @dev - twapLength must be >= 5 * blockTime - uint32 timeDelta = (constants.blockTime / oneSecond == 0) ? 2 - : constants.blockTime * 2 / oneSecond; + /// @dev - twapLength must be >= 5 * sampleInterval + uint32 timeDelta = (constants.sampleInterval / oneSecond == 0) ? 2 + : constants.sampleInterval * 2 / oneSecond; secondsAgos[0] = 0; secondsAgos[1] = timeDelta; secondsAgos[2] = constants.twapLength - timeDelta; @@ -126,17 +137,56 @@ contract UniswapV3Source is ITwapSource { } } - function _isPoolObservationsEnough( - address pool, - uint32 blockCount + function syncLatestTick( + PoolsharkStructs.CoverImmutables memory constants, + address coverPool + ) external view returns ( + int24 latestTick, + bool twapReady + ) { + if (constants.inputPool == address(0)) + return (0, false); + + uint16 samplesRequired = uint16(constants.twapLength) * oneSecond / constants.sampleInterval; + ( + uint16 sampleCount, + uint16 sampleCountMax + ) = _getObservationsCardinality(constants.inputPool); + + if (sampleCountMax < samplesRequired) { + return (0, false); + } else if (sampleCount < samplesRequired) { + return (0, false); + } + // ready to initialize + twapReady = true; + + // if pool exists check unlocked state + uint8 unlockedState = 0; + if (coverPool != address(0)) { + CoverPoolStructs.GlobalState memory state = ICoverPool(coverPool).syncGlobalState(); + unlockedState = state.unlocked; + } + if (unlockedState == 0) { + // pool uninitialized + int24[4] memory averageTicks = calculateAverageTicks(constants); + // take the average of the 4 samples as a starting tick + latestTick = (averageTicks[0] + averageTicks[1] + averageTicks[2] + averageTicks[3]) / 4; + latestTick = (latestTick / int24(constants.tickSpread)) * int24(constants.tickSpread); + } else { + // pool initialized + latestTick = ICoverPool(coverPool).syncLatestTick(); + } + } + + function _getObservationsCardinality( + address pool ) internal view returns ( - bool, - bool + uint16 cardinality, + uint16 cardinalityNext ) { - - (, , , uint16 observationsCount, uint16 observationsLength, , ) = IUniswapV3Pool(pool).slot0(); - return (observationsCount >= blockCount, observationsLength >= blockCount); + (, , , cardinality, cardinalityNext, , ) = IUniswapV3Pool(pool).slot0(); } function _increaseV3Observations(address pool, uint32 blockCount) internal { diff --git a/contracts/libraries/utils/SafeCast.sol b/contracts/libraries/utils/SafeCast.sol index 89192bd3..b86c6526 100644 --- a/contracts/libraries/utils/SafeCast.sol +++ b/contracts/libraries/utils/SafeCast.sol @@ -63,4 +63,18 @@ library SafeCast { if(y < 0) require(false, 'Int256ToUint256:Underflow()'); z = uint256(y); } + + /// @notice Cast a uint256 to a uint8, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint128 + function toUint16(uint256 y) internal pure returns (uint16 z) { + if((z = uint16(y)) != y) require(false, 'Uint256ToUint16:Overflow()'); + } + + /// @notice Cast a uint256 to a uint8, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint128 + function toUint8(uint256 y) internal pure returns (uint8 z) { + if((z = uint8(y)) != y) require(false, 'Uint256ToUint8:Overflow()'); + } } \ No newline at end of file diff --git a/contracts/test/RangePoolFactoryMock.sol b/contracts/test/LimitPoolFactoryMock.sol similarity index 56% rename from contracts/test/RangePoolFactoryMock.sol rename to contracts/test/LimitPoolFactoryMock.sol index 20fb7cab..abf57a9b 100644 --- a/contracts/test/RangePoolFactoryMock.sol +++ b/contracts/test/LimitPoolFactoryMock.sol @@ -1,16 +1,16 @@ //SPDX-License-Identifier: Unlicense pragma solidity 0.8.13; -import '../interfaces/external/poolshark/range/IRangePoolFactory.sol'; -import './RangePoolMock.sol'; +import '../interfaces/limit/ILimitPoolFactory.sol'; +import './LimitPoolMock.sol'; -contract RangePoolFactoryMock is IRangePoolFactory { +contract LimitPoolFactoryMock { address mockPool; address mockPool2; address owner; mapping(uint24 => int24) public feeTierTickSpacing; - mapping(address => mapping(address => mapping(uint24 => address))) public rangePools; + mapping(address => mapping(address => mapping(uint24 => address))) public limitPools; constructor(address tokenA, address tokenB) { owner = msg.sender; @@ -21,22 +21,25 @@ contract RangePoolFactoryMock is IRangePoolFactory { feeTierTickSpacing[10000] = 200; // create mock pool 1 - mockPool = address(new RangePoolMock(tokenA, tokenB, 500, 10)); - rangePools[tokenA][tokenB][500] = mockPool; + mockPool = address(new LimitPoolMock(tokenA, tokenB, 500, 10)); + limitPools[tokenA][tokenB][500] = mockPool; // create mock pool 2 - mockPool2 = address(new RangePoolMock(tokenA, tokenB, 3000, 60)); - rangePools[tokenA][tokenB][3000] = mockPool2; + mockPool2 = address(new LimitPoolMock(tokenA, tokenB, 3000, 60)); + limitPools[tokenA][tokenB][3000] = mockPool2; } - function getRangePool( + function getLimitPool( address tokenIn, address tokenOut, - uint16 feeTier - ) external view override returns (address) { + uint16 feeTier, + uint8 poolTypeId + ) external view returns (address pool, address poolToken) { address token0 = tokenIn < tokenOut ? tokenIn : tokenOut; address token1 = tokenIn < tokenOut ? tokenOut : tokenIn; + poolTypeId; + poolToken; - return rangePools[token0][token1][feeTier]; + pool = limitPools[token0][token1][feeTier]; } } diff --git a/contracts/test/RangePoolMock.sol b/contracts/test/LimitPoolMock.sol similarity index 63% rename from contracts/test/RangePoolMock.sol rename to contracts/test/LimitPoolMock.sol index ba9a8220..dd937c94 100644 --- a/contracts/test/RangePoolMock.sol +++ b/contracts/test/LimitPoolMock.sol @@ -1,18 +1,18 @@ //SPDX-License-Identifier: Unlicense pragma solidity 0.8.13; -import '../interfaces/external/poolshark/range/IRangePool.sol'; +import '../interfaces/limit/ILimitPool.sol'; import './UniswapV3PoolMock.sol'; -contract RangePoolMock is IRangePool { +contract LimitPoolMock is PoolsharkStructs { address internal admin; address public token0; address public token1; int24 public tickSpacing; uint256 swapFee; - uint16 observationCardinality; - uint16 observationCardinalityNext; + uint16 sampleCount; + uint16 sampleCountMax; int56 tickCumulative0; int56 tickCumulative1; @@ -31,55 +31,43 @@ contract RangePoolMock is IRangePool { token1 = _token1; swapFee = _swapFee; tickSpacing = _tickSpacing; - observationCardinality = 4; - observationCardinalityNext = 4; + sampleCount = 4; + sampleCountMax = 4; tickCumulative0 = 10; tickCumulative1 = 9; tickCumulative2 = 6; tickCumulative3 = 5; } - function poolState() + function globalState() external - view override + view returns ( - uint8, - uint16, - int24, - int56, - uint160, - uint160, - uint128, - uint128, - uint200, - uint200, - SampleState memory, - ProtocolFees memory + RangePoolState memory pool, + LimitPoolState memory pool0, + LimitPoolState memory pool1, + uint128 liquidityGlobal, + uint32 positionIdNext, + uint32 epoch, + uint8 unlocked ) { - return ( - 1, - 0, - 0, - 0, - 0, - 1 << 96, - 0, - 0, - 0, - 0, - SampleState( + pool.samples = SampleState( 4, - observationCardinality, - observationCardinalityNext - ), - ProtocolFees(0,0) + sampleCount, + sampleCountMax ); + pool0; + pool1; + liquidityGlobal; + positionIdNext; + epoch; + unlocked; } function sample( uint32[] calldata secondsAgos - ) external view override returns ( + ) external view returns ( int56[] memory tickSecondsAccum, uint160[] memory secondsPerLiquidityAccum, uint160 averagePrice, @@ -103,8 +91,10 @@ contract RangePoolMock is IRangePool { averageTick; } - function increaseSampleLength(uint16 cardinalityNext) external { - observationCardinalityNext = cardinalityNext; + function increaseSampleCount( + uint16 newSampleCountMax + ) external { + sampleCountMax = newSampleCountMax; } function setTickCumulatives(int56 _tickCumulative0, int56 _tickCumulative1, int56 _tickCumulative2, int56 _tickCumulative3) external { @@ -114,8 +104,8 @@ contract RangePoolMock is IRangePool { tickCumulative3 = _tickCumulative3; } - function setObservationCardinality(uint16 _observationCardinality, uint16 _observationCardinalityNext) external { - observationCardinality = _observationCardinality; - observationCardinalityNext = _observationCardinalityNext; + function setObservationCardinality(uint16 _sampleCount, uint16 _sampleCountMax) external { + sampleCount = _sampleCount; + sampleCountMax = _sampleCountMax; } } diff --git a/contracts/utils/CoverPoolErrors.sol b/contracts/utils/CoverPoolErrors.sol index e8841488..b557e957 100644 --- a/contracts/utils/CoverPoolErrors.sol +++ b/contracts/utils/CoverPoolErrors.sol @@ -45,9 +45,7 @@ abstract contract CoverPoolFactoryErrors { error VolatilityTierNotSupported(); error InvalidTickSpread(); error PoolTypeNotFound(); - error CurveMathNotFound(); - error TickSpreadNotMultipleOfTickSpacing(); - error TickSpreadNotAtLeastDoubleTickSpread(); + error InputPoolDoesNotExist(); } abstract contract CoverTransferErrors { diff --git a/contracts/utils/CoverPoolManager.sol b/contracts/utils/CoverPoolManager.sol index c0298744..6e1f1fd5 100644 --- a/contracts/utils/CoverPoolManager.sol +++ b/contracts/utils/CoverPoolManager.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.13; +import '../libraries/utils/SafeCast.sol'; import '../interfaces/cover/ICoverPool.sol'; import '../interfaces/cover/ICoverPoolFactory.sol'; import '../interfaces/cover/ICoverPoolManager.sol'; @@ -16,12 +17,15 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { address public factory; uint16 public constant MAX_PROTOCOL_FEE = 1e4; /// @dev - max protocol fee of 1% uint16 public constant oneSecond = 1000; - // sourceName => sourceAddress - mapping(bytes32 => address) internal _poolTypes; - mapping(bytes32 => address) internal _poolTokens; - mapping(bytes32 => address) internal _twapSources; - // sourceName => feeTier => tickSpread => twapLength => VolatilityTier - mapping(bytes32 => mapping(uint16 => mapping(int16 => mapping(uint16 => VolatilityTier)))) internal _volatilityTiers; + // poolType => impl address + bytes32[] _poolTypeNames; + mapping(uint256 => address) internal _poolTypes; + mapping(uint256 => address) internal _poolTokens; + mapping(uint256 => address) internal _twapSources; + // poolTypeId => feeTier => tickSpread => twapLength => VolatilityTier + mapping(uint256 => mapping(uint16 => mapping(int16 => mapping(uint16 => VolatilityTier)))) internal _volatilityTiers; + + using SafeCast for uint256; constructor() { owner = msg.sender; @@ -78,38 +82,45 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { } function enablePoolType( - bytes32 poolType, - address poolImpl, - address tokenImpl, - address twapImpl + address poolImpl_, + address tokenImpl_, + address twapImpl_, + bytes32 poolTypeName_ ) external onlyOwner { - if (poolType[0] == bytes32("")) require (false, 'TwapSourceNameInvalid()'); - if (poolImpl == address(0) || twapImpl == address(0)) require (false, 'TwapSourceAddressZero()'); - if (_twapSources[poolType] != address(0)) require (false, 'ImplementationAlreadyExists()'); - if (_poolTypes[poolType] != address(0)) require (false, 'ImplementationAlreadyExists()'); - _poolTypes[poolType] = poolImpl; - _poolTokens[poolType] = tokenImpl; - _twapSources[poolType] = twapImpl; - emit PoolTypeEnabled(poolType, poolImpl, twapImpl, ITwapSource(twapImpl).factory()); + uint16 poolTypeId_ = _poolTypeNames.length.toUint16(); + // valid poolType name + if(poolTypeName_ == bytes32("")) + require (false, 'PoolTypeNameInvalid()'); + // invalid impl address + if(poolImpl_ == address(0) || twapImpl_ == address(0) || tokenImpl_ == address(0)) + require (false, 'TwapSourceAddressZero()'); + // pool type already exists + if(_twapSources[poolTypeId_] != address(0) || _poolTypes[poolTypeId_] != address(0)) + require (false, 'PoolTypeAlreadyExists()'); + _poolTypes[poolTypeId_] = poolImpl_; + _poolTokens[poolTypeId_] = tokenImpl_; + _twapSources[poolTypeId_] = twapImpl_; + _poolTypeNames.push(poolTypeName_); + emit PoolTypeEnabled(poolTypeName_, poolImpl_, twapImpl_, ITwapSource(twapImpl_).factory(), poolTypeId_); } function enableVolatilityTier( - bytes32 poolType, + uint16 poolTypeId, uint16 feeTier, int16 tickSpread, uint16 twapLength, VolatilityTier memory volTier ) external onlyOwner { - if (_volatilityTiers[poolType][feeTier][tickSpread][twapLength].auctionLength != 0) { + if (_volatilityTiers[poolTypeId][feeTier][tickSpread][twapLength].auctionLength != 0) { require (false, 'VolatilityTierAlreadyEnabled()'); } else if (volTier.auctionLength == 0 || volTier.minPositionWidth <= 0) { require (false, 'VolatilityTierCannotBeZero()'); - } else if (twapLength < 5 * volTier.blockTime / oneSecond) { + } else if (twapLength < 5 * volTier.sampleInterval / oneSecond) { require (false, 'VoltatilityTierTwapTooShort()'); } else if (volTier.syncFee > 10000 || volTier.fillFee > 10000) { require (false, 'ProtocolFeeCeilingExceeded()'); } - address sourceAddress = _twapSources[poolType]; + address sourceAddress = _twapSources[poolTypeId]; { // check fee tier exists if (sourceAddress == address(0)) require (false, 'TwapSourceNotFound()'); @@ -125,17 +136,17 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { require (false, 'TickSpreadNotAtLeastDoubleTickSpread()'); } } - // twapLength * blockTime should never overflow uint16 - _volatilityTiers[poolType][feeTier][tickSpread][twapLength] = volTier; + // twapLength * sampleInterval should never overflow uint16 + _volatilityTiers[poolTypeId][feeTier][tickSpread][twapLength] = volTier; emit VolatilityTierEnabled( - poolType, + poolTypeId, feeTier, tickSpread, twapLength, volTier.minAmountPerAuction, volTier.auctionLength, - volTier.blockTime, + volTier.sampleInterval, volTier.syncFee, volTier.fillFee, volTier.minPositionWidth, @@ -144,7 +155,7 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { } function modifyVolatilityTierFees( - bytes32 implName, + uint16 poolTypeId, uint16 feeTier, int16 tickSpread, uint16 twapLength, @@ -154,8 +165,8 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { if (syncFee > 10000 || fillFee > 10000) { require (false, 'ProtocolFeeCeilingExceeded()'); } - _volatilityTiers[implName][feeTier][tickSpread][twapLength].syncFee = syncFee; - _volatilityTiers[implName][feeTier][tickSpread][twapLength].fillFee = fillFee; + _volatilityTiers[poolTypeId][feeTier][tickSpread][twapLength].syncFee = syncFee; + _volatilityTiers[poolTypeId][feeTier][tickSpread][twapLength].fillFee = fillFee; } function setFactory( @@ -208,28 +219,28 @@ contract CoverPoolManager is ICoverPoolManager, CoverPoolManagerEvents { } function poolTypes( - bytes32 poolType + uint16 poolTypeId ) external view returns ( address poolImpl, address tokenImpl, address twapImpl ) { return ( - _poolTypes[poolType], - _poolTokens[poolType], - _twapSources[poolType] + _poolTypes[poolTypeId], + _poolTokens[poolTypeId], + _twapSources[poolTypeId] ); } function volatilityTiers( - bytes32 implName, + uint16 poolTypeId, uint16 feeTier, int16 tickSpread, uint16 twapLength ) external view returns ( VolatilityTier memory config ) { - config = _volatilityTiers[implName][feeTier][tickSpread][twapLength]; + config = _volatilityTiers[poolTypeId][feeTier][tickSpread][twapLength]; } /** diff --git a/contracts/utils/PoolsharkRouter.sol b/contracts/utils/PoolsharkRouter.sol index 6caed43b..34ca713a 100644 --- a/contracts/utils/PoolsharkRouter.sol +++ b/contracts/utils/PoolsharkRouter.sol @@ -2,10 +2,13 @@ pragma solidity 0.8.13; import '../interfaces/IPool.sol'; +import '../interfaces/range/IRangePool.sol'; import '../interfaces/limit/ILimitPool.sol'; import '../interfaces/cover/ICoverPool.sol'; -import '../interfaces/callbacks/ILimitPoolSwapCallback.sol'; -import '../interfaces/callbacks/ICoverPoolSwapCallback.sol'; +import '../interfaces/cover/ICoverPoolFactory.sol'; +import '../interfaces/limit/ILimitPoolFactory.sol'; +import '../interfaces/callbacks/ILimitPoolCallback.sol'; +import '../interfaces/callbacks/ICoverPoolCallback.sol'; import '../libraries/utils/SafeTransfers.sol'; import '../libraries/utils/SafeCast.sol'; import '../interfaces/structs/PoolsharkStructs.sol'; @@ -13,8 +16,10 @@ import '../external/solady/LibClone.sol'; contract PoolsharkRouter is PoolsharkStructs, + ILimitPoolMintCallback, ILimitPoolSwapCallback, - ICoverPoolSwapCallback + ICoverPoolSwapCallback, + ICoverPoolMintCallback { using SafeCast for uint256; using SafeCast for int256; @@ -28,6 +33,10 @@ contract PoolsharkRouter is address coverPoolFactory ); + struct MintCallbackData { + address sender; + } + struct SwapCallbackData { address sender; } @@ -53,24 +62,8 @@ contract PoolsharkRouter is ) external override { PoolsharkStructs.LimitImmutables memory constants = ILimitPool(msg.sender).immutables(); - // generate key for pool - bytes32 key = keccak256(abi.encode( - constants.poolImpl, - constants.token0, - constants.token1, - constants.swapFee - )); - - // compute address - address predictedAddress = LibClone.predictDeterministicAddress( - constants.poolImpl, - encodeLimit(constants), - key, - limitPoolFactory - ); - - // revert on sender mismatch - if (msg.sender != predictedAddress) require(false, 'InvalidCallerAddress()'); + // validate sender is a canonical limit pool + canonicalLimitPoolsOnly(constants); // decode original sender SwapCallbackData memory _data = abi.decode(data, (SwapCallbackData)); @@ -78,11 +71,13 @@ contract PoolsharkRouter is // transfer from swap caller if (amount0Delta < 0) { SafeTransfers.transferInto(constants.token0, _data.sender, uint256(-amount0Delta)); - } else { + } + if (amount1Delta < 0) { SafeTransfers.transferInto(constants.token1, _data.sender, uint256(-amount1Delta)); } } + /// @inheritdoc ICoverPoolSwapCallback function coverPoolSwapCallback( int256 amount0Delta, int256 amount1Delta, @@ -90,38 +85,109 @@ contract PoolsharkRouter is ) external override { PoolsharkStructs.CoverImmutables memory constants = ICoverPool(msg.sender).immutables(); - // generate key for pool - bytes32 key = keccak256(abi.encode( - constants.token0, - constants.token1, - constants.source, - constants.inputPool, - constants.tickSpread, - constants.twapLength - )); + // validate sender is a canonical cover pool + canonicalCoverPoolsOnly(constants); - // compute address - address predictedAddress = LibClone.predictDeterministicAddress( - constants.poolImpl, - encodeCover(constants), - key, - coverPoolFactory - ); + // decode original sender + SwapCallbackData memory _data = abi.decode(data, (SwapCallbackData)); + + // transfer from swap caller + if (amount0Delta < 0) { + SafeTransfers.transferInto(constants.token0, _data.sender, uint256(-amount0Delta)); + } + if (amount1Delta < 0) { + SafeTransfers.transferInto(constants.token1, _data.sender, uint256(-amount1Delta)); + } + } - // revert on sender mismatch - if (msg.sender != predictedAddress) require(false, 'InvalidCallerAddress()'); + /// @inheritdoc ILimitPoolMintCallback + function limitPoolMintCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external override { + PoolsharkStructs.LimitImmutables memory constants = ILimitPool(msg.sender).immutables(); + + // validate sender is a canonical limit pool + canonicalLimitPoolsOnly(constants); // decode original sender - SwapCallbackData memory _data = abi.decode(data, (SwapCallbackData)); + MintCallbackData memory _data = abi.decode(data, (MintCallbackData)); // transfer from swap caller if (amount0Delta < 0) { SafeTransfers.transferInto(constants.token0, _data.sender, uint256(-amount0Delta)); - } else { + } + if (amount1Delta < 0) { SafeTransfers.transferInto(constants.token1, _data.sender, uint256(-amount1Delta)); } } + /// @inheritdoc ICoverPoolMintCallback + function coverPoolMintCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external override { + PoolsharkStructs.CoverImmutables memory constants = ICoverPool(msg.sender).immutables(); + + // validate sender is a canonical cover pool + canonicalCoverPoolsOnly(constants); + + // decode original sender + MintCallbackData memory _data = abi.decode(data, (MintCallbackData)); + + // transfer from swap caller + if (amount0Delta < 0) { + SafeTransfers.transferInto(constants.token0, _data.sender, uint256(-amount0Delta)); + } + if (amount1Delta < 0) { + SafeTransfers.transferInto(constants.token1, _data.sender, uint256(-amount1Delta)); + } + } + + function multiMintLimit( + address[] memory pools, + MintLimitParams[] memory params + ) external { + if (pools.length != params.length) require(false, 'InputArrayLengthsMismatch()'); + for (uint i = 0; i < pools.length;) { + params[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + ILimitPool(pools[i]).mintLimit(params[i]); + unchecked { + ++i; + } + } + } + + function multiMintRange( + address[] memory pools, + MintRangeParams[] memory params + ) external { + if (pools.length != params.length) require(false, 'InputArrayLengthsMismatch()'); + for (uint i = 0; i < pools.length;) { + params[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + IRangePool(pools[i]).mintRange(params[i]); + unchecked { + ++i; + } + } + } + + function multiMintCover( + address[] memory pools, + PoolsharkStructs.MintCoverParams[] memory params + ) external { + if (pools.length != params.length) require(false, 'InputArrayLengthsMismatch()'); + for (uint i = 0; i < pools.length;) { + params[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + ICoverPool(pools[i]).mint(params[i]); + unchecked { + ++i; + } + } + } + function multiQuote( address[] memory pools, QuoteParams[] memory params, @@ -137,8 +203,7 @@ contract PoolsharkRouter is if (i > 0) { if (params[i].zeroForOne != params[0].zeroForOne) require (false, 'ZeroForOneParamMismatch()'); if (params[i].exactIn != params[0].exactIn) require(false, 'ExactInParamMismatch()'); - if (params[i].amount != params[0].amount) require(false, 'AmountParamMisMatch()'); - /// @dev - priceLimit values are allowed to be different + /// @dev - amount and priceLimit values are allowed to be different } unchecked { ++i; @@ -163,20 +228,6 @@ contract PoolsharkRouter is } } - function multiCall( - address[] memory pools, - SwapParams[] memory params - ) external { - if (pools.length != params.length) require(false, 'InputArrayLengthsMismatch()'); - for (uint i = 0; i < pools.length;) { - params[i].callbackData = abi.encode(SwapCallbackData({sender: msg.sender})); - ICoverPool(pools[i]).swap(params[i]); - unchecked { - ++i; - } - } - } - function multiSwapSplit( address[] memory pools, SwapParams[] memory params @@ -218,50 +269,241 @@ contract PoolsharkRouter is } } + function multiSnapshotLimit( + address[] memory pools, + SnapshotLimitParams[] memory params + ) external view returns( + uint128[] memory amountIns, + uint128[] memory amountOuts + ) { + amountIns = new uint128[](pools.length); + amountOuts = new uint128[](pools.length); + for (uint i = 0; i < pools.length;) { + if (pools[i] == address(0)) require(false, "InvalidPoolAddress()"); + (amountIns[i], amountOuts[i]) = ILimitPool(pools[i]).snapshotLimit(params[i]); + unchecked { + ++i; + } + } + } + + function createLimitPoolAndMint( + ILimitPoolFactory.LimitPoolParams memory params, + MintRangeParams[] memory mintRangeParams, + MintLimitParams[] memory mintLimitParams + ) external returns ( + address pool, + address poolToken + ) { + // check if pool exists + ( + pool, + poolToken + ) = ILimitPoolFactory(limitPoolFactory).getLimitPool( + params.tokenIn, + params.tokenOut, + params.swapFee, + params.poolTypeId + ); + // create if pool doesn't exist + if (pool == address(0)) { + ( + pool, + poolToken + ) = ILimitPoolFactory(limitPoolFactory).createLimitPool( + params + ); + } + // mint initial range positions + for (uint i = 0; i < mintRangeParams.length;) { + mintRangeParams[i].positionId = 0; + mintRangeParams[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + try IRangePool(pool).mintRange(mintRangeParams[i]){ + } catch { + } + unchecked { + ++i; + } + } + // mint initial limit positions + for (uint i = 0; i < mintLimitParams.length;) { + mintLimitParams[i].positionId = 0; + mintLimitParams[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + try ILimitPool(pool).mintLimit(mintLimitParams[i]) { + } catch { + } + unchecked { + ++i; + } + } + } + + function createCoverPoolAndMint( + ICoverPoolFactory.CoverPoolParams memory params, + MintCoverParams[] memory mintCoverParams + ) external returns ( + address pool, + address poolToken + ) { + // check if pool exists + ( + pool, + poolToken + ) = ICoverPoolFactory(coverPoolFactory).getCoverPool( + params + ); + // create if pool doesn't exist + if (pool == address(0)) { + ( + pool, + poolToken + ) = ICoverPoolFactory(coverPoolFactory).createCoverPool( + params + ); + } + // mint initial cover positions + for (uint i = 0; i < mintCoverParams.length;) { + mintCoverParams[i].positionId = 0; + mintCoverParams[i].callbackData = abi.encode(MintCallbackData({sender: msg.sender})); + try ICoverPool(pool).mint(mintCoverParams[i]){ + } catch { + } + unchecked { + ++i; + } + } + } + + struct SortQuoteResultsLocals { + QuoteResults[] sortedResults; + QuoteResults[] prunedResults; + bool[] sortedFlags; + uint256 emptyResults; + int256 sortAmount; + uint256 sortIndex; + uint256 prunedIndex; + } + function sortQuoteResults( QuoteParams[] memory params, QuoteResults[] memory results ) internal pure returns ( - QuoteResults[] memory sortedResults + QuoteResults[] memory ) { - sortedResults = new QuoteResults[](results.length); - for (uint sorted = 0; sorted < results.length;) { - // if exactIn, sort by most output - // if exactOut, sort by least input - int256 sortAmount = params[0].exactIn ? int256(0) : type(int256).max; - uint256 sortIndex = type(uint256).max; - for (uint index = 0; index < (results.length - sorted);) { - // check if result already sorted - if (results[index].priceAfter > 0) { - if (params[0].exactIn) { - if (results[index].amountOut >= sortAmount) { - sortIndex = index; - sortAmount = results[index].amountOut; - } - } else { - if (results[index].amountIn <= sortAmount) { - sortIndex = index; - sortAmount = results[index].amountIn; - } + SortQuoteResultsLocals memory locals; + locals.sortedResults = new QuoteResults[](results.length); + locals.sortedFlags = new bool[](results.length); + locals.emptyResults = 0; + for (uint sorted = 0; sorted < results.length;) { + // if exactIn, sort by most output + // if exactOut, sort by least input + locals.sortAmount = params[0].exactIn ? int256(0) : type(int256).max; + locals.sortIndex = type(uint256).max; + for (uint index = 0; index < results.length;) { + // check if result already sorted + if (!locals.sortedFlags[index]) { + if (params[0].exactIn) { + if (results[index].amountOut > 0 && results[index].amountOut >= locals.sortAmount) { + locals.sortIndex = index; + locals.sortAmount = results[index].amountOut; + } + } else { + if (results[index].amountIn > 0 && results[index].amountIn <= locals.sortAmount) { + locals.sortIndex = index; + locals.sortAmount = results[index].amountIn; } } - if (sortIndex != type(uint256).max) { - // add the sorted result - sortedResults[sorted] = results[sortIndex]; + } + // continue finding nth element + unchecked { + ++index; + } + } + if (locals.sortIndex != type(uint256).max) { + // add the sorted result + locals.sortedResults[sorted].pool = results[locals.sortIndex].pool; + locals.sortedResults[sorted].amountIn = results[locals.sortIndex].amountIn; + locals.sortedResults[sorted].amountOut = results[locals.sortIndex].amountOut; + locals.sortedResults[sorted].priceAfter = results[locals.sortIndex].priceAfter; - // indicate this result was already sorted - results[sortIndex].priceAfter = 0; - } - // continue finding nth element + // indicate this result was already sorted + locals.sortedFlags[locals.sortIndex] = true; + } else { + ++locals.emptyResults; + } + // find next sorted element + unchecked { + ++sorted; + } + } + // if any results were empty, prune them + if (locals.emptyResults > 0) { + locals.prunedResults = new QuoteResults[](results.length - locals.emptyResults); + locals.prunedIndex = 0; + for (uint sorted = 0; sorted < results.length;) { + // empty results are omitted + if (locals.sortedResults[sorted].pool != address(0)) { + locals.prunedResults[locals.prunedIndex] = locals.sortedResults[sorted]; unchecked { - ++index; + ++locals.prunedIndex; } } - // find next sorted element unchecked { ++sorted; } } + } else { + locals.prunedResults = locals.sortedResults; + } + return locals.prunedResults; + } + + function canonicalLimitPoolsOnly( + PoolsharkStructs.LimitImmutables memory constants + ) private view { + // generate key for pool + bytes32 key = keccak256(abi.encode( + constants.poolImpl, + constants.token0, + constants.token1, + constants.swapFee + )); + + // compute address + address predictedAddress = LibClone.predictDeterministicAddress( + constants.poolImpl, + encodeLimit(constants), + key, + limitPoolFactory + ); + + // revert on sender mismatch + if (msg.sender != predictedAddress) require(false, 'InvalidCallerAddress()'); + } + + function canonicalCoverPoolsOnly( + PoolsharkStructs.CoverImmutables memory constants + ) private view { + // generate key for pool + bytes32 key = keccak256(abi.encode( + constants.token0, + constants.token1, + constants.source, + constants.inputPool, + constants.tickSpread, + constants.twapLength + )); + + // compute address + address predictedAddress = LibClone.predictDeterministicAddress( + constants.poolImpl, + encodeCover(constants), + key, + coverPoolFactory + ); + + // revert on sender mismatch + if (msg.sender != predictedAddress) require(false, 'InvalidCallerAddress()'); } function encodeLimit( @@ -291,7 +533,9 @@ contract PoolsharkRouter is constants.poolToken, constants.inputPool, constants.bounds.min, - constants.bounds.max, + constants.bounds.max + ); + bytes memory value2 = abi.encodePacked( constants.minAmountPerAuction, constants.genesisTime, constants.minPositionWidth, @@ -299,12 +543,26 @@ contract PoolsharkRouter is constants.twapLength, constants.auctionLength ); - bytes memory value2 = abi.encodePacked( - constants.blockTime, + bytes memory value3 = abi.encodePacked( + constants.sampleInterval, constants.token0Decimals, constants.token1Decimals, constants.minAmountLowerPriced ); - return abi.encodePacked(value1, value2); + return abi.encodePacked(value1, value2, value3); + } + + function multiCall( + address[] memory pools, + SwapParams[] memory params + ) external { + if (pools.length != params.length) require(false, 'InputArrayLengthsMismatch()'); + for (uint i = 0; i < pools.length;) { + params[i].callbackData = abi.encode(SwapCallbackData({sender: msg.sender})); + ICoverPool(pools[i]).swap(params[i]); + unchecked { + ++i; + } + } } } \ No newline at end of file diff --git a/contracts/utils/PositionERC1155.sol b/contracts/utils/PositionERC1155.sol index af1ef7e8..990aa07c 100644 --- a/contracts/utils/PositionERC1155.sol +++ b/contracts/utils/PositionERC1155.sol @@ -323,7 +323,9 @@ contract PositionERC1155 is constants.poolToken, constants.inputPool, constants.bounds.min, - constants.bounds.max, + constants.bounds.max + ); + bytes memory value2 = abi.encodePacked( constants.minAmountPerAuction, constants.genesisTime, constants.minPositionWidth, @@ -331,12 +333,12 @@ contract PositionERC1155 is constants.twapLength, constants.auctionLength ); - bytes memory value2 = abi.encodePacked( - constants.blockTime, + bytes memory value3 = abi.encodePacked( + constants.sampleInterval, constants.token0Decimals, constants.token1Decimals, constants.minAmountLowerPriced ); - return abi.encodePacked(value1, value2); + return abi.encodePacked(value1, value2, value3); } } \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index ca9f04f6..ae96119a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -35,19 +35,18 @@ const config: HardhatUserConfig = { }, arb_goerli: { chainId: 421613, - gasPrice: 100000000000, + gasPrice: 1_000_000_000, url: process.env.ARBITRUM_GOERLI_URL || '', accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], timeout: 60000, allowUnlimitedContractSize: true, }, - scroll_alpha: { - chainId: 534353, - gasPrice: 4000000, - url: process.env.SCROLL_ALPHA_URL || '', - accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], - timeout: 60000, - allowUnlimitedContractSize: true, + scrollSepolia: { + chainId: 534351, + url: "https://sepolia-rpc.scroll.io/" || "", + gasPrice: 1_500_000_000, + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, op_goerli: { chainId: 420, @@ -61,15 +60,15 @@ const config: HardhatUserConfig = { etherscan: { apiKey: { arbitrumGoerli: process.env.ARBITRUM_GOERLI_API_KEY, - scroll_alpha: 'abc', + scrollSepolia: 'abc', }, customChains: [ { - network: 'scroll_alpha', - chainId: 534353, + network: 'scrollSepolia', + chainId: 534351, urls: { - apiURL: 'https://blockscout.scroll.io/api', - browserURL: 'https://blockscout.scroll.io/', + apiURL: 'https://sepolia-blockscout.scroll.io/api', + browserURL: 'https://sepolia-blockscout.scroll.io/', }, }, ], diff --git a/scripts/autogen/contract-deployments-keys.ts b/scripts/autogen/contract-deployments-keys.ts index 5f1ee3af..ffab0baa 100644 --- a/scripts/autogen/contract-deployments-keys.ts +++ b/scripts/autogen/contract-deployments-keys.ts @@ -1,8 +1,12 @@ import { ContractDeploymentsKey } from "../util/files/contractDeploymentsJson"; export const CONTRACT_DEPLOYMENT_KEYS: ContractDeploymentsKey[] = [ + { + networkName: 'arb_goerli', + objectName: 'ticksLib' + }, { networkName: 'arb_goerli', objectName: 'positionsLib' - } -]; \ No newline at end of file + }, +] \ No newline at end of file diff --git a/scripts/autogen/contract-deployments.json b/scripts/autogen/contract-deployments.json index cdff0fe8..6fb6e54c 100644 --- a/scripts/autogen/contract-deployments.json +++ b/scripts/autogen/contract-deployments.json @@ -2,23 +2,23 @@ "arb_goerli": { "token0": { "contractName": "Token20", - "contractAddress": "0x0BfaaAfa6e8fB009cD4e2Bd3693F2EEC2d18B053", + "contractAddress": "0xebff7a98149b4774c9743c5d1f382305fe5422c9", "constructorArguments": [ - "Wrapped Ether", - "WETH", - 18 + "USD Coin", + "USDC", + 6 ], - "created": "2023-08-23T15:56:24.715Z" + "created": "2023-08-23T15:56:24.723Z" }, "token1": { "contractName": "Token20", - "contractAddress": "0x19beE8e887a5db5cf20A841eb4DAACBCacF14B1b", + "contractAddress": "0xefb283ef3167ca2ee9d93b201af15e2af3f6e8c7", "constructorArguments": [ - "Dai Stablecoin", - "DAI", + "Wrapped Ether", + "WETH", 18 ], - "created": "2023-08-23T15:56:24.723Z" + "created": "2023-08-23T15:56:24.715Z" }, "uniswapV3FactoryMock": { "contractName": "UniswapV3FactoryMock", @@ -50,102 +50,89 @@ }, "tickMapLib": { "contractName": "TickMap", - "contractAddress": "0xde06Ae292863ecaD9e1103B40088375FF92d05d6", + "contractAddress": "0xe618ee633aa483b6b90d004e1e848791d4f7c986", "constructorArguments": [], - "created": "2023-09-14T16:32:39.638Z" + "created": "2023-11-02T22:43:32.357Z" }, "epochMapLib": { "contractName": "EpochMap", - "contractAddress": "0x28771dab8a0D8fEc916E72358aCF34f2B5Da988A", + "contractAddress": "0x1fe541c10a679bf02202bae7eda13d71a0180ec2", "constructorArguments": [], - "created": "2023-09-14T16:32:41.280Z" + "created": "2023-11-02T22:43:33.847Z" }, "deltasLib": { "contractName": "Deltas", - "contractAddress": "0xDFC658D78505B48aF471D51fdEd2b4bfab17840e", + "contractAddress": "0x0a080de5da6308e3db793f790033ad753453a2ca", "constructorArguments": [], - "created": "2023-09-14T16:32:43.243Z" + "created": "2023-11-02T22:43:35.604Z" }, "epochsLib": { "contractName": "Epochs", - "contractAddress": "0x44638d74537D5fB7D85f3dBe574D0c6DFad0cEf8", + "contractAddress": "0xa625e1aaa080d2f5dadd51a90948b270d2d8d3dd", "constructorArguments": [], - "created": "2023-09-14T16:32:45.936Z" + "created": "2023-11-02T22:43:38.035Z" }, "ticksLib": { "contractName": "Ticks", - "contractAddress": "0xBc9a51CF74F8d79cEb569679012C5247451e79e4", + "contractAddress": "0xb235cdf93884fc2078b5d929689c6c125fc7fb2c", "constructorArguments": [], - "created": "2023-09-14T16:32:47.959Z" + "created": "2023-11-02T22:43:39.850Z" }, "claimsLib": { "contractName": "Claims", - "contractAddress": "0x80449022bA3034756b3FE92846E4873eF1F4ae00", + "contractAddress": "0x60b9965e3e86ab199c1c76914ae64f9dee241969", "constructorArguments": [], - "created": "2023-09-14T16:32:52.204Z" + "created": "2023-11-02T22:43:41.635Z" }, "positionsLib": { "contractName": "Positions", - "contractAddress": "0x3F7e4e0d21aC909E48f7F38CA1B638512B6AF382", + "contractAddress": "0x26e745ce8153d2d40339bead44b68eb77c29982e", "constructorArguments": [], - "created": "2023-09-14T16:32:54.823Z" + "created": "2023-11-02T22:43:43.624Z" }, "coverPoolManager": { "contractName": "CoverPoolManager", - "contractAddress": "0x74db8d63FD2e6FAeA61a7a53bFB5Ab319d17f220", + "contractAddress": "0x87131ccc50786e387cfebcaafa522073a8e43548", "constructorArguments": [], - "created": "2023-09-14T16:32:57.398Z" + "created": "2023-11-02T22:43:45.355Z" }, "mintCall": { "contractName": "MintCall", - "contractAddress": "0x2C918C70b02E49264De7b8c7A7a7B87485726342", + "contractAddress": "0xe644bd89b10afac8ec8ac19f1872837f08fcbe19", "constructorArguments": [], - "created": "2023-09-14T16:33:05.271Z" + "created": "2023-11-02T22:43:51.505Z" }, "burnCall": { "contractName": "BurnCall", - "contractAddress": "0xDd4D0ca909B883c1F35BD7e55FCeB8D3495f5Dc2", + "contractAddress": "0xc86be213fa0f4e13d1a649937052f02a5a194dcb", "constructorArguments": [], - "created": "2023-09-14T16:33:07.906Z" + "created": "2023-11-02T22:43:53.248Z" }, "swapCall": { "contractName": "SwapCall", - "contractAddress": "0x832b611c164058aCfefF09a804D05EB0E6FD9ac4", + "contractAddress": "0x188d2440b8eee2e230e92a1b65d39283e1c47666", "constructorArguments": [], - "created": "2023-09-14T16:33:09.977Z" + "created": "2023-11-02T22:43:54.912Z" }, "quoteCall": { "contractName": "QuoteCall", - "contractAddress": "0x7A44Ad4d2A0C6feFc45b49aC9ea7821852Fd814a", + "contractAddress": "0x35ba9286f1ee7e518beafd4139dc67752b439da2", "constructorArguments": [], - "created": "2023-09-14T16:33:12.476Z" + "created": "2023-11-02T22:43:56.912Z" }, "coverPoolFactory": { "contractName": "CoverPoolFactory", - "contractAddress": "0x479C2Df7eD63ea26146Ac2092C55047C3928A5A6", + "contractAddress": "0xc4230d3f673a44bf125c94effd1a6cc44202e0b8", "constructorArguments": [ - "0x74db8d63FD2e6FAeA61a7a53bFB5Ab319d17f220" + "0x87131cCc50786e387CfebCAafA522073a8E43548" ], - "created": "2023-09-14T16:33:00.945Z" + "created": "2023-11-02T22:43:47.451Z" }, "coverPool": { "contractName": "CoverPool", - "contractAddress": "0xa91eb93a6be6e49DBF7aF22f7C04C8B219f0Cc93", - "constructorArguments": [ - "0xF4516C95Ea10cD81000ac7cB12dFcD20bED02955" - ], - "created": "2023-09-14T16:33:34.141Z" - }, - "coverPool2": { - "contractName": "CoverPool", - "contractAddress": [ - "0xb03aadc6D57428c2bd3a73a859e156C920F04949", - "0x06a75f4E98B8ACB0F8A85f1BAc2E5397D3050b23" - ], - "constructorArguments": [ - "0xF4516C95Ea10cD81000ac7cB12dFcD20bED02955" - ], - "created": "2023-09-14T16:33:36.962Z" + "contractAddress": "0xaff9a4f0d8c2b17e898180f0a29ad76b9a4756c0", + "constructorArguments": [], + "created": "2023-11-02T22:44:15.888Z" }, "token20Batcher": { "contractName": "Token20Batcher", @@ -155,28 +142,52 @@ }, "coverPoolImpl": { "contractName": "CoverPool", - "contractAddress": "0x31e7DE9329b0695fcEAb321D2AA7fB805e1bF4E0", + "contractAddress": "0x68cc0410a7a61061143879d4f79ba5714352c8cf", "constructorArguments": [ - "0x479C2Df7eD63ea26146Ac2092C55047C3928A5A6" + "0xC4230d3F673A44Bf125c94EFFD1A6Cc44202e0B8" ], - "created": "2023-09-14T16:33:15.618Z" + "created": "2023-11-02T22:43:59.040Z" }, "poolRouter": { "contractName": "PoolsharkRouter", - "contractAddress": "0x1759c96aAD9A7E52F6d9c06573e0E0b418Bbdf74", + "contractAddress": "0x6ccccc845849b76f3acb3affcca28a6035e41a20", "constructorArguments": [ - "0xbd6d010bcecc7440a72889546411e0edbb333ea2", - "0x479C2Df7eD63ea26146Ac2092C55047C3928A5A6" + "0xd0219266568eae5c1eea960e3eacf1a1e149aab0", + "0xC4230d3F673A44Bf125c94EFFD1A6Cc44202e0B8" ], - "created": "2023-09-14T16:33:25 .036Z" + "created": "2023-11-02T22:44:04.308Z" }, "positionERC1155": { "contractName": "PositionERC1155", - "contractAddress": "0x6F31bc34C050817f8d95256841D72FA4bE32c22A", + "contractAddress": "0x7744c90fca4c06bd19bf91850bc78c14da53479b", + "constructorArguments": [ + "0xC4230d3F673A44Bf125c94EFFD1A6Cc44202e0B8" + ], + "created": "2023-11-02T22:44:00.878Z" + }, + "limitPoolManager": { + "contractName": "LimitPoolManager", + "contractAddress": "0xcfe989b7d341ef9b766263a980adadd16d3d0ac9", + "constructorArguments": [], + "created": "2023-09-24T14:56:56.998Z" + }, + "limitPoolFactory": { + "contractName": "LimitPoolFactory", + "contractAddress": "0xd0219266568eae5c1eea960e3eacf1a1e149aab0", + "constructorArguments": [ + "0xEcBeC0E3F1d12644168F1a0bCeAf4142ee97B91B" + ], + "created": "2023-09-24T14:56:59.060Z" + }, + "poolsharkLimitSource": { + "contractName": "PoolsharkLimitSource", + "contractAddress": "0x7411c07e51fcbdd5832ede04d54783af1ace3ca7", "constructorArguments": [ - "0x479C2Df7eD63ea26146Ac2092C55047C3928A5A6" + "0xd0219266568eae5c1eea960e3eacf1a1e149aab0", + "0xcfe989b7d341ef9b766263a980adadd16d3d0ac9", + 0 ], - "created": "2023-09-14T16:33:18.642Z" + "created": "2023-11-02T22:43:30.655Z" } }, "scroll_alpha": { diff --git a/scripts/config/networkConfigs.ts b/scripts/config/networkConfigs.ts index 311be7c6..fc490818 100644 --- a/scripts/config/networkConfigs.ts +++ b/scripts/config/networkConfigs.ts @@ -33,4 +33,18 @@ export const NETWORK_CONFIGS: NetworkConfigs = { url: process.env.GOERLI_URL || '', accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, + arb_goerli: { + chainId: 421613, + gas: 9000000, + gasPrice: 10_000_000_000, + url: process.env.ARBITRUM_GOERLI_URL || '', + accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + }, + scrollSepolia : { + chainId: 534353, + gas: 9000000, + gasPrice: 4000000, + url: process.env.SCROLL_ALPHA_URL || '', + accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + } } diff --git a/scripts/constants/supportedNetworks.ts b/scripts/constants/supportedNetworks.ts index 8f4a6cf8..86a43fdf 100644 --- a/scripts/constants/supportedNetworks.ts +++ b/scripts/constants/supportedNetworks.ts @@ -5,13 +5,13 @@ export enum SUPPORTED_NETWORKS { /* Testnet Supported Networks */ GOERLI = 'goerli', ARB_GOERLI = 'arb_goerli', - SCROLL_ALPHA = 'scroll_alpha' + SCROLL_SEPOLIA = 'scrollSepolia' } export enum TESTNET_NETWORKS { GOERLI = 'goerli', ARB_GOERLI = 'arb_goerli', - SCROLL_ALPHA = 'scroll_alpha' + SCROLL_SEPOLIA = 'scrollSepolia' } export enum LOCAL_NETWORKS { diff --git a/scripts/deployment-process.txt b/scripts/deployment-process.txt new file mode 100644 index 00000000..f7d42568 --- /dev/null +++ b/scripts/deployment-process.txt @@ -0,0 +1,43 @@ +1. yarn clean; yarn compile + +2. set values for production + - volatility tiers + - pool types + +3. copy 'limitPoolFactory' and 'limitPoolManager' from limit deployment + - paste into scripts\autogen\contract-deployments.json + +4. set booleans for first deployment + private deployRouter = false + private deployTokens = false + private deployPools = true + private deployContracts = true + private deployPoolsharkLimitSource = true + private deployUniswapV3Source = false + +5. run first deployment + +6. verify contracts + - remove coverPool from scripts\autogen\contract-deployments-keys.ts + - 2nd verification run for Ticks + - 3rd verification run for Positions + - manually verify proxies on Arbiscan + * coverPool + * coverPoolToken + +7. deploy poolRouter on Limit + - deploy + - verify contract + +8. copy 'poolRouter' from limit deployment + - paste into scripts\autogen\contract-deployments.json + +9. add contracts on Tenderly + +10. increase observations on Limit Pools + +11. mint first position(s) + +12. Subgraph setup +- subgraph.yaml => factory, manager, router addresses, startBlock +- constants.ts => FACTORY_ADDRESS down to => STABLE_IS_TOKEN_0 diff --git a/scripts/util/deployAssist.ts b/scripts/util/deployAssist.ts index 30f96927..647549dd 100644 --- a/scripts/util/deployAssist.ts +++ b/scripts/util/deployAssist.ts @@ -119,7 +119,7 @@ export class DeployAssist { network, contractName, objectName, - contract.address, + contract.address.toLocaleLowerCase(), constructorArguments ) await this.contractDeploymentsKeys.addContractDeploymentKey({ diff --git a/subgraph/abis/CoverPoolFactory.json b/subgraph/abis/CoverPoolFactory.json index e0a70850..47cb70f9 100644 --- a/subgraph/abis/CoverPoolFactory.json +++ b/subgraph/abis/CoverPoolFactory.json @@ -26,12 +26,6 @@ "name": "token1", "type": "address" }, - { - "indexed": true, - "internalType": "bytes32", - "name": "poolType", - "type": "bytes32" - }, { "indexed": false, "internalType": "uint16", @@ -49,6 +43,12 @@ "internalType": "uint16", "name": "twapLength", "type": "uint16" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "poolTypeId", + "type": "uint8" } ], "name": "PoolCreated", diff --git a/subgraph/abis/CoverPoolManager.json b/subgraph/abis/CoverPoolManager.json index f5ea9f3d..eeb90400 100644 --- a/subgraph/abis/CoverPoolManager.json +++ b/subgraph/abis/CoverPoolManager.json @@ -59,10 +59,16 @@ { "anonymous": false, "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "poolTypeId", + "type": "uint8" + }, { "indexed": false, "internalType": "bytes32", - "name": "poolType", + "name": "poolTypeName", "type": "bytes32" }, { @@ -160,9 +166,9 @@ "inputs": [ { "indexed": false, - "internalType": "bytes32", - "name": "poolType", - "type": "bytes32" + "internalType": "uint8", + "name": "poolTypeId", + "type": "uint8" }, { "indexed": false, @@ -197,7 +203,7 @@ { "indexed": false, "internalType": "uint16", - "name": "blockTime", + "name": "sampleInterval", "type": "uint16" }, { diff --git a/subgraph/abis/LimitSamples.json b/subgraph/abis/LimitSamples.json new file mode 100644 index 00000000..308c7b67 --- /dev/null +++ b/subgraph/abis/LimitSamples.json @@ -0,0 +1,21 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "int56", + "name": "tickSecondsAccum", + "type": "int56" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "secondsPerLiquidityAccum", + "type": "uint160" + } + ], + "name": "SampleRecorded", + "type": "event" + } +] \ No newline at end of file diff --git a/subgraph/abis/PoolsharkRouter.json b/subgraph/abis/PoolsharkRouter.json new file mode 100644 index 00000000..e1b4e1e5 --- /dev/null +++ b/subgraph/abis/PoolsharkRouter.json @@ -0,0 +1,27 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "limitPoolFactory", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "coverPoolFactory", + "type": "address" + } + ], + "name": "RouterDeployed", + "type": "event" + } + ] \ No newline at end of file diff --git a/subgraph/abis/TwapSource.json b/subgraph/abis/TwapSource.json new file mode 100644 index 00000000..663c544f --- /dev/null +++ b/subgraph/abis/TwapSource.json @@ -0,0 +1,33 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "coverPool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "sampleCount", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "sampleCountMax", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "sampleCountRequired", + "type": "uint16" + } + ], + "name": "SampleCountInitialized", + "type": "event" + } +] \ No newline at end of file diff --git a/subgraph/package.json b/subgraph/package.json index 4c69c471..5f17d591 100644 --- a/subgraph/package.json +++ b/subgraph/package.json @@ -18,7 +18,8 @@ "build": "graph build", "deploy": "graph deploy --product hosted-service alphak3y/poolshark-cover", "deploy-sats": "graph deploy cover-arbitrumGoerli --version-label v0.0.4 --node https://app.satsuma.xyz/api/subgraphs/deploy --deploy-key 7NoUUXPcOGfBX --ipfs https://ipfs.satsuma.xyz", - "deploy-chainstack": "graph deploy --version-label v0.1.2 --node https://api.graph-eu.p2pify.com/643e8c5f0f9ff2b36bb9da6b94af7d5f/deploy --ipfs https://api.graph-eu.p2pify.com/643e8c5f0f9ff2b36bb9da6b94af7d5f/ipfs cover-arbitrumGoerli-beta2", + "deploy-staging": "graph deploy --version-label v0.0.2 --node https://api.graph-eu.p2pify.com/705e9b5127dea429966086e1021e19f6/deploy --ipfs https://api.graph-eu.p2pify.com/705e9b5127dea429966086e1021e19f6/ipfs staging-cover-arbitrumGoerli", + "deploy-chainstack": "graph deploy --version-label v0.2.4 --node https://api.graph-eu.p2pify.com/643e8c5f0f9ff2b36bb9da6b94af7d5f/deploy --ipfs https://api.graph-eu.p2pify.com/643e8c5f0f9ff2b36bb9da6b94af7d5f/ipfs cover-arbitrumGoerli-beta2", "deploy-op-test": "graph deploy --product hosted-service alphak3y/poolshark-cover-op-goerli", "deploy-local": "graph deploy example --ipfs http://localhost:5001 --node http://127.0.0.1:8020", "test": "graph codegen; graph test -v 0.2.0" diff --git a/subgraph/schema.graphql b/subgraph/schema.graphql index a0a8b019..c48ac01a 100644 --- a/subgraph/schema.graphql +++ b/subgraph/schema.graphql @@ -1,6 +1,7 @@ type CoverPoolFactory @entity { # poolAddress id: ID! + manager: CoverPoolManager! poolCount: BigInt! txnCount: BigInt! volumeUsdTotal: BigDecimal! @@ -16,18 +17,25 @@ type BasePrice @entity { USD: BigDecimal! } +type PoolRouter @entity { + id: ID! + + limitPoolFactory: Bytes! + coverPoolFactory: Bytes! +} + type CoverPoolManager @entity { id: ID! owner: Bytes! feeTo: Bytes! - # factory: CoverPoolFactory! + factory: CoverPoolFactory! volatilityTiers: [VolatilityTier!]! } type VolatilityTier @entity { # fee amount + tick spread + twap length + auction length id: ID! - poolType: String! + poolTypeId: String! feeAmount: BigInt! tickSpread: BigInt! twapLength: BigInt! @@ -38,6 +46,10 @@ type VolatilityTier @entity { createdAtBlockNumber: BigInt! } +type TwapSource @entity { + id: ID! +} + type Token @entity { # token address id: ID! @@ -76,9 +88,15 @@ type CoverPool @entity { volatilityTier: VolatilityTier! genesisTime: BigInt! + # sample status + sampleCount: BigInt! + sampleCountMax: BigInt! + sampleCountRequired: BigInt! + + newLatestTick: BigInt! + latestTick: BigInt! liquidity: BigInt! liquidityGlobal: BigInt! - latestTick: BigInt! auctionEpoch: BigInt! auctionStart: BigInt! @@ -113,6 +131,21 @@ type CoverPool @entity { updatedAtBlockNumber: BigInt! } +type LimitPool @entity { + # poolAddress + id: ID! + + coverPool: CoverPool! + samplesRecorded: BigInt! +} + +type Sample @entity { + # poolAddress + sample number + id: ID! + tickSecondsAccum: BigInt! + blockTimestamp: BigInt! +} + type Position @entity { # poolAddress + owner + lower + upper id: ID! @@ -175,7 +208,8 @@ type Tick @entity { index: BigInt! price0: BigDecimal! price1: BigDecimal! - epochLast: BigInt! + epochLast0: BigInt! + epochLast1: BigInt! } type TickDeltas @entity { diff --git a/subgraph/src/constants/constants.ts b/subgraph/src/constants/constants.ts index 21e6bc9e..abd6ceea 100644 --- a/subgraph/src/constants/constants.ts +++ b/subgraph/src/constants/constants.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { BigInt, BigDecimal, Address } from '@graphprotocol/graph-ts' import { CoverPoolFactory as FactoryContract } from '../../generated/CoverPoolFactory/CoverPoolFactory' -export let FACTORY_ADDRESS = '0x479c2df7ed63ea26146ac2092c55047c3928a5a6' +export let FACTORY_ADDRESS = '0xf8f75a39663e97d36da4be2882608513512f4cf0' export let WETH_ADDRESS = '0x0bfaaafa6e8fb009cd4e2bd3693f2eec2d18b053' // tokens where USD value is safe to use for globals @@ -16,7 +16,7 @@ export let STABLE_COINS: string[] = [ ] // used for safe eth pricing -export const STABLE_POOL_ADDRESS = '0xa91eb93a6be6e49dbf7af22f7c04c8b219f0cc93' +export const STABLE_POOL_ADDRESS = '0xaaa678556b92c93e81e15107bb43df9980f4255d' // determines which token to use for eth<-> rate, true means stable is token0 in pool above export const STABLE_IS_TOKEN_0 = false diff --git a/subgraph/src/mappings/coverpool.ts b/subgraph/src/mappings/coverpool.ts index 7991f2fe..53badeac 100644 --- a/subgraph/src/mappings/coverpool.ts +++ b/subgraph/src/mappings/coverpool.ts @@ -649,7 +649,8 @@ export function handleFinalDeltasAccumulated(event: FinalDeltasAccumulated): voi let accumTickDeltas = loadAccumTickDeltas.entity pool.liquidityGlobal = pool.liquidityGlobal.minus(accumTickDeltas.liquidityMinus) - accumTick.epochLast = auctionEpochParam + if (isPool0Param) accumTick.epochLast0 = auctionEpochParam + else accumTick.epochLast1 = auctionEpochParam accumTickDeltas.liquidityMinus = BIGINT_ZERO accumTickDeltas.amountInDelta = accumTickDeltas.amountInDelta.plus(amountInDeltaParam) accumTickDeltas.amountOutDelta = accumTickDeltas.amountOutDelta.plus(amountOutDeltaParam) @@ -679,7 +680,8 @@ export function handleStashDeltasAccumulated(event: StashDeltasAccumulated): voi let stashTick = loadStashTick.entity let stashTickDeltas = loadStashTickDeltas.entity - stashTick.epochLast = auctionEpochParam + if (isPool0Param) stashTick.epochLast0 = auctionEpochParam + else stashTick.epochLast1 = auctionEpochParam stashTickDeltas.amountInDelta = stashTickDeltas.amountInDelta.plus(amountInDeltaParam) stashTickDeltas.amountOutDelta = stashTickDeltas.amountOutDelta.plus(amountOutDeltaParam) stashTickDeltas.amountInDeltaMaxStashed = stashTickDeltas.amountInDeltaMaxStashed.plus(amountInDeltaMaxStashedParam) diff --git a/subgraph/src/mappings/coverpoolfactory.ts b/subgraph/src/mappings/coverpoolfactory.ts index eafa3101..b98d9078 100644 --- a/subgraph/src/mappings/coverpoolfactory.ts +++ b/subgraph/src/mappings/coverpoolfactory.ts @@ -1,12 +1,12 @@ import { log } from '@graphprotocol/graph-ts' import { PoolCreated } from '../../generated/CoverPoolFactory/CoverPoolFactory' -import { CoverPoolTemplate } from '../../generated/templates' +import { CoverPoolTemplate, LimitSamplesTemplate } from '../../generated/templates' import { fetchTokenSymbol, fetchTokenName, fetchTokenDecimals, } from './utils/helpers' -import { safeLoadCoverPool, safeLoadCoverPoolFactory, safeLoadToken, safeLoadVolatilityTier } from './utils/loads' +import { safeLoadCoverPool, safeLoadCoverPoolFactory, safeLoadLimitPool, safeLoadToken, safeLoadVolatilityTier } from './utils/loads' import { BigInt } from '@graphprotocol/graph-ts' import { FACTORY_ADDRESS, ONE_BI } from '../constants/constants' @@ -15,12 +15,13 @@ export function handlePoolCreated(event: PoolCreated): void { let feeTierParam = BigInt.fromI32(event.params.fee) let tickSpreadParam = BigInt.fromI32(event.params.tickSpread) let twapLengthParam = BigInt.fromI32(event.params.twapLength) - let poolTypeParam = event.params.poolType.toHex() - let poolAddressParam = event.params.pool.toHex() + let poolTypeIdParam: string = event.params.poolTypeId.toString() + let poolAddressParam = event.params.pool + let inputPoolParam = event.params.inputPool // load from store - let loadVolatilityTier = safeLoadVolatilityTier(poolTypeParam, feeTierParam, tickSpreadParam, twapLengthParam) - let loadCoverPool = safeLoadCoverPool(poolAddressParam) + let loadVolatilityTier = safeLoadVolatilityTier(poolTypeIdParam, feeTierParam, tickSpreadParam, twapLengthParam) + let loadCoverPool = safeLoadCoverPool(poolAddressParam.toHex()) let loadCoverPoolFactory = safeLoadCoverPoolFactory(FACTORY_ADDRESS.toLowerCase()) let loadToken0 = safeLoadToken(event.params.token0.toHexString()) let loadToken1 = safeLoadToken(event.params.token1.toHexString()) @@ -69,11 +70,21 @@ export function handlePoolCreated(event: PoolCreated): void { factory.poolCount = factory.poolCount.plus(ONE_BI) factory.txnCount = factory.txnCount.plus(ONE_BI) + // create the tracked contract based on the template + CoverPoolTemplate.create(poolAddressParam) + + if (poolTypeIdParam == '0') { + let loadLimitPool = safeLoadLimitPool(inputPoolParam.toHex()) + let limitPool = loadLimitPool.entity + if (!loadLimitPool.exists) { + limitPool.coverPool = poolAddressParam.toHex() + limitPool.save() + } + LimitSamplesTemplate.create(inputPoolParam) + } + pool.save() factory.save() token0.save() token1.save() - - // create the tracked contract based on the template - CoverPoolTemplate.create(event.params.pool) } diff --git a/subgraph/src/mappings/coverpoolmanager.ts b/subgraph/src/mappings/coverpoolmanager.ts index daf7c996..be0cb9f7 100644 --- a/subgraph/src/mappings/coverpoolmanager.ts +++ b/subgraph/src/mappings/coverpoolmanager.ts @@ -1,23 +1,34 @@ -import { safeLoadManager, safeLoadCoverPoolFactory, safeLoadVolatilityTier } from './utils/loads' +import { safeLoadManager, safeLoadCoverPoolFactory, safeLoadVolatilityTier, safeLoadTwapSource } from './utils/loads' import { BigInt, log } from '@graphprotocol/graph-ts' import { FACTORY_ADDRESS } from '../constants/constants' -import { FactoryChanged, FeeToTransfer, OwnerTransfer, ProtocolFeesCollected } from '../../generated/CoverPoolManager/CoverPoolManager' +import { FactoryChanged, FeeToTransfer, OwnerTransfer, PoolTypeEnabled, ProtocolFeesCollected } from '../../generated/CoverPoolManager/CoverPoolManager' import { VolatilityTierEnabled } from '../../generated/CoverPoolManager/CoverPoolManager' +import { TwapSourceTemplate } from '../../generated/templates' + +export function handlePoolTypeEnabled(event: PoolTypeEnabled): void { + let twapSourceAddressParam = event.params.sourceAddress + let loadTwapSource = safeLoadTwapSource(twapSourceAddressParam.toHex()) + if (!loadTwapSource.exists) { + TwapSourceTemplate.create(twapSourceAddressParam) + } + let twapSource = loadTwapSource.entity + twapSource.save() +} export function handleVolatilityTierEnabled(event: VolatilityTierEnabled): void { let feeTierParam = BigInt.fromI32(event.params.feeTier) let tickSpreadParam = BigInt.fromI32(event.params.tickSpread) let twapLengthParam = BigInt.fromI32(event.params.twapLength) let auctionLengthParam = BigInt.fromI32(event.params.auctionLength) - let poolTypeParam = event.params.poolType.toHex() + let poolTypeIdParam: string = event.params.poolTypeId.toString() let loadManager = safeLoadManager(event.address.toHex()) - let loadVolatilityTier = safeLoadVolatilityTier(poolTypeParam, feeTierParam, tickSpreadParam, twapLengthParam) + let loadVolatilityTier = safeLoadVolatilityTier(poolTypeIdParam, feeTierParam, tickSpreadParam, twapLengthParam) let manager = loadManager.entity let volatilityTier = loadVolatilityTier.entity - volatilityTier.poolType = poolTypeParam + volatilityTier.poolTypeId = poolTypeIdParam volatilityTier.feeAmount = feeTierParam volatilityTier.tickSpread = tickSpreadParam volatilityTier.twapLength = twapLengthParam @@ -38,11 +49,11 @@ export function handleFactoryChanged(event: FactoryChanged): void { let manager = loadManager.entity let factory = loadRangePoolFactory.entity - // manager.factory = factory.id - // factory.owner = manager.id + manager.factory = factory.id + factory.manager = manager.id - // manager.save() - // factory.save() + manager.save() + factory.save() } export function handleProtocolFeesCollected(event: ProtocolFeesCollected): void { @@ -74,12 +85,11 @@ export function handleOwnerTransfer(event: OwnerTransfer): void { if(!loadManager.exists) { manager.feeTo = newOwnerParam - // manager.factory = FACTORY_ADDRESS + manager.factory = FACTORY_ADDRESS.toLowerCase() } if(!loadFactory.exists) { - //factory.owner = manager.id + factory.manager = manager.id } - manager.owner = newOwnerParam manager.save() diff --git a/subgraph/src/mappings/coverpoolrouter.ts b/subgraph/src/mappings/coverpoolrouter.ts new file mode 100644 index 00000000..3b0d0076 --- /dev/null +++ b/subgraph/src/mappings/coverpoolrouter.ts @@ -0,0 +1,18 @@ +import { RouterDeployed } from "../../generated/PoolsharkRouter/PoolsharkRouter"; +import { safeLoadPoolRouter } from "./utils/loads"; + +export function handleRouterDeployed(event: RouterDeployed): void { + let routerParam = event.params.router + let limitPoolFactoryParam = event.params.limitPoolFactory + let coverPoolFactoryParam = event.params.coverPoolFactory + + let loadPoolRouter = safeLoadPoolRouter(routerParam.toHex()) + + let poolRouter = loadPoolRouter.entity + + if (!loadPoolRouter.exists) { + poolRouter.limitPoolFactory = limitPoolFactoryParam + poolRouter.coverPoolFactory = coverPoolFactoryParam + } + poolRouter.save() +} \ No newline at end of file diff --git a/subgraph/src/mappings/limit/samples.ts b/subgraph/src/mappings/limit/samples.ts new file mode 100644 index 00000000..0d66cfad --- /dev/null +++ b/subgraph/src/mappings/limit/samples.ts @@ -0,0 +1,20 @@ +import { SampleRecorded } from "../../../generated/templates/LimitSamplesTemplate/LimitSamples"; +import { ONE_BI } from "../../constants/constants"; +import { safeLoadCoverPool, safeLoadLimitPool } from "../utils/loads"; + +export function handleSampleRecorded(event: SampleRecorded): void { + // load params + let poolAddress = event.address + + // load entities + let loadLimitPool = safeLoadLimitPool(poolAddress.toHex()) + let limitPool = loadLimitPool.entity + + let loadCoverPool = safeLoadCoverPool(limitPool.coverPool) + let coverPool = loadCoverPool.entity + + if (limitPool.samplesRecorded.le(coverPool.sampleCountMax)) { + coverPool.sampleCount = limitPool.samplesRecorded + coverPool.save() + } +} \ No newline at end of file diff --git a/subgraph/src/mappings/twapsource.ts b/subgraph/src/mappings/twapsource.ts new file mode 100644 index 00000000..7547b0b2 --- /dev/null +++ b/subgraph/src/mappings/twapsource.ts @@ -0,0 +1,25 @@ + +import { SampleCountInitialized } from "../../generated/templates/TwapSourceTemplate/TwapSource" +import { safeLoadCoverPool } from "./utils/loads" +import { BigInt } from '@graphprotocol/graph-ts' + +export function handleSampleCountInitialized(event: SampleCountInitialized): void { + let coverPoolParam = event.params.coverPool.toHex() + let sampleCountParam = event.params.sampleCount + let sampleCountMaxParam = event.params.sampleCountMax + let sampleCountRequiredParam = event.params.sampleCountRequired + + let loadPool = safeLoadCoverPool(coverPoolParam) + + if (!loadPool.exists) { + //error + } + + let pool = loadPool.entity + + pool.sampleCount = BigInt.fromI32(sampleCountParam) + pool.sampleCountMax = BigInt.fromI32(sampleCountMaxParam) + pool.sampleCountRequired = BigInt.fromI32(sampleCountRequiredParam) + + pool.save() +} \ No newline at end of file diff --git a/subgraph/src/mappings/utils/loads.ts b/subgraph/src/mappings/utils/loads.ts index 0548bb6e..2d34783c 100644 --- a/subgraph/src/mappings/utils/loads.ts +++ b/subgraph/src/mappings/utils/loads.ts @@ -1,5 +1,5 @@ import { Address, BigDecimal, BigInt, Bytes, log } from '@graphprotocol/graph-ts' -import { BasePrice, BurnLog, CoverPool, CoverPoolFactory, CoverPoolManager, MintLog, Position, Tick, TickDeltas, Token, VolatilityTier } from '../../../generated/schema' +import { BasePrice, BurnLog, CoverPool, CoverPoolFactory, CoverPoolManager, LimitPool, MintLog, PoolRouter, Position, Tick, TickDeltas, Token, VolatilityTier } from '../../../generated/schema' import { ONE_BD, ONE_BI } from '../../constants/constants' import { fetchTokenSymbol, @@ -9,6 +9,7 @@ import { } from './helpers' import { bigDecimalExponated, safeDiv } from './math' import { getEthPriceInUSD } from './price' +import { TwapSource } from '../../../generated/schema' class LoadBasePriceRet { entity: BasePrice @@ -32,6 +33,24 @@ export function safeLoadBasePrice(name: string): LoadBasePriceRet { } } +class LoadPoolRouterRet { + entity: PoolRouter + exists: boolean +} +export function safeLoadPoolRouter(routerAddress: string): LoadPoolRouterRet { + let exists = true + let poolRouterEntity = PoolRouter.load(routerAddress) + + if (!poolRouterEntity) { + poolRouterEntity = new PoolRouter(routerAddress) + exists = false + } + + return { + entity: poolRouterEntity, + exists: exists, + } +} class LoadTokenRet { entity: Token exists: boolean @@ -142,6 +161,26 @@ export function safeLoadManager(address: string): LoadManagerRet { } } +class LoadTwapSourceRet { + entity: TwapSource + exists: boolean +} +export function safeLoadTwapSource(address: string): LoadTwapSourceRet { + let exists = true + + let twapSourceEntity = TwapSource.load(address) + + if (!twapSourceEntity) { + twapSourceEntity = new TwapSource(address) + exists = false + } + + return { + entity: twapSourceEntity, + exists: exists, + } +} + class LoadVolatilityTierRet { entity: VolatilityTier exists: boolean @@ -188,7 +227,6 @@ export function safeLoadTick(address: string, index: BigInt): LoadTickRet { tickEntity = new Tick(tickId) tickEntity.pool = address tickEntity.index = index - tickEntity.epochLast = ONE_BI // 1.0001^tick is token1/token0. tickEntity.price0 = bigDecimalExponated(BigDecimal.fromString('1.0001'), BigInt.fromI32(tickEntity.index.toI32())) tickEntity.price1 = safeDiv(ONE_BD, tickEntity.price0) @@ -248,6 +286,25 @@ export function safeLoadCoverPoolFactory(factoryAddress: string): LoadCoverPoolF } } +class LoadLimitPoolRet { + entity: LimitPool + exists: boolean +} +export function safeLoadLimitPool(poolAddress: string): LoadLimitPoolRet { + let exists = true + let limitPoolEntity = LimitPool.load(poolAddress) + + if (!limitPoolEntity) { + limitPoolEntity = new LimitPool(poolAddress) + exists = false + } + + return { + entity: limitPoolEntity, + exists: exists, + } +} + class LoadCoverPoolRet { entity: CoverPool exists: boolean diff --git a/subgraph/subgraph.yaml b/subgraph/subgraph.yaml index 8b3bba15..8a44e520 100644 --- a/subgraph/subgraph.yaml +++ b/subgraph/subgraph.yaml @@ -8,9 +8,9 @@ dataSources: name: CoverPoolFactory network: arbitrum-goerli source: - address: '0x479C2Df7eD63ea26146Ac2092C55047C3928A5A6' + address: '0xc4230d3f673a44bf125c94effd1a6cc44202e0b8' abi: CoverPoolFactory - startBlock: 41521100 + startBlock: 52612037 mapping: kind: ethereum/events apiVersion: 0.0.6 @@ -31,15 +31,15 @@ dataSources: - name: ERC20NameBytes file: ./abis/ERC20NameBytes.json eventHandlers: - - event: PoolCreated(address,indexed address,address,address,indexed bytes32,uint16,int16,uint16) + - event: PoolCreated(address,indexed address,address,address,uint16,int16,uint16,indexed uint8) handler: handlePoolCreated - kind: ethereum/contract name: CoverPoolManager network: arbitrum-goerli source: - address: '0x74db8d63FD2e6FAeA61a7a53bFB5Ab319d17f220' + address: '0x87131ccc50786e387cfebcaafa522073a8e43548' abi: CoverPoolManager - startBlock: 41521100 + startBlock: 52612031 mapping: kind: ethereum/events apiVersion: 0.0.6 @@ -52,7 +52,9 @@ dataSources: - name: CoverPoolManager file: ./abis/CoverPoolManager.json eventHandlers: - - event: VolatilityTierEnabled(bytes32,uint16,int16,uint16,uint128,uint16,uint16,uint16,uint16,int16,bool) + - event: PoolTypeEnabled(uint8,bytes32,address,address,address) + handler: handlePoolTypeEnabled + - event: VolatilityTierEnabled(uint8,uint16,int16,uint16,uint128,uint16,uint16,uint16,uint16,int16,bool) handler: handleVolatilityTierEnabled - event: FeeToTransfer(indexed address,indexed address) handler: handleFeeToTransfer @@ -62,6 +64,26 @@ dataSources: handler: handleProtocolFeesCollected - event: FactoryChanged(indexed address,indexed address) handler: handleFactoryChanged + - kind: ethereum/contract + name: PoolsharkRouter + network: arbitrum-goerli + source: + address: '0x6ccccc845849b76f3acb3affcca28a6035e41a20' + abi: PoolsharkRouter + startBlock: 52612031 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/coverpoolrouter.ts + entities: + - PoolsharkRouter + abis: + - name: PoolsharkRouter + file: ./abis/PoolsharkRouter.json + eventHandlers: + - event: RouterDeployed(address,address,address) + handler: handleRouterDeployed templates: # Cover Pool Events - kind: ethereum/contract @@ -108,6 +130,44 @@ templates: handler: handleStashDeltasAccumulated - event: StashDeltasCleared(int24,bool) handler: handleStashDeltasCleared + # TwapSource events + - kind: ethereum/contract + name: TwapSourceTemplate + network: arbitrum-goerli + source: + abi: TwapSource + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/twapsource.ts + entities: + - TwapSource + abis: + - name: TwapSource + file: ./abis/TwapSource.json + eventHandlers: + - event: SampleCountInitialized(indexed address,uint16,uint16,uint16) + handler: handleSampleCountInitialized + # InputPool events + - kind: ethereum/contract + name: LimitSamplesTemplate + network: arbitrum-goerli + source: + abi: LimitSamples + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/limit/samples.ts + entities: + - CoverPool + abis: + - name: LimitSamples + file: ./abis/LimitSamples.json + eventHandlers: + - event: SampleRecorded(int56,uint160) + handler: handleSampleRecorded # ERC-1155 events - kind: ethereum/contract name: PositionERC1155Template diff --git a/tasks/deploy/utils/mintPosition.ts b/tasks/deploy/utils/mintPosition.ts index d90596ce..0c9be0fc 100644 --- a/tasks/deploy/utils/mintPosition.ts +++ b/tasks/deploy/utils/mintPosition.ts @@ -38,13 +38,30 @@ export class MintPosition { await getLatestTick(true) // await getPrice(true) - // 0x34e800D1456d87A5F62B774AD98cea54a3A40048 - // 0x1DcF623EDf118E4B21b4C5Dc263bb735E170F9B8 + // 0x65f5B282E024e3d6CaAD112e848dEc3317dB0902 + // 0x1DcF623EDf118E4B21b4C5Dc263bb735E170F9B8 + // 0x9dA9409D17DeA285B078af06206941C049F692Dc + // 0xBd5db4c7D55C086107f4e9D17c4c34395D1B1E1E + const txn = await hre.props.poolRouter.createCoverPoolAndMint( + { + poolType: ethers.utils.formatBytes32String("PSHARK-CPROD"), + tokenIn: '0x0bfaaafa6e8fb009cd4e2bd3693f2eec2d18b053', + tokenOut: '0xEbfF7a98149b4774c9743C5D1f382305Fe5422c9', + feeTier: "1000", + tickSpread: "20", + twapLength: "12" + }, + [] + , {gasLimit: 3_000_000}) + + await txn.wait(); + + return; await validateMint({ signer: hre.props.alice, - recipient: hre.props.alice.address, - lower: '78000', - upper: '79000', + recipient: '0x65f5B282E024e3d6CaAD112e848dEc3317dB0902', + lower: '73400', //1096 + upper: '73600', //1211 amount: token1Amount, zeroForOne: true, balanceInDecrease: token1Amount, diff --git a/tasks/deploy/utils/mintTokens.ts b/tasks/deploy/utils/mintTokens.ts index ef08fb9b..3c0ae4ed 100644 --- a/tasks/deploy/utils/mintTokens.ts +++ b/tasks/deploy/utils/mintTokens.ts @@ -26,56 +26,163 @@ export class MintTokens { hre.nonce = await getNonce(hre, hre.props.alice.address) console.log(this.nonce) await this.initialSetup.readCoverPoolSetup(this.nonce) - const token0Amount = ethers.utils.parseUnits('100', await hre.props.token0.decimals()) - const token1Amount = ethers.utils.parseUnits('100', await hre.props.token1.decimals()) + const token0Amount = ethers.utils.parseUnits('1000', 6) + // const token1Amount = ethers.utils.parseUnits('1000', await hre.props.token1.decimals()) // await mintSigners20(hre.props.token0, token0Amount.mul(10), [hre.props.alice], ['0xCa2A59A26dDfd56C69e3465DF66ee2986B4B0F4a']) - // await mintSigners20(hre.props.token1, token0Amount.mul(10), [hre.props.alice], ['0xCa2A59A26dDfd56C69e3465DF66ee2986B4B0F4a']) + // await mintSigners20(hre.props.token1, token0Amount.mul(1000), [hre.props.alice], ['0xBd5db4c7D55C086107f4e9D17c4c34395D1B1E1E']) + // '0xEbfF7a98149b4774c9743C5D1f382305Fe5422c9' + // '0x0BfaaAfa6e8fB009cD4e2Bd3693F2EEC2d18B053' + // '0x19beE8e887a5db5cf20A841eb4DAACBCacF14B1b' await hre.props.token20Batcher.mintBatch( - [hre.props.token0.address, hre.props.token1.address], - [ - '0xE61aBAFe6AbD96809AD2505F521E7E02ceC41764', - '0x4DD72586fFe14a915ef757b0E9a0c4019932BcA2', - '0xF7128274D8Ec9D25694E34912beAB3d4B2F50130', - '0xaaea48d32fa9c930c4a64e343325f3784bbdea05', - '0xF8C44D4c2BC63AacA7E6EcFd7763effF71A3444C', - '0x46c754738F78454690C04c6F416858aEF8B46eeE', - '0xc05ED8F3adbC1007d9d8dEbc21a721Aa951FAD50', - '0x2b7403bdB196fa5fc0e4f779600A084bE0D8422E', - '0xB0a6C408d37AeC2A58a041919F895cb24d4088f1', - '0xc600a005F9a948F9D44B4B7937a3Dfc7182b238C', - '0x693542F61eBcE90a4F37f6491CC6B0e73F0a2fB3', - '0x5FBce71b1b74d0d8511dC708ceED3250a1aBDacA', - '0x03AC7CaA058026BaaA09a1285F2a9990892b64F0', - '0x7bEeba96a6EaBfbc9ceF5C8a5CF4E28fD4223fcF', - '0xdC1ab035004Fe9D88a308eBaf6417509accb37c2', - '0x1E66DfC2FC49Ba3A323812AeCEbcf50C7b2D64A9', - '0xEADa8ABE36f600da7100eEd8912B5796B9661deC', - '0xC0E36199a29043363A373a8f02b6cc8ab40389cb', - '0x07f8eA7a012fD8d7786567523afA4873993cbB05', - '0x2bd5D10d5994a19e5E911379753A7EbC50119D5B', - '0x6F6932C87Ddc4Ea4B6c9001A8C007344430F233a', - '0x268e0d7811A711Fc0f2735cBc62C1572D05bF70A', - '0x71799D107Df028d5A5D0a11a726969E7DF71B4AB', - '0x9b327BC6616fF854Ef9f78C477F368b929d9dF2D', - '0x06887698291516f0F1A8aa51C24cE9c4DF9ae0d6', - '0xA1a26c50382f10e112328D793f76B2D84Ba87D4A', - '0xCda329d290B6E7Cf8B9B1e4faAA48Da80B6Fa2F2', - '0x465d8F5dB6aBfdAE82FE95Af82CbeC538ec5337b', - '0xBd5db4c7D55C086107f4e9D17c4c34395D1B1E1E', - '0x4ED5f34cf458f0E2a42a514E1B1b846637Bd242E', - '0xaE312276Ea1B35C68617441beddc0d0Fd13c1aF2', - '0x39e9259Aa9d1bf05f5E285BE7acA91Eefe694094', - '0x34e800D1456d87A5F62B774AD98cea54a3A40048', - '0x1DcF623EDf118E4B21b4C5Dc263bb735E170F9B8', - '0xF2bE526eE1242C0e9736CF0E03d834C71389A4c7', - '0x1b5e17110463b3a2da875bc68934EB5137A4f6f4', - '0x20A13F1e2638c9784a031F291943b2A87B3f12A6', - '0x69e8e23Eb2e61BC8449e02Ced7075186DAFBcFc1', - '0x5bcb86339f2B53CA52EdAdc4C692199a78f06E71', - '0xFb408FA20c6f6DA099a7492107bC3531911896e3', - '0x4fd787803dB60816F16B121cC7c5637263f1736f', - '0xB7b60698a41e2375A700a37A46fFCEE42c202c13', - ], token0Amount.mul(10), {gasLimit: 20000000}) + ['0xEbfF7a98149b4774c9743C5D1f382305Fe5422c9',], + [ '0x6a88027bBFC1393f1Ed19fa2A9300C3953aC8b98',// + '0xDc3cCAF326C8ff29a3177805eE87fB0251914309', + '0x07f8eA7a012fD8d7786567523afA4873993cbB05', + '0xf89d78D330434e110b0B5C27b8308d225142Da03', + '0x9B837d0C951a34d565231bED1F9c20772f80e27A', + '0x043Be57cA9A428762C68C6A13cC1266512589E7D', + '0xf46bCfa955c7380E1A9748975428E04E624D1cC9', + '0x106E06bD8dF139ac4A105fC0d6277fb68b39FA3a', + '0x7b042c22A39c651Fb6e516928cD5F5179cD9d4DB', + '0x63440DE92C437967aA08EC0Dd51850FF7FD94aff', + '0xc689A1cf9Ca0D3db7713345D42BEb4B132d8aE32', + '0x8106C8c338B0D86a78Ae9011Ba717e8eaa3Ae9d4', + '0x6b2f58614a8d43829e210e17b1F504EFe0d2eD17', // + '0xa3f9bd4F103F600E5886eE80708372ECC646a036', + '0x44c614dE52a51E6eA0360906E55d717Fa8C5eB3E', + '0x671E83971F543fa4C3E533df4b55838c6aeb2c45', + '0x1846Fb3E55069d144715709dF1C5433E780f9053', + '0x547814d5c60c72ba8B55955bc8A046536FbD77eE', + '0x2e4f0e9240E9240a54d352e90F4CB614bB18C6f7', + '0x45d993393f7B3Fe781935e1155118C7f830d4415', + '0x0c1d5aa8930ff99d735bb5fc1265d3108d09729f', + '0x2eb6896CE4F45192140b5321F61D11E9eDA82F2B', + '0x8847EBaaf29A18396e49191602f8d8D141b98aa7', + '0x664Fae4E52981d3eb5f0E2BDa615A0E4B057F0DE', + '0x2bB4a1Dc852b02eE3a3446cA624aD343b6eEEaA6', + '0x64d196e9cc62ec70ca0379dfc38ccbff344dd38b', + '0x410360b9dCA14DF852Fe2818b57aD4e9d2b7Bc45', + '0x6fCD3561C603cc72b15357caF59C0e579f5C1B03', + '0xC48520Bdf117d951E495225B77EB38734Be96d94', + '0x24E1921f04D3c0AA707bcd588225ccFe1C53493d', + '0x8F76c82F572D97C7b7D773dB62E64b4f0b61B253', + '0x5125889AaB1Ab5dBDD399eFe7f5734e44Db07f56', + '0x860732d54034CdE25E9Dd579E7e36184F04f4548', + '0xe52bA6F91811439854b85CE79a9C65e8911c13aC', + '0xd435231Bed0D4DE8541D94Ec93e1E094Ae633b72', + '0x4dA792b5058F59162E1B619749a0Ce4E984D4841', + '0x547814d5c60c72ba8B55955bc8A046536FbD77eE', + '0xCa9ba74eE20917211ef646AC51ACcc287F27538b', + '0x9AE4F4a3efea60a1d73cb32476A8697E057473BB', + '0x33275f4B8de6571e7650305CbCfCbeeba5fcF4B5', + '0x71ac680233DFA6d8a10600D077Df9bE89e19aaC7', + '0xc63671075D54f73f47fb437528d8643b3D7701C4', + '0xA99DA68da256437215264005605E125973E793Ad', + '0x000f4432a40560bBFf1b581a8b7AdEd8dab80026', + '0x01d0C05F9a9b74CEBF8Fcf2BBfa2257A50708929', + '0x000F38c11De32dF3BFD065c37Eec99a00a42546f', + '0x4b3a832dB69d467819267C04cc632f7636C05Dcc', + '0x315fFf7C53D75737D5a9F5165beD76ca1f689c73', + '0x8bC0dbe3B09bCb20470763143352cB8Cb0238181', + '0x34d6C8F98846e773177519cB59C197caD17A12C9', + '0x148F4e63611be189601f1F1555d4C79e8cEBddC8', + '0x9A810B1cEaF50759693c288D5DB485938663A462', + '0xad2429c165eE3c9E282FF5c9E41045eBe2Aa9044', + '0x01F8B9484B95f9e118a5b0593E513bDba47Ad7Da', + '0x74F0bD24d03F8D444fCa820Fa022964a0eB56eb8', + '0x84533181eb327EbfCcFd2439e8C4966d7260be5b', + '0xAd50B95c9305488dC4444Deb39C6a102Df59D7AE', + '0xcd34746eAc14E60F666E3cEE9d7991D045bBf761', + '0x57FC1299a7FE49882E9593BA79a6E650c2e3F9D0', + '0x72E5d8D85Db805C275561D9FCE16C11002c676FE', + '0x72E5d8D85Db805C275561D9FCE16C11002c676FE', + '0x42a831A7279C8De5DEea822627E8991b904653a9', + '0xe08583e015f358ce59489DEba17B9774833C9F8E', + '0x287c07066A0430223a7a02D6695F067dF518Ba5e', + '0x07d095Ff9fCF13e086ab6D44309733E95DAa28c3', + '0xb9AAd7fb7D0854a4bD55Cca2c8c606fDA499f7B9', + '0x88c736cCee4cf398297a764f2fe2aAF7e6937D7b', + '0x8c952cc18969129A1C67297332A0B6558eB0bFEc', + '0x9eFEb581C020Fc746C1df51d8d2e829C03aA6674', + '0x1b4C396f3F25b86c33FFDe1E7F6afCAeBDD3ddF4', + '0x5881d9BfFf787C8655A9b7F3484aE1a6f7a966E8', + '0x5df12FaD79F630DC7c64D3BD2C00673F4ef36682', + '0x7b042c22A39c651Fb6e516928cD5F5179cD9d4DB', + '0xc689A1cf9Ca0D3db7713345D42BEb4B132d8aE32', + '0x720B451a9c21908DeDA19D92E55895847Bec043d', + '0x8c90e009A6567eA39fE1443Ae3aB03c0EB80Cc11', + '0x659835D141416Cb9c868Ae99486A58Ee214140cD', + '0xBe317BcadDFD00106977e17420427BC8D0101F05', + '0x10C292a9B4b0D085e71590B67F99408a38F3e40a', + '0x1DfE222F72B87Ca8bF2e1414c3B2F952B0F73B62', + '0x8522442BE81c98F18C63Ab6446f9De11481F4F92', + '0xc195F2726352165A1C628cd08833aaF837d76924', + '0x10D8614a4De958A5831F6b015604864E30252a30', + '0x943f12dF9afA19DB3b67888aBd9EaE4465eb7c8d', + '0xF23BE978a2Fa4d91D6E3A9E62120dD069cF1c733', + '0x6d3E6D2f1546dBF6E691b17Af425c6124D1Dc374', + '0x62f1511269AAc24e79BF6F5172ca661711C7dC23', + '0x8446DfE7f0c8d5Db618E8264cC6ed15b7a91c66b', + '0x465d8F5dB6aBfdAE82FE95Af82CbeC538ec5337b', + '0x7A8583Db42EF87E618a4879b18a58FA62E462bF4', + '0x69978da94A19E96C4DD44bc13b693CF83057B9a1', + '0xF40efeF7a98502B33c1BeE076c886795BAbcA02C', + '0xe1E281bC6a1B6b12B775d8b08A829c4ecCeD4B35', + '0x734ef1765588150677F26F4Ebb43d7d3326A9Eaa', + '0x503596528878b9603534c8a43363FE4ea5A89262', + '0xc4735b87dFb6c7cC3651677651E4bF139B27Cc59', + '0x8F90628fBF475dEd16a83071eDCD1D21B764017D', + '0x85B2Ca334f2E6dC60428ce663575aA96F81F7256', + '0x4692C5fE408879c96f0d94D1814c690434f550c0', + '0x573aDaD94A5d502E14A772488Dea8FBf30ECd47E', + '0x515b754d8b51E08F7a4BB0d804C047d63fa4FD27', + '0x8B2B8601124CDFa264558169473A2c12f2ddaa50', + '0x010fFce4B90d552Ec754B7B37DBA9bF49C1Ad3e9', + '0xAe14Daa3Ec60a5EC30c83ffC3f3f3d7553D7973D', + '0x478e78583A27972845971fA1bf1e43fBBF14d7F0', + '0x35CB5ABD6d3B8911291B06477a61691FaA407265', + '0xe8D79Ded7c683Edbe868aC5c7EC3016B9687Db02', + '0x2cFDFD87eF82ebAEB6603a1124de410ADf72bE65', + '0x28581D2854fBc981DBC9eA109105D73A70c527E7', + '0xAf61B0f9756163709A129F60c2192876365D13D5', + '0xd516ACf12120a0Aa34CA3D835F688dA9195BAbc3', + '0x0A01715CF0D6728e16f753F61d447eE580e35558', + '0xaE0BF45abf324f15E00bEe2D5b5D8e7612e50a32', + '0xa303Fe338F7E412d90ff27BA3FCe1903B0c885Bc', + '0x54372fd91473e0F6F8507A955CD45De9B9D740D5', + '0x0874827c91c24d337854fB3e8c701088Cd9881d3', + '0x6DDe50a4ABF87193C2aba53153Ef1734C5A192BA', + '0x33DdF6B07804ECf8BC378A4B197002F02078E7EB', + '0x7C058598da8998599CD5477B1646dE0Fc5839049', + '0x1049c923fF19FA5088d2466F942A1dAa79D03d85', + '0x1d75Ccf1E886a203c8f84e0A7b68475CF8bD2c6a', + '0xc92eF6b1426Be8fCB048831ab0555e1577a16E28', + '0xED5A42f58d5dBDE7e0cC122416A3540De6ccc6C3', + '0x8A8C879D39A74fCE0593714956bB7Ed048A5c1BF', + '0x0949C8d6aa7aD809ea08216f327ecF8a3C6B6518', + '0x8bC0dbe3B09bCb20470763143352cB8Cb0238181', + '0x8bC0dbe3B09bCb20470763143352cB8Cb0238181', + '0xa1e0699E73C1523a788bf2058Be68196c933D1E1', + '0x14d1a955e2467ee8D04d747EeD647c7966E6AC06', + '0x7C1c510878768Ab7A37dF92411Dee3244967F5c6', + '0xa8EB650d195B8c271d16Bdd80DFa0dC54A0FC27A', + '0x5320A72B8EEcDa26be1eE9E0245d39D53979D1a0', + '0x69cE139860Bb9162DC3356d48495Cbf90299F5EF', + '0x2d209040c031d4e2D4d9cb4D3aabf18F52260AB0', + '0x507bFAaEDB3394d0c5aeE8170ebce209057A6Cce', + '0x05Aff7939E8072ab2eA03306A099EFF2516aFe6F', + '0x4236c9f574057c6530067e1FDD9AC6c08c8287fd', + '0x1D97170895cf2a7788DF357e67071beA821b296C', + '0x59A337056D8f5FCbB900d8Ad3268002a11587F1d', + '0x004A014984904D48fE450db8dEB9289aC27F427D', + '0xABBb979bAe05506E018FAA1e7aa12582C00988A4', + '0x814101E949f2690Ee7c5BEc7e7817C1A72bf09c4', + '0xc13E2dB13C132c05895997a05A6b0474543961bE', + '0xF671a0945586c3bF398f5a0bF5Ade9436DC89B1f', + '0x9FBC318fb93821b36dE5b0344bA1E522a5d5D7Ba', + '0x416cE32e56bEA5Df1D5B1D81aA1761C7433bC6B7', + '0x6b2f58614a8d43829e210e17b1F504EFe0d2eD17', + '0xe08583e015f358ce59489DEba17B9774833C9F8E',], token0Amount.mul(1), {gasLimit: 80_000_000}) //TODO: take in address parameter // const token0Balance = await hre.props.token0.balanceOf( diff --git a/test/contracts/coverpool.ts b/test/contracts/coverpool.ts index 3c33f6dc..8dddc810 100644 --- a/test/contracts/coverpool.ts +++ b/test/contracts/coverpool.ts @@ -61,7 +61,7 @@ describe('CoverPool Tests', function () { const latestTick = globalState.latestTick expect(liquidity).to.be.equal(BN_ZERO) - expect(coverImmutables.genesisTime).to.be.equal(currentTime - 2) + expect(coverImmutables.genesisTime).to.be.equal(currentTime) expect(amountInDelta).to.be.equal(BN_ZERO) expect(latestTick).to.be.equal(BN_ZERO) @@ -131,7 +131,6 @@ describe('CoverPool Tests', function () { upperTickCleared: false, revertMessage: 'WaitUntilTwapLengthSufficient()', }) - }) it('pool1 - Should wait until enough observations', async function () { @@ -3532,6 +3531,7 @@ describe('CoverPool Tests', function () { lowerTickCleared: false, revertMessage: '', }) + if (debugMode) await getTick(false, 600000, true) await validateSync(600000) diff --git a/test/contracts/coverpoolmanager.ts b/test/contracts/coverpoolmanager.ts index df5d383c..5f36cb98 100644 --- a/test/contracts/coverpoolmanager.ts +++ b/test/contracts/coverpoolmanager.ts @@ -28,7 +28,9 @@ describe('CoverPoolManager Tests', function () { await gBefore() }) - this.beforeEach(async function () {}) + this.beforeEach(async function () { + await hre.props.uniswapV3PoolMock.setObservationCardinality('5', '5') + }) it('Should be able to change owner', async function () { // check pool contract owner @@ -164,7 +166,7 @@ describe('CoverPoolManager Tests', function () { const volTier: VolatilityTier = { minAmountPerAuction: ethers.utils.parseUnits("1", 18), auctionLength: 40, - blockTime: 1000, + sampleInterval: 1000, syncFee: 0, fillFee: 0, minPositionWidth: 4, @@ -181,7 +183,7 @@ describe('CoverPoolManager Tests', function () { const volTier: VolatilityTier = { minAmountPerAuction: ethers.utils.parseUnits("1", 18), auctionLength: 20, - blockTime: 1000, + sampleInterval: 1000, syncFee: 0, fillFee: 0, minPositionWidth: 1, @@ -198,7 +200,7 @@ describe('CoverPoolManager Tests', function () { const volTier: VolatilityTier = { minAmountPerAuction: ethers.utils.parseUnits("1", 18), auctionLength: 20, - blockTime: 1000, + sampleInterval: 1000, syncFee: 0, fillFee: 0, minPositionWidth: 1, @@ -252,7 +254,7 @@ describe('CoverPoolManager Tests', function () { hre.props.uniswapV3Source.address, hre.props.uniswapV3Source.address ) - ).to.be.revertedWith('TwapSourceNameInvalid()') + ).to.be.revertedWith('PoolTypeNameInvalid()') }) it('Should update sync and fill fees on pool', async function () { @@ -312,12 +314,13 @@ describe('CoverPoolManager Tests', function () { const volTier: VolatilityTier = { minAmountPerAuction: ethers.utils.parseUnits("1", 18), auctionLength: 30, - blockTime: 1000, + sampleInterval: 1000, syncFee: 50, fillFee: 500, minPositionWidth: 5, minAmountLowerPriced: true } + await expect( hre.props.coverPoolManager .connect(hre.props.bob) @@ -330,22 +333,21 @@ describe('CoverPoolManager Tests', function () { .enableVolatilityTier(uniV3String, "500", "20", "5", volTier) ).to.be.revertedWith('VolatilityTierAlreadyEnabled()') - await expect( + await hre.props.coverPoolManager .connect(hre.props.admin) .enableVolatilityTier(uniV3String, "500", "40", "10", volTier) - ).to.be.revertedWith('VolatilityTierAlreadyEnabled()') let volatilityTierConfig = await hre.props.coverPoolManager .volatilityTiers(uniV3String, "500", "40", "10"); - expect(volatilityTierConfig[0]).to.be.equal(BN_ZERO) - expect(volatilityTierConfig[1]).to.be.equal(10) + expect(volatilityTierConfig[0]).to.be.equal(ethers.utils.parseUnits("1", 18)) + expect(volatilityTierConfig[1]).to.be.equal(30) expect(volatilityTierConfig[2]).to.be.equal(1000) - expect(volatilityTierConfig[3]).to.be.equal(500) - expect(volatilityTierConfig[4]).to.be.equal(5000) + expect(volatilityTierConfig[3]).to.be.equal(50) + expect(volatilityTierConfig[4]).to.be.equal(500) expect(volatilityTierConfig[5]).to.be.equal(5) - expect(volatilityTierConfig[6]).to.be.equal(false) + expect(volatilityTierConfig[6]).to.be.equal(true) expect((await hre.props.coverPoolManager diff --git a/test/utils/contracts/coverpool.ts b/test/utils/contracts/coverpool.ts index 2d87e2fe..063fdb9c 100644 --- a/test/utils/contracts/coverpool.ts +++ b/test/utils/contracts/coverpool.ts @@ -23,7 +23,7 @@ export interface PoolState { export interface VolatilityTier { minAmountPerAuction: BigNumber // based on 18 decimals and then converted based on token decimals auctionLength: number - blockTime: number // average block time where 1e3 is 1 second + sampleInterval: number // average block time where 1e3 is 1 second syncFee: number fillFee: number minPositionWidth: number @@ -44,7 +44,7 @@ export interface CoverImmutables { tickSpread: number twapLength: number auctionLength: number - blockTime: number + sampleInterval: number token0Decimals: number token1Decimals: number minAmountLowerPriced: boolean @@ -56,12 +56,12 @@ export interface PriceBounds { } export interface CoverPoolParams { - poolType: any // bytes tokenIn: string tokenOut: string feeTier: number tickSpread: number twapLength: number + poolTypeId: number } export interface Tick { @@ -200,7 +200,7 @@ export async function validateSync(newLatestTick: number, autoSync: boolean = tr /// send a "no op" swap to trigger accumulate const token1Balance = await hre.props.token1.balanceOf(signer.address) - await hre.props.token1.connect(signer).approve(hre.props.coverPool.address, token1Balance) + await hre.props.token1.connect(signer).approve(hre.props.poolRouter.address, token1Balance) if (autoSync) { if (!revertMessage || revertMessage == '') { @@ -383,13 +383,13 @@ export async function validateMint(params: ValidateMintParams): Promise balanceOutBefore = await hre.props.token1.balanceOf(params.signer.address) await hre.props.token0 .connect(params.signer) - .approve(hre.props.coverPool.address, amountDesired) + .approve(hre.props.poolRouter.address, amountDesired) } else { balanceInBefore = await hre.props.token1.balanceOf(params.signer.address) balanceOutBefore = await hre.props.token0.balanceOf(params.signer.address) await hre.props.token1 .connect(params.signer) - .approve(hre.props.coverPool.address, amountDesired) + .approve(hre.props.poolRouter.address, amountDesired) } let lowerTickBefore: Tick @@ -411,29 +411,41 @@ export async function validateMint(params: ValidateMintParams): Promise if (revertMessage == '') { // console.log('MINT CALL') - const txn = await hre.props.coverPool + const txn = await hre.props.poolRouter .connect(params.signer) - .mint({ - to: recipient, - amount: amountDesired, - positionId: positionId, - lower: lower, - upper: upper, - zeroForOne: zeroForOne - }) + .multiMintCover( + [hre.props.coverPool.address], + [ + { + to: recipient, + amount: amountDesired, + positionId: positionId, + lower: lower, + upper: upper, + zeroForOne: zeroForOne, + callbackData: ethers.utils.formatBytes32String('') + } + ] + , {gasLimit: 3000000}) await txn.wait() } else { await expect( - hre.props.coverPool - .connect(params.signer) - .mint({ - to: params.signer.address, + hre.props.poolRouter + .connect(params.signer) + .multiMintCover( + [hre.props.coverPool.address], + [ + { + to: recipient, + amount: amountDesired, positionId: positionId, lower: lower, upper: upper, - amount: amountDesired, - zeroForOne: zeroForOne - }) + zeroForOne: zeroForOne, + callbackData: ethers.utils.formatBytes32String('') + } + ] + , {gasLimit: 3000000}) ).to.be.revertedWith(revertMessage) return expectedPositionId } diff --git a/test/utils/setup/beforeEachProps.ts b/test/utils/setup/beforeEachProps.ts index a03914bc..ec64aada 100644 --- a/test/utils/setup/beforeEachProps.ts +++ b/test/utils/setup/beforeEachProps.ts @@ -18,6 +18,7 @@ import { Token20Batcher, PoolsharkRouter, PositionERC1155, + PoolsharkLimitSource, } from '../../../typechain' import { InitialSetup } from './initialSetup' import { MintCall } from '../../../typechain' @@ -41,6 +42,7 @@ export interface BeforeEachProps { epochsLib: Epochs epochMapLib: EpochMap ticksLib: Ticks + poolsharkLimitSource: PoolsharkLimitSource uniswapV3Source: UniswapV3Source claimsLib: Claims positionsLib: Positions @@ -96,6 +98,7 @@ export class GetBeforeEach { let epochsLib: Epochs let epochMapLib: EpochMap let ticksLib: Ticks + let poolsharkLimitSource: PoolsharkLimitSource let uniswapV3Source: UniswapV3Source let claimsLib: Claims let positionsLib: Positions @@ -129,6 +132,7 @@ export class GetBeforeEach { epochsLib, epochMapLib, ticksLib, + poolsharkLimitSource, uniswapV3Source, claimsLib, positionsLib, diff --git a/test/utils/setup/initialSetup.ts b/test/utils/setup/initialSetup.ts index 7db52ce0..e84e4770 100644 --- a/test/utils/setup/initialSetup.ts +++ b/test/utils/setup/initialSetup.ts @@ -2,7 +2,7 @@ import { SUPPORTED_NETWORKS } from '../../../scripts/constants/supportedNetworks import { DeployAssist } from '../../../scripts/util/deployAssist' import { ContractDeploymentsKeys } from '../../../scripts/util/files/contractDeploymentKeys' import { ContractDeploymentsJson } from '../../../scripts/util/files/contractDeploymentsJson' -import { CoverPool__factory, PoolsharkRouter__factory, PositionERC1155__factory, QuoteCall__factory, Token20Batcher__factory } from '../../../typechain' +import { CoverPool__factory, PoolsharkLimitSource__factory, PoolsharkRouter__factory, PositionERC1155__factory, QuoteCall__factory, Token20Batcher__factory } from '../../../typechain' import { BurnCall__factory } from '../../../typechain' import { SwapCall__factory } from '../../../typechain' import { MintCall__factory } from '../../../typechain' @@ -26,11 +26,20 @@ export class InitialSetup { private token0Decimals = 18 private token1Decimals = 18 private uniV3String = ethers.utils.formatBytes32String('UNI-V3') + private poolsharkString = ethers.utils.formatBytes32String('PSHARK-CPROD') private constantProductString = ethers.utils.formatBytes32String('CONSTANT-PRODUCT') private deployAssist: DeployAssist private contractDeploymentsJson: ContractDeploymentsJson private contractDeploymentsKeys: ContractDeploymentsKeys + /// DEPLOY CONFIG + private deployRouter = true + private deployTokens = false + private deployPools = true + private deployContracts = true + private deployPoolsharkLimitSource = true + private deployUniswapV3Source = false + constructor() { this.deployAssist = new DeployAssist() this.contractDeploymentsJson = new ContractDeploymentsJson() @@ -40,414 +49,633 @@ export class InitialSetup { public async initialCoverPoolSetup(): Promise { const network = SUPPORTED_NETWORKS[hre.network.name.toUpperCase()] - - // const token0Address = ( - // await this.contractDeploymentsJson.readContractDeploymentsJsonFile( - // { - // networkName: hre.network.name, - // objectName: 'token0', - // }, - // 'readRangePoolSetup' - // ) - // ).contractAddress - // const token1Address = ( - // await this.contractDeploymentsJson.readContractDeploymentsJsonFile( - // { - // networkName: hre.network.name, - // objectName: 'token1', - // }, - // 'readRangePoolSetup' - // ) - // ).contractAddress - // hre.props.token0 = await hre.ethers.getContractAt('Token20', token0Address) - // hre.props.token1 = await hre.ethers.getContractAt('Token20', token1Address) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Token20Batcher__factory, - 'token20Batcher', - [] - ) - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Token20__factory, - 'tokenA', - ['Wrapped Ether', 'WETH', this.token0Decimals] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Token20__factory, - 'tokenB', - ['Dai Stablecoin', 'DAI', this.token1Decimals] - ) - - const tokenOrder = hre.props.tokenA.address.localeCompare(hre.props.tokenB.address) < 0 - let token0Args - let token1Args - if (tokenOrder) { - hre.props.token0 = hre.props.tokenA - hre.props.token1 = hre.props.tokenB - token0Args = ['Wrapped Ether', 'WETH', this.token0Decimals] - token1Args = ['Dai Stablecoin', 'DAI', this.token1Decimals] - } else { - hre.props.token0 = hre.props.tokenB - hre.props.token1 = hre.props.tokenA - token0Args = ['Dai Stablecoin', 'DAI', this.token1Decimals] - token1Args = ['Wrapped Ether', 'WETH', this.token0Decimals] - } - this.deployAssist.saveContractDeployment( - network, - 'Token20', - 'token0', - hre.props.token0, - token0Args - ) - this.deployAssist.saveContractDeployment( - network, - 'Token20', - 'token1', - hre.props.token1, - token1Args - ) - this.deployAssist.deleteContractDeployment(network, 'tokenA') - this.deployAssist.deleteContractDeployment(network, 'tokenB') - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - UniswapV3FactoryMock__factory, - 'uniswapV3FactoryMock', - [ - hre.props.token0.address, - hre.props.token1.address - ] - ) - const mockPoolAddress = await hre.props.uniswapV3FactoryMock.getPool( - hre.props.token0.address, - hre.props.token1.address, - '500' - ) + // await this.deployAssist.deployContractWithRetry( + // network, + // //@ts-ignore + // PoolsharkRouter__factory, + // 'poolRouter', + // [ + // '0xd0219266568eae5c1eea960e3eacf1a1e149aab0', // limitPoolFactory + // '0x4FddF20f10BfBc722B013F154762395dA5ba477f' + // ] + // ) - hre.props.uniswapV3PoolMock = await hre.ethers.getContractAt('UniswapV3PoolMock', mockPoolAddress) - await this.deployAssist.saveContractDeployment( - network, - 'UniswapV3PoolMock', - 'uniswapV3PoolMock', - hre.props.uniswapV3PoolMock, - [hre.props.token0.address, hre.props.token1.address, '500', '10'] - ) + // return; - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - UniswapV3Source__factory, - 'uniswapV3Source', - [ - hre.props.uniswapV3FactoryMock.address - ] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - TickMap__factory, - 'tickMapLib', - [] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - EpochMap__factory, - 'epochMapLib', - [] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Deltas__factory, - 'deltasLib', - [], - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Epochs__factory, - 'epochsLib', - [], - { - 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, - 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, - 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address - } - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Ticks__factory, - 'ticksLib', - [], - { - 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address - } - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Claims__factory, - 'claimsLib', - [], - { - 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, - 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, - 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address - } - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - Positions__factory, - 'positionsLib', - [], - { - 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, - 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, - 'contracts/libraries/Claims.sol:Claims': hre.props.claimsLib.address - } - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - CoverPoolManager__factory, - 'coverPoolManager', - [] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - CoverPoolFactory__factory, - 'coverPoolFactory', - [ - hre.props.coverPoolManager.address - ] - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - MintCall__factory, - 'mintCall', - [], - { - 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, - 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, - 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address, - 'contracts/libraries/Ticks.sol:Ticks': hre.props.ticksLib.address - } - ) + // const limitPoolFactoryAddress = ( + // await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + // { + // networkName: hre.network.name, + // objectName: 'limitPoolFactory', + // }, + // 'initialSetup' + // ) + // ).contractAddress + + // const limitPoolManagerAddress = ( + // await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + // { + // networkName: hre.network.name, + // objectName: 'limitPoolManager', + // }, + // 'initialSetup' + // ) + // ).contractAddress + + // await this.deployAssist.deployContractWithRetry( + // network, + // // @ts-ignore + // PoolsharkLimitSource__factory, + // 'poolsharkLimitSource', + // [ + // limitPoolFactoryAddress, + // limitPoolManagerAddress, + // 0 + // ] + // ) + + // const coverPoolManagerAddress = ( + // await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + // { + // networkName: hre.network.name, + // objectName: 'coverPoolManager', + // }, + // 'initialSetup' + // ) + // ).contractAddress + + // await this.deployAssist.deployContractWithRetry( + // network, + // // @ts-ignore + // CoverPoolFactory__factory, + // 'coverPoolFactory', + // [ + // coverPoolManagerAddress, + // hre.props.poolsharkLimitSource.address + // ] + // ) + + // return; + + if (!this.deployTokens && hre.network.name != 'hardhat') { + const token0Address = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'token0', + }, + 'initialSetup' + ) + ).contractAddress + const token1Address = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'token1', + }, + 'initialSetup' + ) + ).contractAddress + hre.props.token0 = await hre.ethers.getContractAt('Token20', token0Address) + hre.props.token1 = await hre.ethers.getContractAt('Token20', token1Address) + } else { + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Token20Batcher__factory, + 'token20Batcher', + [] + ) - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - BurnCall__factory, - 'burnCall', - [], - { - 'contracts/libraries/Positions.sol:Positions': hre.props.positionsLib.address + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Token20__factory, + 'tokenA', + ['Wrapped Ether', 'WETH', this.token0Decimals] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Token20__factory, + 'tokenB', + ['Dai Stablecoin', 'DAI', this.token1Decimals] + ) + + const tokenOrder = hre.props.tokenA.address.localeCompare(hre.props.tokenB.address) < 0 + let token0Args + let token1Args + if (tokenOrder) { + hre.props.token0 = hre.props.tokenA + hre.props.token1 = hre.props.tokenB + token0Args = ['Wrapped Ether', 'WETH', this.token0Decimals] + token1Args = ['Dai Stablecoin', 'DAI', this.token1Decimals] + } else { + hre.props.token0 = hre.props.tokenB + hre.props.token1 = hre.props.tokenA + token0Args = ['Dai Stablecoin', 'DAI', this.token1Decimals] + token1Args = ['Wrapped Ether', 'WETH', this.token0Decimals] } - ) + this.deployAssist.saveContractDeployment( + network, + 'Token20', + 'token0', + hre.props.token0, + token0Args + ) + this.deployAssist.saveContractDeployment( + network, + 'Token20', + 'token1', + hre.props.token1, + token1Args + ) + this.deployAssist.deleteContractDeployment(network, 'tokenA') + this.deployAssist.deleteContractDeployment(network, 'tokenB') + } - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - SwapCall__factory, - 'swapCall', - [] - ) + if (hre.network.name == 'hardhat') { + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + UniswapV3FactoryMock__factory, + 'uniswapV3FactoryMock', + [ + hre.props.token0.address, + hre.props.token1.address + ] + ) + const mockPoolAddress = await hre.props.uniswapV3FactoryMock.getPool( + hre.props.token0.address, + hre.props.token1.address, + '500' + ) + hre.props.uniswapV3PoolMock = await hre.ethers.getContractAt('UniswapV3PoolMock', mockPoolAddress) + await this.deployAssist.saveContractDeployment( + network, + 'UniswapV3PoolMock', + 'uniswapV3PoolMock', + hre.props.uniswapV3PoolMock, + [hre.props.token0.address, hre.props.token1.address, '500', '10'] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + UniswapV3Source__factory, + 'uniswapV3Source', + [ + hre.props.uniswapV3FactoryMock.address + ] + ) + await hre.props.uniswapV3PoolMock.setObservationCardinality('4', '4') + + hre.nonce += 1; + } else if (this.deployPoolsharkLimitSource) { + const limitPoolFactoryAddress = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'limitPoolFactory', + }, + 'initialSetup' + ) + ).contractAddress + + const limitPoolManagerAddress = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'limitPoolManager', + }, + 'initialSetup' + ) + ).contractAddress + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + PoolsharkLimitSource__factory, + 'poolsharkLimitSource', + [ + limitPoolFactoryAddress, + limitPoolManagerAddress, + 0 + ] + ) + } else if (this.deployUniswapV3Source) { + const uniswapV3FactoryAddress = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'uniswapV3Factory', + }, + 'initialSetup' + ) + ).contractAddress + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + UniswapV3Source__factory, + 'uniswapV3Source', + [ + uniswapV3FactoryAddress + ] + ) + } - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - QuoteCall__factory, - 'quoteCall', - [] - ) + if (this.deployContracts || hre.network.name == 'hardhat') { + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + TickMap__factory, + 'tickMapLib', + [] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + EpochMap__factory, + 'epochMapLib', + [] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Deltas__factory, + 'deltasLib', + [], + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Epochs__factory, + 'epochsLib', + [], + { + 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, + 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, + 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Ticks__factory, + 'ticksLib', + [], + { + 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Claims__factory, + 'claimsLib', + [], + { + 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, + 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, + 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + Positions__factory, + 'positionsLib', + [], + { + 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, + 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, + 'contracts/libraries/Claims.sol:Claims': hre.props.claimsLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + CoverPoolManager__factory, + 'coverPoolManager', + [] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + CoverPoolFactory__factory, + 'coverPoolFactory', + [ + hre.props.coverPoolManager.address, + ] + ) - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - CoverPool__factory, - 'coverPoolImpl', - [ + const setFactoryTxn = await hre.props.coverPoolManager.setFactory( hre.props.coverPoolFactory.address - ], - { - 'contracts/libraries/Positions.sol:Positions': hre.props.positionsLib.address, - 'contracts/libraries/Ticks.sol:Ticks': hre.props.ticksLib.address, - 'contracts/libraries/Epochs.sol:Epochs': hre.props.epochsLib.address, - 'contracts/libraries/pool/MintCall.sol:MintCall': hre.props.mintCall.address, - 'contracts/libraries/pool/BurnCall.sol:BurnCall': hre.props.burnCall.address, - 'contracts/libraries/pool/SwapCall.sol:SwapCall': hre.props.swapCall.address, - 'contracts/libraries/pool/QuoteCall.sol:QuoteCall': hre.props.quoteCall.address - } - ) - - await this.deployAssist.deployContractWithRetry( - network, - // @ts-ignore - PositionERC1155__factory, - 'positionERC1155', - [ - hre.props.coverPoolFactory.address - ] - ) - - const enableImplTxn = await hre.props.coverPoolManager.enablePoolType( - this.uniV3String, - hre.props.coverPoolImpl.address, - hre.props.positionERC1155.address, - hre.props.uniswapV3Source.address - ) - await enableImplTxn.wait(); - - hre.nonce += 1; - - await this.deployAssist.deployContractWithRetry( - network, - //@ts-ignore - PoolsharkRouter__factory, - 'poolRouter', - [ - '0xbd6d010bcecc7440a72889546411e0edbb333ea2', // limitPoolFactory - hre.props.coverPoolFactory.address - ] - ) + ) + await setFactoryTxn.wait() + + hre.nonce += 1 + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + MintCall__factory, + 'mintCall', + [], + { + 'contracts/libraries/Deltas.sol:Deltas': hre.props.deltasLib.address, + 'contracts/libraries/TickMap.sol:TickMap': hre.props.tickMapLib.address, + 'contracts/libraries/EpochMap.sol:EpochMap': hre.props.epochMapLib.address, + 'contracts/libraries/Ticks.sol:Ticks': hre.props.ticksLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + BurnCall__factory, + 'burnCall', + [], + { + 'contracts/libraries/Positions.sol:Positions': hre.props.positionsLib.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + SwapCall__factory, + 'swapCall', + [] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + QuoteCall__factory, + 'quoteCall', + [] + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + CoverPool__factory, + 'coverPoolImpl', + [ + hre.props.coverPoolFactory.address + ], + { + 'contracts/libraries/Positions.sol:Positions': hre.props.positionsLib.address, + 'contracts/libraries/Ticks.sol:Ticks': hre.props.ticksLib.address, + 'contracts/libraries/Epochs.sol:Epochs': hre.props.epochsLib.address, + 'contracts/libraries/pool/MintCall.sol:MintCall': hre.props.mintCall.address, + 'contracts/libraries/pool/BurnCall.sol:BurnCall': hre.props.burnCall.address, + 'contracts/libraries/pool/SwapCall.sol:SwapCall': hre.props.swapCall.address, + 'contracts/libraries/pool/QuoteCall.sol:QuoteCall': hre.props.quoteCall.address + } + ) + + await this.deployAssist.deployContractWithRetry( + network, + // @ts-ignore + PositionERC1155__factory, + 'positionERC1155', + [ + hre.props.coverPoolFactory.address + ] + ) - const volTier1: VolatilityTier = { - minAmountPerAuction: BN_ZERO, - auctionLength: 5, - blockTime: 1000, - syncFee: 0, - fillFee: 0, - minPositionWidth: 1, - minAmountLowerPriced: true + if (hre.network.name == 'hardhat' || this.deployUniswapV3Source) { + const enableImplTxn = await hre.props.coverPoolManager.enablePoolType( + this.uniV3String, + hre.props.coverPoolImpl.address, + hre.props.positionERC1155.address, + hre.props.uniswapV3Source.address + ) + await enableImplTxn.wait(); + hre.nonce += 1; + } else if (this.deployPoolsharkLimitSource) { + const enableImplTxn = await hre.props.coverPoolManager.enablePoolType( + hre.props.coverPoolImpl.addre6ss, + hre.props.positionERC1155.address, + hre.props.poolsharkLimitSource.address, + this.poolsharkString + ) + await enableImplTxn.wait(); + hre.nonce += 1; + } } - const enableVolTier1 = await hre.props.coverPoolManager.enableVolatilityTier( - this.uniV3String, - 500, // feeTier - 20, // tickSpread - 5, // auctionLength (seconds) - volTier1 - ) - await enableVolTier1.wait(); - - hre.nonce += 1; - - const volTier2: VolatilityTier = { - minAmountPerAuction: BN_ZERO, - auctionLength: 10, - blockTime: 1000, - syncFee: 500, - fillFee: 5000, - minPositionWidth: 5, - minAmountLowerPriced: false + if (this.deployRouter || hre.network.name == 'hardhat') { + await this.deployAssist.deployContractWithRetry( + network, + //@ts-ignore + PoolsharkRouter__factory, + 'poolRouter', + [ + '0xd0219266568eae5c1eea960e3eacf1a1e149aab0', // limitPoolFactory + hre.props.coverPoolFactory.address + ] + ) } - const enableVolTier2 = await hre.props.coverPoolManager.enableVolatilityTier( - this.uniV3String, - 500, // feeTier - 40, // tickSpread - 10, // auctionLength (seconds) - volTier2 - ) - await enableVolTier2.wait(); - - hre.nonce += 1; + let coverPoolAddress; let coverPoolTokenAddress; - const setFactoryTxn = await hre.props.coverPoolManager.setFactory( - hre.props.coverPoolFactory.address - ) - await setFactoryTxn.wait() + if (this.deployPools && hre.network.name != 'hardhat') { + let poolParams1: CoverPoolParams; + if (this.deployPoolsharkLimitSource) { + // ENABLE VOL TIER 1 + console.log('vol tier 1') + const volTier1: VolatilityTier = { + minAmountPerAuction: BN_ZERO, + auctionLength: 12, + sampleInterval: 1000, + syncFee: 0, + fillFee: 0, + minPositionWidth: 1, + minAmountLowerPriced: true + } + + const enableVolTier1 = await hre.props.coverPoolManager.enableVolatilityTier( + 0, + 1000, // feeTier + 20, // tickSpread + 12, // twapLength (seconds) = ~40 arbitrum blocks + volTier1 + ) + await enableVolTier1.wait(); + + hre.nonce += 1; + console.log('pool 1') + + // CREATE POOL 1 + poolParams1 = { + poolTypeId: 0, + tokenIn: hre.props.token0.address, + tokenOut: hre.props.token1.address, + feeTier: 1000, + tickSpread: 20, + twapLength: 12 + } + let createPoolTxn = await hre.props.coverPoolFactory.createCoverPool( + poolParams1 + ) + await createPoolTxn.wait(); + + hre.nonce += 1; + + // CREATE VOL TIER 2 + console.log('vol tier 2') + const volTier2: VolatilityTier = { + minAmountPerAuction: BN_ZERO, + auctionLength: 12, + sampleInterval: 1000, + syncFee: 0, + fillFee: 0, + minPositionWidth: 1, + minAmountLowerPriced: true + } + + const enableVolTier2 = await hre.props.coverPoolManager.enableVolatilityTier( + 0, + 3000, // feeTier + 60, // tickSpread + 12, // twapLength (seconds) = ~40 arbitrum blocks + volTier2 + ) + await enableVolTier2.wait(); + + hre.nonce += 1; + + // CREATE POOL 2 + console.log('pool 2') + const poolParams2: CoverPoolParams = { + poolTypeId: 0, + tokenIn: hre.props.token0.address, + tokenOut: hre.props.token1.address, + feeTier: 3000, + tickSpread: 60, + twapLength: 12 + } + let createPoolTxn2 = await hre.props.coverPoolFactory.createCoverPool( + poolParams2 + ) + await createPoolTxn2.wait(); + + hre.nonce += 1; + + // CREATE VOL TIER 3 + console.log('vol tier 3') + const volTier3: VolatilityTier = { + minAmountPerAuction: BN_ZERO, + auctionLength: 5, + sampleInterval: 1000, + syncFee: 0, + fillFee: 0, + minPositionWidth: 1, + minAmountLowerPriced: true + } + + const enableVolTier3 = await hre.props.coverPoolManager.enableVolatilityTier( + 0, + 10000, // feeTier + 200, // tickSpread + 12, // twapLength (seconds) = ~40 arbitrum blocks + volTier3 + ) + await enableVolTier3.wait(); + + hre.nonce += 1; + + // CREATE POOL 3 + console.log('pool 3') + const poolParams3: CoverPoolParams = { + poolTypeId: 0, + tokenIn: hre.props.token0.address, + tokenOut: hre.props.token1.address, + feeTier: 10000, + tickSpread: 200, + twapLength: 12 + } + let createPoolTxn3 = await hre.props.coverPoolFactory.createCoverPool( + poolParams3 + ) + await createPoolTxn3.wait(); + + hre.nonce += 1; + } - hre.nonce += 1 + [coverPoolAddress, coverPoolTokenAddress] = await hre.props.coverPoolFactory.getCoverPool( + poolParams1 + ); + } else if (hre.network.name == 'hardhat') { + const volTier1: VolatilityTier = { + minAmountPerAuction: BN_ZERO, + auctionLength: 5, + sampleInterval: 1000, + syncFee: 0, + fillFee: 0, + minPositionWidth: 1, + minAmountLowerPriced: true + } + + const enableVolTier1 = await hre.props.coverPoolManager.enableVolatilityTier( + 0, + 500, // feeTier + 20, // tickSpread + 5, // twapLength (seconds) + volTier1 + ) + await enableVolTier1.wait(); + + hre.nonce += 1; + + const poolParams1: CoverPoolParams = { + poolTypeId: 0, + tokenIn: hre.props.token0.address, + tokenOut: hre.props.token1.address, + feeTier: 500, + tickSpread: 20, + twapLength: 5 + } + console.log('about to create pool') + // create first cover pool + let createPoolTxn = await hre.props.poolRouter.createCoverPoolAndMint( + poolParams1, + [] + ) + await createPoolTxn.wait(); + console.log('pool created') + hre.nonce += 1; - const poolParams1: CoverPoolParams = { - poolType: this.uniV3String, - tokenIn: hre.props.token0.address, - tokenOut: hre.props.token1.address, - feeTier: 500, - tickSpread: 20, - twapLength: 5 + [coverPoolAddress, coverPoolTokenAddress] = await hre.props.coverPoolFactory.getCoverPool( + poolParams1 + ); } - - // create first cover pool - let createPoolTxn = await hre.props.coverPoolFactory.createCoverPool( - poolParams1 - ) - await createPoolTxn.wait() - - hre.nonce += 1 - - let coverPoolAddress; let coverPoolTokenAddress; - [coverPoolAddress, coverPoolTokenAddress] = await hre.props.coverPoolFactory.getCoverPool( - poolParams1 - ) - hre.props.coverPool = await hre.ethers.getContractAt('CoverPool', coverPoolAddress) - hre.props.coverPoolToken = await hre.ethers.getContractAt('PositionERC1155', coverPoolTokenAddress) - - await this.deployAssist.saveContractDeployment( - network, - 'CoverPool', - 'coverPool', - hre.props.coverPool, - [hre.props.uniswapV3PoolMock.address] - ) - - const poolParams2: CoverPoolParams = { - poolType: this.uniV3String, - tokenIn: hre.props.token0.address, - tokenOut: hre.props.token1.address, - feeTier: 500, - tickSpread: 40, - twapLength: 10 + if (this.deployPools || hre.network.name == 'hardhat') { + hre.props.coverPool = await hre.ethers.getContractAt('CoverPool', coverPoolAddress) + hre.props.coverPoolToken = await hre.ethers.getContractAt('PositionERC1155', coverPoolTokenAddress) + + await this.deployAssist.saveContractDeployment( + network, + 'CoverPool', + 'coverPool', + hre.props.coverPool, + [] + ) } - - // create second cover pool - createPoolTxn = await hre.props.coverPoolFactory.createCoverPool( - poolParams2 - ) - await createPoolTxn.wait() - - hre.nonce += 1 - - coverPoolAddress = await hre.props.coverPoolFactory.getCoverPool( - poolParams2 - ) - hre.props.coverPool2 = await hre.ethers.getContractAt('CoverPool', coverPoolAddress) - - await this.deployAssist.saveContractDeployment( - network, - 'CoverPool', - 'coverPool2', - hre.props.coverPool2, - [hre.props.uniswapV3PoolMock.address] - ) - - await hre.props.uniswapV3PoolMock.setObservationCardinality('10', '10') - return hre.nonce } @@ -508,6 +736,15 @@ export class InitialSetup { 'readCoverPoolSetup' ) ).contractAddress + const poolsharkRouterAddress = ( + await this.contractDeploymentsJson.readContractDeploymentsJsonFile( + { + networkName: hre.network.name, + objectName: 'poolRouter', + }, + 'readCoverPoolSetup' + ) + ).contractAddress hre.props.token0 = await hre.ethers.getContractAt('Token20', token0Address) hre.props.token1 = await hre.ethers.getContractAt('Token20', token1Address) @@ -515,6 +752,7 @@ export class InitialSetup { hre.props.coverPool = await hre.ethers.getContractAt('CoverPool', coverPoolAddress) hre.props.coverPoolFactory = await hre.ethers.getContractAt('CoverPoolFactory', coverPoolFactoryAddress) hre.props.uniswapV3PoolMock = await hre.ethers.getContractAt('UniswapV3PoolMock', uniswapV3PoolMockAddress) + hre.props.poolRouter = await hre.ethers.getContractAt('PoolsharkRouter', poolsharkRouterAddress) return nonce } @@ -522,7 +760,7 @@ export class InitialSetup { public async createCoverPool(): Promise { const poolParams: CoverPoolParams = { - poolType: this.uniV3String, + poolTypeId: 0, tokenIn: hre.props.token0.address, tokenOut: hre.props.token1.address, feeTier: 500,