How to deploy contracts (defaulted to Mainnet Fork)
In the first terminal run
npx hardhat node
Open second terminal and run
npx hardhat test
Strategy
- Here we are providing liquidity for the entire range such as in the Uniswap V2.
- MockDAI address on Mumbai: 0x4d582295afB968eA3b9492c5ec594b830D180E8d
- I need to get the PoolId of the pool.
- I mostly need two things to calculate impermnent loss:
- I need to track the liquidity present the pool each time swaps happen, so that I can calculate how much the LP will get when he will withdraw his tokens.
- Then I need to calculate what is the current price of both the tokens. Using that impermanent loss can be calculated.
(This is because it will help to tell how much the tokens would have been worth if the LP would have simply held the tokens instead of providing liquidity to the Uniswap Pair)
- UniswapPool contract has liquidity variable which gives its total liquidity. Using NFT of a position, we can get the local liquidity of an LP. So if two LPs are present, then the ratio of local liquidity to total liquidity may tell how much percent of tokens will each LP get back (need to test this and verify).
- Now if you know the percent of Liquidity provided by the LP. You now want how many tokens will he get if he withdraws now. For that simply check the balance of the UniswapPool using the ERC20 token contracts. Then let's say the LP has 10 percent of the liquidity share. Then he will get back 10 percent of the total tokens that the UniswapPool holds.
Additional
- Maybe Chainlink External Adapter will be needed that queries the Uniswap subgraph and sends the necessary data to smart contract.
- Maybe Uniswap SDK will do all the required stuff instead of an external adapter.
Uniswap SDK
Uniswap Subgraph
- https://docs.uniswap.org/sdk/subgraph/subgraph-examples
- Query Uniswap LP position data using subgraph
Uniswap Docs
- https://docs.uniswap.org/protocol/guides/local-environment
- Note: The setup used here is for demonstration purposes. It must be improved with proper security checks for production use.
- https://docs.uniswap.org/protocol/reference/periphery/base/PoolInitializer
References
- Uniswap V3 Deep Dive
- Uniswap V3 Math
- Uniswap V3 whitepaper
- Get a Uniswap V3 Pool Address for a Testnet
- https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.sol
- https://ethereum.org/en/developers/tutorials/the-graph-fixing-web3-data-querying/
- Chainlink External Adapter in Typescript
- https://github.com/smartcontractkit/external-adapters-js/tree/develop/packages/examples/source
- https://github.com/atiselsts/uniswap-v3-liquidity-math
- https://github.com/hackmoney-superfluid-project/lp-automation-super-app/blob/28dc0cc3c67157d08739add5ceb88afb0a139944/uniswapFactory/contracts/UserPosition.sol
Possible Errors
- While writing the return values of the function, if there are multiple return values then you only need to declare them once in the returns statement of the function and not inside the function.
Goal
The goal is to
- Continuously monitor the impermanent loss incurred for a position in Uniswap V3
- If there is a loss, we need to exit the position Monitoring a position We can use the Uniswap V3's subgraph to know a position's details like tokens, price at the time of providing liquidity, etc. We will also know the current tick in the pool.
We can use the following Impermanent Loss Calculation.
When the loss crosses a threshold (like say 50%), we need to trigger the recovery process.
Exiting a position
We need to deploy a simple smart contract that wraps around Uniswap V3's LP exiting function and trigger this function when the above monitoring logic is executed. The output of this should result in the wallet having the LP tokens back with the position fuly exited.
Testing with MockDAI and UFO
Testing with MockDAI and SAND
- Alice Added liquidity at price 1000 DAI per SAND
- NFT with Minted for the position
- Pool id for the tokens- 0x076c373a9aeb3E2F72f45339e9e11A4D37Dc7fEf
- Bob provides some liquidity
- Bob added more liquidity
Alice before removing liquidity
- Aliceliquidity: BigNumber { value: "4627199995519345936797" } Ratio: BigNumber { value: "82" } PoolLiquidity: BigNumber { value: "5575930413013974886531" }
Alice after removing liquidity
- Aliceliquidity: BigNumber { value: "0" } Ratio: BigNumber { value: "0" } PoolLiquidity: BigNumber { value: "948730417494628949734" }
- Note: This remaining Pool liquidity is of Bob.
Caution
- When you mint new position using the Liquidity Example contract, the positions NFT is actually minted to contract. So if you had manually added liquidity, you already have the positions NFT minted and hence you won't be able to add liquidity anymore using the contract(not even the increaseLiquidityCurrentRange works then)
- tickLower and tickUpper should be properly set
Testing with MockDAI-GALA
Getting Price from Q64.96 number
- See https://ethereum.stackexchange.com/questions/98685/computing-the-uniswap-v3-pair-price-from-q64-96-number
- See the second answer and then do reciprocal of that to get actual price ratio of assets
-85200 sqrtPriceX96: 1119122402227839611194053926
-46080 sqrtPriceX96: 7912525539738091750091588668
- Python console calculations
>>> (1119122402227839611194053926 ** 2) / (2**192)
0.00019952439899256438
>>> 7912525539738091750091588668 ** 2 / 2**192
0.009974039462202664
>>> 1/0.000999901
1000.0990098019703
>>> 1/0.00019952439899256438
5011.918367122944
>>> 1/0.009974039462202664
100.2602810816592
Continued testing with MockDAI-GALA
- Alice adds mints liquidity from LiquidityExample contract