New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Ondo_ILP as a price identifier #490
Conversation
Signed-off-by: Alex Gaines <againes@umaproject.org>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very detailed example to follow, thank you
Implementations/ondo-ilp.md
Outdated
# Price Feed Implementation | ||
|
||
``` | ||
// Step 1: Copy the allPairVault contract abi and paste it in a json file named 'abi.json' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: where to find the allPairVault contract abi?
Implementations/ondo-ilp.md
Outdated
}, function(error, events){ (events) }) | ||
.then(function(events){ | ||
for (i = 0; i < events.length; i++) { | ||
if (events[i].raw.topics[1] === vaultId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since vaultId
is indexed in the Invested
events it would be more efficient to call getPastEvents
with filter: {vaultId: vaultId}
added to its options parameter.
The same can also be done to Redeemed
events below.
Implementations/ondo-ilp.md
Outdated
2. Scan the `Invested` events from the `VaultContractAddress` and retrieve the event that matches the Vault ID for the second element of the topics array. | ||
- In the `returnValues` response, note the `seniorAmount` and `juniorAmount` and confirm the values are associated with your `VaultId` from the contracts ancillary data. Scale both values 18 decimals. | ||
3. Scan the `Redeemed` events from the `VaultContractAddress` and retrieve the event that matches the Vault ID for the second element of the topics array. | ||
- In the `returnValues` response, note the `seniorReceived` and `juniorReceived` and confirm the values are associated with your `VaultId` from the contracts ancillary data. Scale both values 18 decimals. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for Ondo vaults to be partially invested/redeemed or have these split in multiple transactions that would affect this calculation?
Implementations/ondo-ilp.md
Outdated
- In the `returnValues` response, note the `seniorReceived` and `juniorReceived` and confirm the values are associated with your `VaultId` from the contracts ancillary data. Scale both values 18 decimals. | ||
4. Calculate the price ratio at the `Redeemed` event using values from step 3: | ||
``` | ||
redeemPriceRatio = juniorReceived / seniorReceived |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the interpretation of redeemPriceRatio
- is it the price of one vault asset expressed in terms of the other pair? If so this would require access to underlying strategy pool reserves since amounts received in Redeemed
event also include rebalancing of tranches (senior position holders only can receive up to hurdle rate while any remaining senior assets from LP withdrawal are swapped to junior assets).
Implementations/ondo-ilp.md
Outdated
``` | ||
7. Calculate the performance if the holder provided liquidity to the Ondo vault. ProductConstant is from step 5 and redeemPriceRatio is from step 4: | ||
``` | ||
The square root of ((productConstant / redeemPriceRatio) * redeemPriceRatio) plus the square root of (productConstant * redeemPriceRatio) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate the underlying reason for this formula?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the script in Price Feed Implementation section slightly differs as it takes square root of (productConstant / redeemPriceRatio)
Implementations/ondo-ilp.md
Outdated
// Calculates the stragety to compare providing liquidity vs holding the assets | ||
const nonLpStrategy = ((seniorInvest * redeemPriceRatio) + juniorInvest); | ||
|
||
const lpStrategy = ((Math.sqrt(productConstant / redeemPriceRatio)) * redeemPriceRatio) + (Math.sqrt(productConstant * redeemPriceRatio)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this expression can be simplified to:
const lpStrategy = Math.sqrt(productConstant * redeemPriceRatio) * 2
Though it differs a bit from expression described in the Implementation section Step 7
|
||
# Motivation | ||
|
||
This identifier sets a general standard for calculating Ondo IL and removes the need to create individual proposals for each vault. The objective of this identifier is to provide impermanent loss protection to liquidity providers of Ondo vaults. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to better understand formulas in implementation section it would be helpful to understand better whose IL is being measured - is it for senior + junior liquidity providers as a group? Or only from perspective of junior liquidity providers who take on leverage and underlying pool IL risk?
Signed-off-by: Alex Gaines <againes@umaproject.org>
Signed-off-by: Alex Gaines <againes@umaproject.org>
Signed-off-by: Alex Gaines <againes@umaproject.org>
const utcVersion = new Date(timestamp * 1000).toUTCString(); | ||
let block = await dater.getDate( | ||
utcVersion, | ||
true // to search for block before request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parameter is for "Block after", so in order to search for block before request this needs to be set to false
// get reserves, supply, and pool balance based on the blockNumber | ||
let reserves = await pair.methods.getReserves().call({}, blockNumber); | ||
let ondoBalance = await poolContract.methods | ||
.balanceOf(strategy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cannot reliably query Ondo vault balance as the same strategy contract and pool address can be shared by multiple vaultID
.
Also SushiSwap strategy deposits LP tokens to MasterChef staking contract, thus balanceOf
call would be wrong.
let poolContract = new web3.eth.Contract(poolAbi, poolAddress); | ||
let pair = new web3.eth.Contract(poolAbi, poolAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the difference between poolContract
and pair
contract instances if they are based on the same poolAbi
and poolAddress
?
const nonLpStrategy = token0Hold + token1Hold; | ||
|
||
// Calculates IL | ||
const impermanentLoss = ((nonLpStrategy - lpStrategy) / nonLpStrategy) * 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nonLpStrategy
is value of all pool reserves while lpStrategy
has it adjusted by ondoPoolPercentage
* Based on CoinGecko [API documentation](https://www.coingecko.com/api/documentations/v3#/contract/get_coins__id__contract__contract_address__market_chart_range) construct price API request with following parameters: | ||
* `id`: CoinGecko platform id - Ondo vaults currently only use "ethereum"; | ||
* `contract_address`: reserve token address from Step 4; | ||
* `vs_currency`: TVL measurement currency based on the passed `TVLCurrency` parameter in the ancillary data (e.g. "usd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no TVLCurrency
parameter specified in ancillary data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the point of getting all values in USD and why CoinGecko data is required. For IL calculation we need only relative pricing (token0 in terms of token 1) and that can be obtained from pool contract reserves. Sure, this works only for Uniswap v2 and SushiSwap, but the script above is already dependent on this assumption.
* `from`: start timestamp (`StartTimestamp` parameter from the ancillary data); | ||
* `to`: end timestamp (`EndTimestamp` parameter from the ancillary data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How this would be different for each of evaluation timestamps from Step 1?
6. Scale down LP reserve balances from Step 4 with their respective decimals (call `decimals()` method on the token contracts from Step 2). | ||
7. Multiply the LP reserve token balances at the `EndTimestamp` from Step 6 with the `EndTimestamp` price value from Step 5. | ||
8. Sum the LP reserve balances returned from Step 7 to get the total value of the pool at the `EndTimestamp`. | ||
9. Get the total LP token supply by calling `totalSupply()` method on the LP contract from Step 3 at the latest available block at or before the `EndTimestamp`. Get the portion of the pool balance owned by the vault at the same timestamp by calling the `balanceOf()` method on the `poolAddress` using the `vaultStrategy` from Step 2 as the argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments above to the script why balanceOf()
method is not reliable for Ondo
10. Divide the balance of the vault strategy by the total supply of the pool from Step 9 to get the percentage of the pool owned by the vault. | ||
11. Multiply the total reserve balances from step 8 by the percentage of the pool owned by the vault from step 10 to get the value of the vault for the `EndTimestamp`. | ||
12. Identify the value of the two reserve currencies from step 2 if liquidity had not been provided to the pool by using the `StartTimestamp` reserve balances from step 4 multiplied by the CoinGecko `EndTimestamp` prices from step 5. Sum these values together to get the total value of the pool for the `EndTimestamp` based on the quantities from the `StartTimestamp`. | ||
13. Calculate impermanent loss by subtracting the returned value from step 11 from the returned value from step 12 and then dividing the returned value by the returned value from step 11. Multiply by 100 and round the returned value to 6 decimals to arrive at the final value as a percentage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note the same comment to script above why Ondo vault value cannot be compared to total LP reserves value
``` | ||
* Get the total LP token supply by calling `totalSupply()` method on the LP contract and the portion of the pool balance owned by the vault by calling the `balanceOf()` method for the vault strategy at the `EndTimestamp`. Scale the values based on the `decimals()` method on the token contracts and then divide the balance of the vault by the total pool and multiply the returned value by the total value of the pool: | ||
``` | ||
(4326814.937110206473452784 / 4326814.937110206473453784) * 4605668.912359925 = 4605668.912359925 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure what does 4326814.937110206473452784 / 4326814.937110206473453784
represent - is it Ondo balance value divided by itself so always should be 1?
StartTimestamp:1645581288, | ||
EndTimestamp:1647737313 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who chooses these timestamps and how do they relate to actual Ondo vault lifecycle?
Signed-off-by: Alex Gaines againes@umaproject.org