Skip to content
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

Closed
wants to merge 4 commits into from
Closed

Add Ondo_ILP as a price identifier #490

wants to merge 4 commits into from

Conversation

abg4
Copy link
Contributor

@abg4 abg4 commented Feb 28, 2022

Signed-off-by: Alex Gaines againes@umaproject.org

Signed-off-by: Alex Gaines <againes@umaproject.org>
therealhexi
therealhexi previously approved these changes Mar 7, 2022
Copy link

@therealhexi therealhexi left a 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

# Price Feed Implementation

```
// Step 1: Copy the allPairVault contract abi and paste it in a json file named 'abi.json'

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?

}, function(error, events){ (events) })
.then(function(events){
for (i = 0; i < events.length; i++) {
if (events[i].raw.topics[1] === vaultId) {
Copy link
Contributor

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.

Comment on lines 167 to 170
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.
Copy link
Contributor

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?

- 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
Copy link
Contributor

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).

```
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)
Copy link
Contributor

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?

Copy link
Contributor

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)

// 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));
Copy link
Contributor

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.
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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.

Comment on lines +211 to +212
let poolContract = new web3.eth.Contract(poolAbi, poolAddress);
let pair = new web3.eth.Contract(poolAbi, poolAddress);
Copy link
Contributor

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;
Copy link
Contributor

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");
Copy link
Contributor

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

Copy link
Contributor

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.

Comment on lines +272 to +273
* `from`: start timestamp (`StartTimestamp` parameter from the ancillary data);
* `to`: end timestamp (`EndTimestamp` parameter from the ancillary data);
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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
Copy link
Contributor

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?

Comment on lines +49 to +50
StartTimestamp:1645581288,
EndTimestamp:1647737313
Copy link
Contributor

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?

@smb2796 smb2796 closed this Apr 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants