-
Notifications
You must be signed in to change notification settings - Fork 22
BPT Oracles (supporting BPT as Collateral) #289
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
Open
EndymionJkb
wants to merge
17
commits into
v3-outline
Choose a base branch
from
bpt-oracles
base: v3-outline
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
0f589e4
Add BPT Oracle docs
EndymionJkb 788c367
refactor: adjust order of other pages
EndymionJkb 5365b2a
refactor: add Weighted Pool description
EndymionJkb b3a4739
feat: add contract descriptions
EndymionJkb a2e2133
refactor: add a note about oracle support to the BPT page
EndymionJkb 8069cea
refactor: move into folder
EndymionJkb 6cf7eee
review of first page
EndymionJkb b84a55a
flail: try to render them properly
EndymionJkb 98c5ba3
flail: keep trying
EndymionJkb cf1131b
formatting
EndymionJkb 9f73580
more formatting
EndymionJkb 96d6747
fix: discussion of roots and convergence
EndymionJkb ab836c9
Merge branch 'v3-outline' into bpt-oracles
EndymionJkb 29cf664
docs: move stable-specific comments; add notes on usage with rate pro…
EndymionJkb 4347c8b
Merge branch 'v3-outline' into bpt-oracles
EndymionJkb 954dc36
Add note explaining that the given feeds must match the pool tokens. …
EndymionJkb 1f20d8a
Add comment about correcting oracle creation mistakes
EndymionJkb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
docs/concepts/core-concepts/bpt-oracles/bpt-oracles-contracts.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| --- | ||
| order: 4 | ||
| title: BPT Oracle contracts | ||
| --- | ||
| # Overview | ||
|
|
||
| We have implemented oracles for both Weighted and Stable Balancer pools. (ReCLAMM pools should also work, as they are fundamentally Weighted pools, just incorporating virtual balances.) These are in the standalone-utils package: `WeightedLPOracle` and `StableLPOracle`, and associated factories. | ||
|
|
||
|  | ||
|
|
||
| ## Oracle Factories | ||
|
|
||
| Oracles are created from the corresponding pool factories using the create function: | ||
|
|
||
| `function create(IBasePool pool, AggregatorV3Interface[] memory feeds) external returns (ILPOracleBase oracle);` | ||
|
|
||
| `AggregatorV3Interface` is a Chainlink price feed. Note that this assumes the feed array is parallel to the pool tokens; i.e., the feed at each position is the correct one for the corresponding pool token. There is no way for the code to check this, so responsibility falls on the caller to ensure that this is the case. See the [Usage with Rate Providers](./bpt-oracles.md#usage-with-rate-providers) section for further considerations. Note that there are no restrictions in the factories that limit oracle creation, so any mistakes can be remedied by simply calling create again with correct values. | ||
|
|
||
| This first computes an "OracleID" as the hash of the pool and price feed addresses. Since the tokens must be ordered, we know the price feed addresses will also be in the same order, so a given combination of the pool and price feeds is guaranteed unique. It then calls an internal `_create` (implemented by derived contracts) to deploy the oracle with the given configuration, and registers it with the factory. | ||
|
|
||
| The base factory contract [interface](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/standalone-utils/ILPOracleFactoryBase.sol) defines functions to find an oracle given pool and feed addresses, and check whether a given oracle was deployed by the official factory: | ||
|
|
||
| ``` | ||
| function getOracle( | ||
| IBasePool pool, | ||
| AggregatorV3Interface[] memory feeds | ||
| ) external view returns (ILPOracleBase oracle); | ||
|
|
||
| function isOracleFromFactory(ILPOracleBase oracle) external view returns (bool success); | ||
| ``` | ||
|
|
||
| ## Oracle contracts | ||
|
|
||
| The common factory contract [interface](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/standalone-utils/ILPOracleBase.sol) defines: | ||
|
|
||
| `function calculateTVL(int256[] memory prices) external view returns (uint256 tvl);` | ||
|
|
||
| This function computes the total value of the pool, solving the pool and price constraints simultaneously as described in the [main article](./bpt-oracles.md#derivation). | ||
|
|
||
| Just as the price feeds implement the Chainlink interface `AggregatorV3Interface` to fetch individual token prices, the common factory [contract](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/standalone-utils/contracts/LPOracleBase.sol) also implements this interface, and calling `latestRoundData` invokes the virtual `calculateTVL` function to get the total value, then divides by the total supply to get the BPT price. |
112 changes: 112 additions & 0 deletions
112
docs/concepts/core-concepts/bpt-oracles/bpt-oracles-example.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| --- | ||
| order: 4 | ||
| title: BPT Oracle Numerical Example | ||
| --- | ||
| # Numerical example | ||
|
|
||
| Consider a Stable Pool containing USDC and USDT, where USDT has slightly de-pegged (say, to 0.98). | ||
|
|
||
| p₁ = $1.00 (USDC), p₂ = $0.98 (USDT)<br> | ||
| A = 100, D = 1000 (invariant) | ||
|
|
||
| Step 1: Calculate the parameters (amplification coefficient, and "helper" constants derived from the Amplification parameter) | ||
|
|
||
| a = A × n^(2n) = 100 × 2^4 = 1600<br> | ||
| b = a - n^n = 1600 - 2^2 = 1596<br> | ||
| c = b/a = 1596/1600 = 0.9975 | ||
|
|
||
| Step 2: Calculate the r values (scaled prices) | ||
|
|
||
| r₁ = p₁/a = 1.00/1600 = 0.000625<br> | ||
| r₂ = p₂/a = 0.98/1600 = 0.0006125 | ||
|
|
||
| Step 3: Find the root, using Newton's method: | ||
|
|
||
| Choose the starting point: k₀ = (1 + 1/(1+b)) × ρ | ||
|
|
||
| k₀ = (1 + 1/(1+1596)) × 1633 = (1 + 1/1597) × 1633 ≈ 1634.02 | ||
|
|
||
| Newton's method:<br> | ||
| kₙ₊₁ = kₙ − G(kₙ) / G′(kₙ); where | ||
|
|
||
| G(k) = T(k)³ × P(k) - α<br> | ||
| G'(k) = T²(k) × P(k) × [(3T'(k) × P(k) + T(k) × P'(k))/P(k)] | ||
|
|
||
| And: | ||
| T'(k) = -r₁/(kr₁-1)² - r₂/(kr₂-1)²<br> | ||
| P'(k) = P(k) × [r₁/(kr₁-1) + r₂/(kr₂-1)] | ||
|
|
||
| First iteration (k₀ = 1634.02) | ||
|
|
||
| Calculate T(1634.02): | ||
|
|
||
| T = 1/(1634.02×0.000625-1) + 1/(1634.02×0.0006125-1) - 1<br> | ||
| T = 1/(1.0213-1) + 1/(1.0008-1) - 1<br> | ||
| T = 1/0.0213 + 1/0.0008 - 1 = 46.95 + 1250 - 1 ≈ 1295.95 | ||
|
|
||
| Calculate P(1634.02): | ||
|
|
||
| P = (1634.02×0.000625-1) × (1634.02×0.0006125-1)<br> | ||
| P = 0.0213 × 0.0008 ≈ 0.000017 | ||
|
|
||
| Calculate G(1634.02): the "error" | ||
|
|
||
| G = (1295.95)³ × 0.000017 - 1588.03<br> | ||
| G = 2.17×10⁹ × 0.000017 - 1588.03 ≈ 36,890 - 1588 ≈ 35,302 | ||
|
|
||
| Calculate derivatives and find k₁: | ||
|
|
||
| T' = -0.000625/(0.0213)² - 0.0006125/(0.0008)² ≈ -1380 - 956 ≈ -2336<br> | ||
| G' ≈ ... (complex calculation) ≈ 50.8 | ||
|
|
||
| k₁ = 1634.02 - 35,302/50.8 ≈ 1634.02 - 694.9 ≈ 939.1 | ||
|
|
||
| Continue iterations: | ||
| | Step | k̃ₙ | G(k̃ₙ) | k̃ₙ₊₁ | | ||
| | ---- | ------- | ------ | ------- | | ||
| | 0 | 1634.02 | 35,302 | 939.1 | | ||
| | 1 | 939.1. | -285.4 | 1425.7 | | ||
| | 2 | 1425.7 | 892.1 | 1580.3 | | ||
| | 3 | 1580.3 | 124.7 | 1635.8 | | ||
| | 4 | 1635.8 | 8.2 | 1640.1 | | ||
| | 5 | 1640.1 | 0.1 | 1641.0 | | ||
| | 6 | 1641.0 | ~0 | 1641.0 | | ||
|
|
||
| Graphical representation of the T curve: | ||
|
|
||
|  | ||
|
|
||
| Step 4: Calculate T (using our found k̃ = 1641) | ||
|
|
||
| Recall that T = 1/(k̃r₁ - 1) + 1/(k̃r₂ - 1) - 1 | ||
|
|
||
| T = 1/(1641 × 0.000625 - 1) + 1/(1641 × 0.0006125 - 1) - 1<br> | ||
| T = 1/(1.025625 - 1) + 1/(1.0051125 - 1) - 1<br> | ||
| T = 1/0.025625 + 1/0.0051125 - 1<br> | ||
| T = 39.02 + 195.60 - 1 = 233.62 | ||
|
|
||
| Step 5: Calculate effective balances | ||
|
|
||
| Recall that x₁ = (cD)/(k̃r₁ - 1) × T⁻¹ | ||
|
|
||
| x₁ = (0.9975 × 1000)/(1641 × 0.000625 - 1) × (1/233.62)<br> | ||
| x₁ = 997.5/0.025625 × (1/233.62) = 166.69 | ||
|
|
||
| x₂ = (cD)/(k̃r₂ - 1) × T⁻¹<br> | ||
| x₂ = (0.9975 × 1000)/(1641 × 0.0006125 - 1) × (1/233.62)<br> | ||
| x₂ = 997.5/0.0051125 × (1/233.62) = 833.31 | ||
|
|
||
| Step 6: Calculate total pool value | ||
|
|
||
| Recall that Pool Value = p₁ × x₁ + p₂ × x₂ | ||
|
|
||
| Pool Value = $1.00 × 166.69 + $0.98 × 833.31<br> | ||
| Pool Value = $166.69 + $816.64 = $983.33 | ||
|
|
||
| Step 7: Calculate BPT price | ||
|
|
||
| Recall that BPT Price = Pool Value / Total BPT Supply | ||
|
|
||
| (If total BPT supply = 1000 tokens, since D = 1000, and starting prices were nominal at $1) | ||
|
|
||
| BPT Price = $983.33 / 1000 = $0.983 per BPT | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 page is simply awesome!