From 44b22fdec84b7357fdfe94e61f5cbf6d74a42293 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 17 May 2023 14:14:30 +0200 Subject: [PATCH 01/36] docs: add rateProvider specifics --- docs/reference/contracts/rate-providers.md | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 54080ac4..57184e84 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -39,3 +39,50 @@ This contract makes use of Chainlink's registry contract so it can handle if Cha #### [`ChainlinkRateProvider`](https://github.com/balancer/metastable-rate-providers/blob/master/contracts/ChainlinkRateProvider.sol) If you're running on a network for which Chainlink doesn't have a registry and you think the risk of a deprecated price feed is low enough, then you can use the rateProvider that directly queries a given Chainlink oracle. + +## RateProviders as part of a pool's lifecycle +| PoolType | Yield Fee | Pricing Equations| +| ----------- | ----------- | ----------- | +| Composable Stable Pool| ✅ | ✅ | +| Weighted Pool | ✅ | ❌ | +| Managed Pool | Text | Text | +| Custom Pool | Text | Text | + +Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the rateProvider, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. + +Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns +20040415915824227571764 for rETH and 21953505292747563228232 for Weth. + +Before these balances are being used in the Trade equations two operations will be done to them: +- 1. Upscaling. +- 2. Applying the rate +Effectively changing the value of the balances reported via the earlier call to `vault.getPoolTokens(poolId)`. + Implementation for MetaStablePools (superseded by ComposableStablePool) + +``` +function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { + uint256 baseScalingFactor = super._scalingFactor(token); + uint256 priceRate = _priceRate(token); + // Given there is no generic direction for this rounding, it simply follows the same strategy as the BasePool. + return baseScalingFactor.mulDown(priceRate); + } +``` + + Implementation for Composable Stable Pool + +``` +function _scalingFactors() internal view virtual override returns (uint256[] memory) { + // There is no need to check the arrays length since both are based on `_getTotalTokens` + uint256 totalTokens = _getTotalTokens(); + uint256[] memory scalingFactors = new uint256[](totalTokens); + + for (uint256 i = 0; i < totalTokens; ++i) { + scalingFactors[i] = _getScalingFactor(i).mulDown(_getTokenRate(i)); + } + + return scalingFactors; + } +``` + +The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11). In the example transaction the tokenOutBalances which are fed into +[`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2) is 21445684973708525874136. Meaning the rate, the providers supplies is "baked in" the balances. From 9502b412f19dfa9fb8fb6bfb63824a828c26fdf7 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Thu, 25 May 2023 11:18:32 +0200 Subject: [PATCH 02/36] add yield fee calculation. --- docs/reference/contracts/rate-providers.md | 132 +++++++++++++++++---- 1 file changed, 107 insertions(+), 25 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 57184e84..817ae3df 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -41,34 +41,31 @@ This contract makes use of Chainlink's registry contract so it can handle if Cha If you're running on a network for which Chainlink doesn't have a registry and you think the risk of a deprecated price feed is low enough, then you can use the rateProvider that directly queries a given Chainlink oracle. ## RateProviders as part of a pool's lifecycle -| PoolType | Yield Fee | Pricing Equations| -| ----------- | ----------- | ----------- | -| Composable Stable Pool| ✅ | ✅ | -| Weighted Pool | ✅ | ❌ | -| Managed Pool | Text | Text | -| Custom Pool | Text | Text | -Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the rateProvider, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. +Different pool types utilise rate Providers in varying contexts. -Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns -20040415915824227571764 for rETH and 21953505292747563228232 for Weth. +| PoolType | Yield Fee | Pricing Equations| +| ----------- | ----------- | ----------- | +| Composable Stable Pool | ✅ | ✅ | +| Meta Stable (EOL) | ✅ | ✅ | +| Weighted Pool | ✅ | ❌ | +| Managed Pool | ❓ | ❓ | +| Custom Pool | ❓ | ❓ | -Before these balances are being used in the Trade equations two operations will be done to them: -- 1. Upscaling. -- 2. Applying the rate -Effectively changing the value of the balances reported via the earlier call to `vault.getPoolTokens(poolId)`. - Implementation for MetaStablePools (superseded by ComposableStablePool) -``` -function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { - uint256 baseScalingFactor = super._scalingFactor(token); - uint256 priceRate = _priceRate(token); - // Given there is no generic direction for this rounding, it simply follows the same strategy as the BasePool. - return baseScalingFactor.mulDown(priceRate); - } -``` +One important aspect to determine a trade's outcome are the Token balances a pool contains. Before the reported balances by the Vault are being used in the Trade equations for StableMath two operations will be done to them: + +- 1. Upscaling to Balancer's internal 18 decimals. +- 2. Applying the rate via multiplication. + +These operations supply apparent balances to the StableMath equations effectively changing the value of the balances reported via the earlier call to `vault.getPoolTokens(poolId)`. + +ComposableStablePools & MetaStablePools have different implementations for the above operations but the outcome is the same. + - Implementation for Composable Stable Pool +### Implementation for Composable Stable Pool + +The scaling factor that is used can be seen in the below code snippet. ``` function _scalingFactors() internal view virtual override returns (uint256[] memory) { @@ -84,5 +81,90 @@ function _scalingFactors() internal view virtual override returns (uint256[] mem } ``` -The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11). In the example transaction the tokenOutBalances which are fed into -[`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2) is 21445684973708525874136. Meaning the rate, the providers supplies is "baked in" the balances. +### Example for a Composable Stable Pool swap utilising rate providers + +Looking at this [transaction](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3). 0.1575 staked Frax Ether is being exchanged for 0.1456 wrapped staked Ether. This pool is a Composable Stable Pool. According to the table the rate, the rateProvider provides a rate which is being used in the Trade Equations. + +This trade was executed in Block 17233083. Looking at the token balances the pool has before the swap it can be seen that: + +| Token | balances | rate | scaled balance | +| ----------- | ----------- | ----------- | ----------- | +| BPT - 0x5aee1e99fe86960377de9f88689616916d5dcabe | 2596148429265848431954582359320590 | 100000000000000000 | 2596148429265848431954582359320590 | +| wstEth - 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0 | 6998331684674570187580 | 1123265399574462175 | 7860983836140600107855 | +| sfrxEth - 0xac3e018457b222d93114458476f3e3416abbe38f | 7388958961745977404526 | 1038371994655823641 | 7672488055538194248508 | +| rEth - 0xae78736cd615f374d3085123a210448e74fc6393 | 5950507951882438548950 | 1069881935087994199 | 6366340962316480428138 | + +** Bear in mind that the tokens used for demonstration in this doc all have 18 decimals and Balancer natively uses 18 decimals. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. + +After the token balances have been upscaled, they are fed into `_calcOutGivenIn`[here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). +That is how RateProviders feed into the pricing equation for ComposableStablePool and for MetaStablePool + +### Implementation in a MetaStablePool +The scaling factor that is used can be seen in the below code snippet. + +``` +function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { + uint256 baseScalingFactor = super._scalingFactor(token); + uint256 priceRate = _priceRate(token); + // Given there is no generic direction for this rounding, it simply follows the same strategy as the BasePool. + return baseScalingFactor.mulDown(priceRate); + } +``` + +### Example for MetaStablePools (superseded by ComposableStablePool) swap utilising rate providers. + +Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the rateProvider, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. + +Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns +20040415915824227571764 for rETH and 21953505292747563228232 for Weth. + +| Token | balances | rate | scaled balance | +| ----------- | ----------- | ----------- | ----------- | +| reth 0xae78736cd615f374d3085123a210448e74fc6393 | 6998331684674570187580 | 1070121751154609309 | 21445684973708525874136 | +| Weth 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 7388958961745977404526 | 1000000000000000000 | 21953505292747563228232 | + +The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11) the scaled balance. Only after upscaling are the apparent balances supplied to +[`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). + + +### RateProvider being used in a MetaStablePool swap + +## RateProviders being used to collect yieldFees. + +Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: + +The rateProduct is calculated as the weighted product of all current rates: + +| Token | Weight | rate | +| ----------- | ----------- | ----------- | +| A (yield bearing) | 0.3 | 1.01 | +| B (non yield bearing) | 0.5 | 1.00 | +| C (bearing) | 0.2 | 1.05 | + +The rate product is obtained by multiplying the weighted rates together: + +Rate product = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 + +As part of the calculation of the rateProduct, the rate providers of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: + +``` +/** + * @notice Returns the contribution to the total rate product from a token with the given weight and rate provider. + */ + function _getRateFactor(uint256 normalizedWeight, IRateProvider provider) internal view returns (uint256) { + return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); + } +``` + +There are several scenarios in which no yield fees are paid during a pool join or exit operation. Here are a couple of examples: + +- Rate providers only update with a given frequency: If multiple joins or exits occur without any factors contributing to a newATHRateProduct, no yield fees are minted. This means that if the rate providers have not updated their rates within the given timeframe, the rateProduct remains unchanged and no yield fees are charged. +- Rate fluctuations of different tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pool join or exit, as the rateProduct did not increase sufficiently. + + +As part of this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0x9e1d45013f4b65f444bb9b2ef823c0d4fd0a53e2b2bad85ba85a8e26c0bed45d?trace=0.2.7.3.7.1.2.2) yield fees are minted. This can be seen by the positive return values of the `_getYieldProtocolFeesPoolPercentage` function + +Whereas in this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0xe2d8d7f705d23c18e7d25e68bf01ac2544cc73806c8e4572135d9bc16790b4f5) the `ATHRateProduct` did not increase +and (0,0) is [returned](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L191) since `rateProduct <= athRateProduct`. This indicates that no yield fees were paid in this transaction due to the insufficient increase in the rateProduct. + + From 07a2f73897642dc96b306554d3fca7b426847dbf Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Thu, 25 May 2023 11:56:37 +0200 Subject: [PATCH 03/36] change rate provider applicability --- docs/reference/contracts/rate-providers.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 817ae3df..9b6850f3 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -49,8 +49,7 @@ Different pool types utilise rate Providers in varying contexts. | Composable Stable Pool | ✅ | ✅ | | Meta Stable (EOL) | ✅ | ✅ | | Weighted Pool | ✅ | ❌ | -| Managed Pool | ❓ | ❓ | -| Custom Pool | ❓ | ❓ | +| Managed Pool | ✅ | ❌ | One important aspect to determine a trade's outcome are the Token balances a pool contains. Before the reported balances by the Vault are being used in the Trade equations for StableMath two operations will be done to them: @@ -129,7 +128,7 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb ### RateProvider being used in a MetaStablePool swap -## RateProviders being used to collect yieldFees. +## RateProviders being used to collect yieldFees for WeightedPools Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: From 5b45c71d2b84f00034592512e07cde6894313d9b Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Tue, 30 May 2023 09:42:55 +0200 Subject: [PATCH 04/36] docs: spelling for rate Provider --- docs/reference/contracts/rate-providers.md | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 9b6850f3..94f2e7d9 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -40,16 +40,17 @@ This contract makes use of Chainlink's registry contract so it can handle if Cha If you're running on a network for which Chainlink doesn't have a registry and you think the risk of a deprecated price feed is low enough, then you can use the rateProvider that directly queries a given Chainlink oracle. -## RateProviders as part of a pool's lifecycle +## Rate Providers as part of a pool's lifecycle -Different pool types utilise rate Providers in varying contexts. +Different pool types utilise rate providers in varying contexts. -| PoolType | Yield Fee | Pricing Equations| -| ----------- | ----------- | ----------- | -| Composable Stable Pool | ✅ | ✅ | -| Meta Stable (EOL) | ✅ | ✅ | -| Weighted Pool | ✅ | ❌ | -| Managed Pool | ✅ | ❌ | +| PoolType | Yield Fee | Pricing Equations| +| ----------- | ----------- | ----------- | +| Composable Stable Pool | ✅ | ✅ | +| Meta Stable (EOL) | ✅ | ✅ | +| Weighted Pool | ✅ | ❌ | +| Managed Pool | ✅ | ❌ | +| Liquity Bootstrapping Pool | ✅ | ❌ | One important aspect to determine a trade's outcome are the Token balances a pool contains. Before the reported balances by the Vault are being used in the Trade equations for StableMath two operations will be done to them: @@ -80,9 +81,9 @@ function _scalingFactors() internal view virtual override returns (uint256[] mem } ``` -### Example for a Composable Stable Pool swap utilising rate providers +### Example for a Composable Stable Pool swap utilising rate Providers -Looking at this [transaction](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3). 0.1575 staked Frax Ether is being exchanged for 0.1456 wrapped staked Ether. This pool is a Composable Stable Pool. According to the table the rate, the rateProvider provides a rate which is being used in the Trade Equations. +Looking at this [transaction](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3). 0.1575 staked Frax Ether is being exchanged for 0.1456 wrapped staked Ether. This pool is a Composable Stable Pool. According to the table the rate, the `rateProvider` provides a rate which is being used in the Trade Equations. This trade was executed in Block 17233083. Looking at the token balances the pool has before the swap it can be seen that: @@ -96,7 +97,7 @@ This trade was executed in Block 17233083. Looking at the token balances the poo ** Bear in mind that the tokens used for demonstration in this doc all have 18 decimals and Balancer natively uses 18 decimals. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. After the token balances have been upscaled, they are fed into `_calcOutGivenIn`[here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). -That is how RateProviders feed into the pricing equation for ComposableStablePool and for MetaStablePool +That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools and for MetaStablePools ### Implementation in a MetaStablePool The scaling factor that is used can be seen in the below code snippet. @@ -110,9 +111,9 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui } ``` -### Example for MetaStablePools (superseded by ComposableStablePool) swap utilising rate providers. +### Example for MetaStablePools (superseded by ComposableStablePool) swap utilising rate Providers. -Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the rateProvider, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. +Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the `rateProvider`, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns 20040415915824227571764 for rETH and 21953505292747563228232 for Weth. @@ -126,9 +127,9 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb [`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). -### RateProvider being used in a MetaStablePool swap +### Rate Provider being used in a MetaStablePool swap -## RateProviders being used to collect yieldFees for WeightedPools +## Rate Providers being used to collect yieldFees for WeightedPools Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: @@ -144,11 +145,11 @@ The rate product is obtained by multiplying the weighted rates together: Rate product = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 -As part of the calculation of the rateProduct, the rate providers of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: +As part of the calculation of the rateProduct, the `rateProvider` of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: ``` /** - * @notice Returns the contribution to the total rate product from a token with the given weight and rate provider. + * @notice Returns the contribution to the total rate product from a token with the given weight and rate Provider. */ function _getRateFactor(uint256 normalizedWeight, IRateProvider provider) internal view returns (uint256) { return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); @@ -157,7 +158,7 @@ As part of the calculation of the rateProduct, the rate providers of the pool to There are several scenarios in which no yield fees are paid during a pool join or exit operation. Here are a couple of examples: -- Rate providers only update with a given frequency: If multiple joins or exits occur without any factors contributing to a newATHRateProduct, no yield fees are minted. This means that if the rate providers have not updated their rates within the given timeframe, the rateProduct remains unchanged and no yield fees are charged. +- Rate Providers only update with a given frequency: If multiple joins or exits occur without any factors contributing to a newATHRateProduct, no yield fees are minted. This means that if the rate Providers have not updated their rates within the given timeframe, the rateProduct remains unchanged and no yield fees are charged. - Rate fluctuations of different tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pool join or exit, as the rateProduct did not increase sufficiently. From 9b3d4f15f64db3c974acfbec396a955b1936d7f1 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Tue, 30 May 2023 10:06:20 +0200 Subject: [PATCH 05/36] docs: added clarity --- docs/reference/contracts/rate-providers.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 94f2e7d9..89609578 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -50,7 +50,7 @@ Different pool types utilise rate providers in varying contexts. | Meta Stable (EOL) | ✅ | ✅ | | Weighted Pool | ✅ | ❌ | | Managed Pool | ✅ | ❌ | -| Liquity Bootstrapping Pool | ✅ | ❌ | +| Liquity Bootstrapping Pool | ❌ | ❌ | One important aspect to determine a trade's outcome are the Token balances a pool contains. Before the reported balances by the Vault are being used in the Trade equations for StableMath two operations will be done to them: @@ -60,12 +60,12 @@ One important aspect to determine a trade's outcome are the Token balances a poo These operations supply apparent balances to the StableMath equations effectively changing the value of the balances reported via the earlier call to `vault.getPoolTokens(poolId)`. -ComposableStablePools & MetaStablePools have different implementations for the above operations but the outcome is the same. +ComposableStablePools & MetaStablePools have different implementations for the scaling operations but the outcome is the same. ### Implementation for Composable Stable Pool -The scaling factor that is used can be seen in the below code snippet. +How scaling is used can be seen in the below code snippet. ``` function _scalingFactors() internal view virtual override returns (uint256[] memory) { @@ -94,13 +94,13 @@ This trade was executed in Block 17233083. Looking at the token balances the poo | sfrxEth - 0xac3e018457b222d93114458476f3e3416abbe38f | 7388958961745977404526 | 1038371994655823641 | 7672488055538194248508 | | rEth - 0xae78736cd615f374d3085123a210448e74fc6393 | 5950507951882438548950 | 1069881935087994199 | 6366340962316480428138 | -** Bear in mind that the tokens used for demonstration in this doc all have 18 decimals and Balancer natively uses 18 decimals. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. +\*Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. -After the token balances have been upscaled, they are fed into `_calcOutGivenIn`[here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). -That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools and for MetaStablePools +After the token balances have been upscaled, they are fed into `_calcOutGivenIn` [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). +That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools. ### Implementation in a MetaStablePool -The scaling factor that is used can be seen in the below code snippet. +How scaling is used can be seen in the below code snippet. ``` function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { @@ -113,7 +113,7 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui ### Example for MetaStablePools (superseded by ComposableStablePool) swap utilising rate Providers. -Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the table the rate, the `rateProvider`, supplies is being taken into account during the swap. Looking at the Trade equations, the best suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. +Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns 20040415915824227571764 for rETH and 21953505292747563228232 for Weth. From 7a149254d5d54f7cfbaca085f582fc404f0a66c5 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Tue, 30 May 2023 10:07:21 +0200 Subject: [PATCH 06/36] docs: use correct balances values --- docs/reference/contracts/rate-providers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 89609578..9aa24900 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -120,8 +120,8 @@ Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns | Token | balances | rate | scaled balance | | ----------- | ----------- | ----------- | ----------- | -| reth 0xae78736cd615f374d3085123a210448e74fc6393 | 6998331684674570187580 | 1070121751154609309 | 21445684973708525874136 | -| Weth 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 7388958961745977404526 | 1000000000000000000 | 21953505292747563228232 | +| reth 0xae78736cd615f374d3085123a210448e74fc6393 | 20040415915824227571764 | 1070121751154609309 | 21445684973708525874136 | +| Weth 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 21953505292747563228232 | 1000000000000000000 | 21953505292747563228232 | The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11) the scaled balance. Only after upscaling are the apparent balances supplied to [`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). From f33f06d4ea365a8a5df4165d35bef3381b45208a Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Tue, 30 May 2023 10:10:04 +0200 Subject: [PATCH 07/36] docs: spelling of tokens --- docs/reference/contracts/rate-providers.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 9aa24900..dabf231d 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -90,9 +90,9 @@ This trade was executed in Block 17233083. Looking at the token balances the poo | Token | balances | rate | scaled balance | | ----------- | ----------- | ----------- | ----------- | | BPT - 0x5aee1e99fe86960377de9f88689616916d5dcabe | 2596148429265848431954582359320590 | 100000000000000000 | 2596148429265848431954582359320590 | -| wstEth - 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0 | 6998331684674570187580 | 1123265399574462175 | 7860983836140600107855 | -| sfrxEth - 0xac3e018457b222d93114458476f3e3416abbe38f | 7388958961745977404526 | 1038371994655823641 | 7672488055538194248508 | -| rEth - 0xae78736cd615f374d3085123a210448e74fc6393 | 5950507951882438548950 | 1069881935087994199 | 6366340962316480428138 | +| wstETH - 0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0 | 6998331684674570187580 | 1123265399574462175 | 7860983836140600107855 | +| sfrxETH - 0xac3e018457b222d93114458476f3e3416abbe38f | 7388958961745977404526 | 1038371994655823641 | 7672488055538194248508 | +| rETH - 0xae78736cd615f374d3085123a210448e74fc6393 | 5950507951882438548950 | 1069881935087994199 | 6366340962316480428138 | \*Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. @@ -116,12 +116,12 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns -20040415915824227571764 for rETH and 21953505292747563228232 for Weth. +20040415915824227571764 for rETH and 21953505292747563228232 for WETH. | Token | balances | rate | scaled balance | | ----------- | ----------- | ----------- | ----------- | -| reth 0xae78736cd615f374d3085123a210448e74fc6393 | 20040415915824227571764 | 1070121751154609309 | 21445684973708525874136 | -| Weth 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 21953505292747563228232 | 1000000000000000000 | 21953505292747563228232 | +| rEth 0xae78736cd615f374d3085123a210448e74fc6393 | 20040415915824227571764 | 1070121751154609309 | 21445684973708525874136 | +| WETH 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 21953505292747563228232 | 1000000000000000000 | 21953505292747563228232 | The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11) the scaled balance. Only after upscaling are the apparent balances supplied to [`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). From a844d195cbf2df139da79ce2a9ccf096ac9a5d5b Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:42:23 +0200 Subject: [PATCH 08/36] docs: address some comments --- docs/reference/contracts/rate-providers.md | 31 +++++++++------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index f1d326a7..82c01818 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -40,9 +40,9 @@ This contract makes use of Chainlink's registry contract so it can handle if Cha If you're running on a network for which Chainlink doesn't have a registry and you think the risk of a deprecated price feed is low enough, then you can use the rateProvider that directly queries a given Chainlink oracle. -## Rate Providers as part of a pool's lifecycle +## Application of Rate Providers By Pool Type -Different pool types utilise rate providers in varying contexts. +Different types of pools utilize rate providers in different contexts. | PoolType | Yield Fee | Pricing Equations| | ----------- | ----------- | ----------- | @@ -53,19 +53,17 @@ Different pool types utilise rate providers in varying contexts. | Liquity Bootstrapping Pool | ❌ | ❌ | -One important aspect to determine a trade's outcome are the Token balances a pool contains. Before the reported balances by the Vault are being used in the Trade equations for StableMath two operations will be done to them: +Stable Pools require the reported token balances from , `vault.getPoolTokens(poolId)`, be manipulated in two distinct manners before prices can be calculated using StableMath: -- 1. Upscaling to Balancer's internal 18 decimals. -- 2. Applying the rate via multiplication. +1. Scale token balances to an 18-decimal fixed point number. +2. Multiply the scaled balance by the rate. -These operations supply apparent balances to the StableMath equations effectively changing the value of the balances reported via the earlier call to `vault.getPoolTokens(poolId)`. ComposableStablePools & MetaStablePools have different implementations for the scaling operations but the outcome is the same. -### Implementation for Composable Stable Pool - -How scaling is used can be seen in the below code snippet. +### Composable Stable Pool Implementation +Scaling Example: ``` function _scalingFactors() internal view virtual override returns (uint256[] memory) { @@ -81,9 +79,7 @@ function _scalingFactors() internal view virtual override returns (uint256[] mem } ``` -### Example for a Composable Stable Pool swap utilising rate Providers - -Looking at this [transaction](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3). 0.1575 staked Frax Ether is being exchanged for 0.1456 wrapped staked Ether. This pool is a Composable Stable Pool. According to the table the rate, the `rateProvider` provides a rate which is being used in the Trade Equations. +### Composable Stable Pool Swap Example This trade was executed in Block 17233083. Looking at the token balances the pool has before the swap it can be seen that: @@ -99,8 +95,8 @@ This trade was executed in Block 17233083. Looking at the token balances the poo After the token balances have been upscaled, they are fed into `_calcOutGivenIn` [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools. -### Implementation in a MetaStablePool -How scaling is used can be seen in the below code snippet. +### Meta Stable Pool Implementation +Scaling Example: ``` function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { @@ -111,8 +107,7 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui } ``` -### Example for MetaStablePools (superseded by ComposableStablePool) swap utilising rate Providers. - +### Meta Stable Pool Swap Example Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns @@ -134,12 +129,12 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: The rateProduct is calculated as the weighted product of all current rates: - +## Yield Fees for WeightedPools | Token | Weight | rate | | ----------- | ----------- | ----------- | | A (yield bearing) | 0.3 | 1.01 | | B (non yield bearing) | 0.5 | 1.00 | -| C (bearing) | 0.2 | 1.05 | +| C (yield bearing) | 0.2 | 1.05 | The rate product is obtained by multiplying the weighted rates together: From 4987734d47bacaca370fb9d39dbd466278d2ba24 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:45:29 +0200 Subject: [PATCH 09/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 82c01818..0c8b7ed3 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -128,7 +128,7 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: -The rateProduct is calculated as the weighted product of all current rates: +`rateProduct` is calculated as the weighted product of all current rates: ## Yield Fees for WeightedPools | Token | Weight | rate | | ----------- | ----------- | ----------- | From 9690b8ea9c08a96329a49ded8fccc02d128a4cd1 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:46:18 +0200 Subject: [PATCH 10/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 0c8b7ed3..ae200df6 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -153,7 +153,7 @@ As part of the calculation of the rateProduct, the `rateProvider` of the pool to There are several scenarios in which no yield fees are paid during a pool join or exit operation. Here are a couple of examples: -- Rate Providers only update with a given frequency: If multiple joins or exits occur without any factors contributing to a newATHRateProduct, no yield fees are minted. This means that if the rate Providers have not updated their rates within the given timeframe, the rateProduct remains unchanged and no yield fees are charged. +- `rateProduct' remain unchanged: If multiple joins or exits occur without any factors contributing to a `newATHRateProduct`, no yield fees are minted. - Rate fluctuations of different tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pool join or exit, as the rateProduct did not increase sufficiently. From 2ef6a0f2df3305f43ed2de11bc8f98aac2fd1b9b Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:50:18 +0200 Subject: [PATCH 11/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index ae200df6..bed794f6 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -143,7 +143,7 @@ Rate product = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 As part of the calculation of the rateProduct, the `rateProvider` of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: ``` -/** + /** * @notice Returns the contribution to the total rate product from a token with the given weight and rate Provider. */ function _getRateFactor(uint256 normalizedWeight, IRateProvider provider) internal view returns (uint256) { From 965a8818e83c4f6a9c8d022be18a7a802d8c558a Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:00:19 +0200 Subject: [PATCH 12/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index bed794f6..b17976b4 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -59,7 +59,7 @@ Stable Pools require the reported token balances from , `vault.getPoolTokens(poo 2. Multiply the scaled balance by the rate. -ComposableStablePools & MetaStablePools have different implementations for the scaling operations but the outcome is the same. +Composable Stable Pools and Meta Stable Pools have different implementations for the scaling operations but the outcome is the same. ### Composable Stable Pool Implementation From 49200804da97cd721060741b66e2d0fd6e8edc49 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:00:54 +0200 Subject: [PATCH 13/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index b17976b4..2d0c4758 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -126,7 +126,7 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb ## Rate Providers being used to collect yieldFees for WeightedPools -Rate providers play a crucial role in determining whether yield fees are charged during pool join or exit. The primary factor used for this determination is the comparison of the `_athRateProduct` private variable with a dynamically calculated `rateProduct` on every pool join or exit. Here's an example illustrating what the `rateProduct` represents: +Rate providers play a crucial role in determining whether yield fees are charged during pool joins and exits. The pool's `_athRateProduct` and it dynamically calculated `rateProduct` are key factors that determine this. `rateProduct` is calculated as the weighted product of all current rates: ## Yield Fees for WeightedPools From a2d1d81c888853823bda99cb4d218fca65c83eb0 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:01:16 +0200 Subject: [PATCH 14/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 2d0c4758..078a999d 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -136,7 +136,6 @@ Rate providers play a crucial role in determining whether yield fees are charged | B (non yield bearing) | 0.5 | 1.00 | | C (yield bearing) | 0.2 | 1.05 | -The rate product is obtained by multiplying the weighted rates together: Rate product = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 From 17eff16c06874d6cd41905c7414f4f0278fdc95a Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:01:25 +0200 Subject: [PATCH 15/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 078a999d..2020ccd2 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -137,7 +137,7 @@ Rate providers play a crucial role in determining whether yield fees are charged | C (yield bearing) | 0.2 | 1.05 | -Rate product = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 +`rateProduct` = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 As part of the calculation of the rateProduct, the `rateProvider` of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: From d01c0f6cbcecdbce521c898a6f28b9be3c0fcda7 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:01:33 +0200 Subject: [PATCH 16/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 2020ccd2..4184d57a 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -150,7 +150,7 @@ As part of the calculation of the rateProduct, the `rateProvider` of the pool to } ``` -There are several scenarios in which no yield fees are paid during a pool join or exit operation. Here are a couple of examples: +There are several scenarios in which no yield fees are paid during a pools join or exit operation. Below are a couple of examples: - `rateProduct' remain unchanged: If multiple joins or exits occur without any factors contributing to a `newATHRateProduct`, no yield fees are minted. - Rate fluctuations of different tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pool join or exit, as the rateProduct did not increase sufficiently. From 91325fa1476dcfa70f68a9a23e702d5c0d3a2856 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:01:45 +0200 Subject: [PATCH 17/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 4184d57a..06cd9817 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -153,7 +153,7 @@ As part of the calculation of the rateProduct, the `rateProvider` of the pool to There are several scenarios in which no yield fees are paid during a pools join or exit operation. Below are a couple of examples: - `rateProduct' remain unchanged: If multiple joins or exits occur without any factors contributing to a `newATHRateProduct`, no yield fees are minted. -- Rate fluctuations of different tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pool join or exit, as the rateProduct did not increase sufficiently. +- Insignificant `rateProduct` fluctuations between tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pools join or exit. As part of this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0x9e1d45013f4b65f444bb9b2ef823c0d4fd0a53e2b2bad85ba85a8e26c0bed45d?trace=0.2.7.3.7.1.2.2) yield fees are minted. This can be seen by the positive return values of the `_getYieldProtocolFeesPoolPercentage` function From 6dcd723b188e33d23e8b62eba78bf5810e9e03ab Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Mon, 5 Jun 2023 08:13:34 +0200 Subject: [PATCH 18/36] docs: remove unnecessary line --- docs/reference/contracts/rate-providers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 06cd9817..fd6a152c 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -122,7 +122,6 @@ The token balances used in the Trade Equations are then [upscaled](https://dashb [`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). -### Rate Provider being used in a MetaStablePool swap ## Rate Providers being used to collect yieldFees for WeightedPools From 6edec5a4ad99e8dc3a1757dc99c30bfbcbf28cdc Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Mon, 5 Jun 2023 08:18:05 +0200 Subject: [PATCH 19/36] docs: spelling --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index fd6a152c..635c7311 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -151,7 +151,7 @@ As part of the calculation of the rateProduct, the `rateProvider` of the pool to There are several scenarios in which no yield fees are paid during a pools join or exit operation. Below are a couple of examples: -- `rateProduct' remain unchanged: If multiple joins or exits occur without any factors contributing to a `newATHRateProduct`, no yield fees are minted. +- `rateProduct` remain unchanged: If multiple joins or exits occur without any factors contributing to a `newATHRateProduct`, no yield fees are minted. - Insignificant `rateProduct` fluctuations between tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pools join or exit. From 25dc96767e837ffe41e947c0b3a312b8ffdef910 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 15:07:18 +0200 Subject: [PATCH 20/36] docs: display as math expression --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 635c7311..c4910564 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -136,7 +136,7 @@ Rate providers play a crucial role in determining whether yield fees are charged | C (yield bearing) | 0.2 | 1.05 | -`rateProduct` = (0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013 +`rateProduct` = $(0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013$ As part of the calculation of the rateProduct, the `rateProvider` of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: From 310bbd3e9f972abde55ed18c2e062b158c723b4f Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 15:36:44 +0200 Subject: [PATCH 21/36] docs: formatting change --- docs/reference/contracts/rate-providers.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index c4910564..69a48995 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -81,8 +81,7 @@ function _scalingFactors() internal view virtual override returns (uint256[] mem ### Composable Stable Pool Swap Example -This trade was executed in Block 17233083. Looking at the token balances the pool has before the swap it can be seen that: - +Looking at a swap of [WETH to sfrxETH](https://etherscan.io/tx/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3) the pool balances are upscaled. | Token | balances | rate | scaled balance | | ----------- | ----------- | ----------- | ----------- | | BPT - 0x5aee1e99fe86960377de9f88689616916d5dcabe | 2596148429265848431954582359320590 | 100000000000000000 | 2596148429265848431954582359320590 | @@ -90,7 +89,14 @@ This trade was executed in Block 17233083. Looking at the token balances the poo | sfrxETH - 0xac3e018457b222d93114458476f3e3416abbe38f | 7388958961745977404526 | 1038371994655823641 | 7672488055538194248508 | | rETH - 0xae78736cd615f374d3085123a210448e74fc6393 | 5950507951882438548950 | 1069881935087994199 | 6366340962316480428138 | -\*Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. + + +::: note scaling +Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. + +::: + + After the token balances have been upscaled, they are fed into `_calcOutGivenIn` [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools. @@ -108,7 +114,7 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui ``` ### Meta Stable Pool Swap Example -Looking at this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. +In this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies, is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns 20040415915824227571764 for rETH and 21953505292747563228232 for WETH. From add96c67dfb88e88f0fef4c364c0b0fd217390af Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 15:42:49 +0200 Subject: [PATCH 22/36] docs: clearer wording of differences --- docs/reference/contracts/rate-providers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 69a48995..1e035129 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -52,12 +52,12 @@ Different types of pools utilize rate providers in different contexts. | Managed Pool | ✅ | ❌ | | Liquity Bootstrapping Pool | ❌ | ❌ | - -Stable Pools require the reported token balances from , `vault.getPoolTokens(poolId)`, be manipulated in two distinct manners before prices can be calculated using StableMath: +::: note +Stable Pools apply Stable Math as their pricing equations whereas all other pools (except Linear Pools) use Weighted Math for pricing equations. The key difference between why rate Providers impact pricing equations for Stable Pools with rate Providers is that the pool balances (retrieved via `vault.getPoolTokens(poolId)`) are manipulated in two distinct manners before prices are calculated: 1. Scale token balances to an 18-decimal fixed point number. 2. Multiply the scaled balance by the rate. - +::: Composable Stable Pools and Meta Stable Pools have different implementations for the scaling operations but the outcome is the same. From e60065f541c49ee354d540c1e236e6a6fff84655 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 16:02:26 +0200 Subject: [PATCH 23/36] docs: remove unnecessary text --- docs/reference/contracts/rate-providers.md | 43 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 1e035129..2a0a05b3 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -96,10 +96,22 @@ Bear in mind that the tokens used for demonstration in these examples all have 1 ::: +The upscaled token balances are are fed into `_calcOutGivenIn`. +``` +ComposableStablePool._calcOutGivenIn + ( + amplificationParameter = 200000, + balances = ["7860983836140600107855","7672488055538194248508","6366340962316480428138"], + tokenIndexIn = 1, + tokenIndexOut = 0, + tokenAmountIn = 163517835854389679, + invariant = 21899336949210774987256 + ) + +=> (163536626614409153) +``` - -After the token balances have been upscaled, they are fed into `_calcOutGivenIn` [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). -That is how `rateProvider` feeds a rate into the pricing equation for ComposableStablePools. +More details on this specific transaction can be found [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). ### Meta Stable Pool Implementation Scaling Example: @@ -114,18 +126,33 @@ function _scalingFactor(IERC20 token) internal view virtual override returns (ui ``` ### Meta Stable Pool Swap Example -In this [transaction](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) 50 ETH is being exchanged for 46.68 rETH. This pool is a MetaStablePool. According to the next table the rate, the `rateProvider` supplies, is being taken into account during the swap. Looking at the Trade equations, a well suited parameter to weave in the `rate` is the balances which are used to compute `OutGivenIn` or `InGivenOut`. -Querying the balances of this pool via `vault.getPoolTokens(poolId)` returns -20040415915824227571764 for rETH and 21953505292747563228232 for WETH. +Looking at a swap of 50 ETH to 46.68 rETH the pool balances are upscaled. + + | Token | balances | rate | scaled balance | | ----------- | ----------- | ----------- | ----------- | | rEth 0xae78736cd615f374d3085123a210448e74fc6393 | 20040415915824227571764 | 1070121751154609309 | 21445684973708525874136 | | WETH 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 21953505292747563228232 | 1000000000000000000 | 21953505292747563228232 | -The token balances used in the Trade Equations are then [upscaled](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.11) the scaled balance. Only after upscaling are the apparent balances supplied to -[`OnSwapGivenIn()`](https://dashboard.tenderly.co/tx/mainnet/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21?trace=0.5.2.1.5.13.2). +The upscaled token balances are fed into `_calcOutGivenIn` + +``` +MetaStablePool._calcOutGivenIn + ( + amplificationParameter = 50000, + balances = ["21445684973708525874136","21953505292747563228232"], + tokenIndexIn = 1, + tokenIndexOut = 0, + tokenAmountIn = 49980000000000000000 + ) + +=> (49954807369169689518) + +``` + +more details on this specific transaction can be found [here].(https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) From fdf3be7b3a76d4c562e75445d7a88eac7e2b889d Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 16:03:27 +0200 Subject: [PATCH 24/36] docs: reposition note on scaling --- docs/reference/contracts/rate-providers.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 2a0a05b3..e60e14c6 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -91,10 +91,6 @@ Looking at a swap of [WETH to sfrxETH](https://etherscan.io/tx/0x72d756d0fcd6633 -::: note scaling -Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. - -::: The upscaled token balances are are fed into `_calcOutGivenIn`. ``` @@ -152,9 +148,12 @@ MetaStablePool._calcOutGivenIn ``` -more details on this specific transaction can be found [here].(https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) +more details on this specific transaction can be found [here](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). +::: note scaling +Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. +::: ## Rate Providers being used to collect yieldFees for WeightedPools From b05ee55747a94bb534bd840690e26e3b4e8d0a72 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Wed, 7 Jun 2023 16:27:13 +0200 Subject: [PATCH 25/36] docs: update to yield fee section --- docs/reference/contracts/rate-providers.md | 38 ++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index e60e14c6..89f60459 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -103,11 +103,10 @@ ComposableStablePool._calcOutGivenIn tokenAmountIn = 163517835854389679, invariant = 21899336949210774987256 ) - => (163536626614409153) ``` -More details on this specific transaction can be found [here](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5). +More details on this specific transaction can be found [here.](https://dashboard.tenderly.co/tx/mainnet/0x72d756d0fcd663343ca1b2adcfc7e114e8598bc0be28386752f16222384a29b3?trace=0.4.2.2.19.11.0.3.0.5) ### Meta Stable Pool Implementation Scaling Example: @@ -143,24 +142,23 @@ MetaStablePool._calcOutGivenIn tokenIndexOut = 0, tokenAmountIn = 49980000000000000000 ) - => (49954807369169689518) ``` -more details on this specific transaction can be found [here](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21). +more details on this specific transaction can be found [here.](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) ::: note scaling Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. ::: -## Rate Providers being used to collect yieldFees for WeightedPools +## Yield Fee -Rate providers play a crucial role in determining whether yield fees are charged during pool joins and exits. The pool's `_athRateProduct` and it dynamically calculated `rateProduct` are key factors that determine this. +Rate providers play a crucial role in determining whether yield fees are charged during pool joins and exits. The pool's `_athRateProduct` and its dynamically calculated `rateProduct` are key factors that determine this. `rateProduct` is calculated as the weighted product of all current rates: -## Yield Fees for WeightedPools +### Yield Fees for WeightedPools | Token | Weight | rate | | ----------- | ----------- | ----------- | | A (yield bearing) | 0.3 | 1.01 | @@ -170,7 +168,7 @@ Rate providers play a crucial role in determining whether yield fees are charged `rateProduct` = $(0.3 * 1.01) * (0.5 * 1) * (0.2 * 1.05) = 1.013$ -As part of the calculation of the rateProduct, the `rateProvider` of the pool tokens are queried for their rates [here](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304). This occurs in the following code snippet: +As part of the calculation of the `rateProduct`, the `rateProvider` of the pool tokens are queried for their rates. ``` /** @@ -180,6 +178,7 @@ As part of the calculation of the rateProduct, the `rateProvider` of the pool to return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); } ``` +more details on the implementation can be found [here.](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304) There are several scenarios in which no yield fees are paid during a pools join or exit operation. Below are a couple of examples: @@ -187,9 +186,28 @@ There are several scenarios in which no yield fees are paid during a pools join - Insignificant `rateProduct` fluctuations between tokens: In some cases, the rate of one token may increase while the rate of another token decreases. If, on a normalized basis, the rate increase of Token A is less than the rate decrease of Token B, the calculated `rateProduct` would not reach the ceiling of `ATHRateProduct`. As a result, no yield fees would be paid during the pools join or exit. -As part of this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0x9e1d45013f4b65f444bb9b2ef823c0d4fd0a53e2b2bad85ba85a8e26c0bed45d?trace=0.2.7.3.7.1.2.2) yield fees are minted. This can be seen by the positive return values of the `_getYieldProtocolFeesPoolPercentage` function +Yield fees being minted indicated by positive return values of +``` +WeightedPool._getYieldProtocolFeesPoolPercentage + ( + normalizedWeights = ["800000000000000000","200000000000000000"] + ) +=> +(1074881576209740, 978863663373687295) +``` +More details on this specific transaction be found [here.](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0x9e1d45013f4b65f444bb9b2ef823c0d4fd0a53e2b2bad85ba85a8e26c0bed45d?trace=0.2.7.3.7.1.2.2) +No yield fees being minted indicated by zoer return values of +``` +WeightedPool.getYieldProtocolFeesPoolPercentage + ( + normalizedWeights = ["800000000000000000","200000000000000000"] + ) +=> +(0, 0) +``` +More Whereas in this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0xe2d8d7f705d23c18e7d25e68bf01ac2544cc73806c8e4572135d9bc16790b4f5) the `ATHRateProduct` did not increase -and (0,0) is [returned](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L191) since `rateProduct <= athRateProduct`. This indicates that no yield fees were paid in this transaction due to the insufficient increase in the rateProduct. +and `(0,0)` is [returned](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L191) since `rateProduct <= athRateProduct`. This indicates that no yield fees were paid in this transaction due to the insufficient increase in the rateProduct. From 0088176c5318a9bb0118d6a0c29d98c9e5bd3357 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:39:10 +0200 Subject: [PATCH 26/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 89f60459..c4dd152c 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -146,7 +146,7 @@ MetaStablePool._calcOutGivenIn ``` -more details on this specific transaction can be found [here.](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) +More details on this specific transaction can be found [here.](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) ::: note scaling Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. From 221032d3f83e6d52263f2fb95e3da71c5481e5d6 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:40:41 +0200 Subject: [PATCH 27/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index c4dd152c..7088643d 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -148,7 +148,7 @@ MetaStablePool._calcOutGivenIn More details on this specific transaction can be found [here.](https://etherscan.io/tx/0x67f477517acf6e0c91ec7997e665ca25d2806da060af30272876742584f0aa21) -::: note scaling +::: note Scaling Bear in mind that the tokens used for demonstration in these examples all have 18 decimals and Balancer natively uses 18 decimals for internal accounting. If tokens have different decimals, the scaled balances scale with the tokens decimals as well. ::: From 7469f76b3a032b75ac1098942c4ebf60d830cc9e Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:40:53 +0200 Subject: [PATCH 28/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 7088643d..9b9bb8e3 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -178,7 +178,7 @@ As part of the calculation of the `rateProduct`, the `rateProvider` of the pool return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); } ``` -more details on the implementation can be found [here.](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304) +More details on the implementation can be found [here.](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304) There are several scenarios in which no yield fees are paid during a pools join or exit operation. Below are a couple of examples: From d70b490734f648af28498f8b56a601d1283cc7b1 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:41:05 +0200 Subject: [PATCH 29/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 9b9bb8e3..cca32122 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -206,7 +206,6 @@ WeightedPool.getYieldProtocolFeesPoolPercentage => (0, 0) ``` -More Whereas in this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0xe2d8d7f705d23c18e7d25e68bf01ac2544cc73806c8e4572135d9bc16790b4f5) the `ATHRateProduct` did not increase and `(0,0)` is [returned](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L191) since `rateProduct <= athRateProduct`. This indicates that no yield fees were paid in this transaction due to the insufficient increase in the rateProduct. From a22a987414b6f89da9c3b97ad828adfe6e5f3961 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:41:17 +0200 Subject: [PATCH 30/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index cca32122..c37b6a43 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -53,7 +53,7 @@ Different types of pools utilize rate providers in different contexts. | Liquity Bootstrapping Pool | ❌ | ❌ | ::: note -Stable Pools apply Stable Math as their pricing equations whereas all other pools (except Linear Pools) use Weighted Math for pricing equations. The key difference between why rate Providers impact pricing equations for Stable Pools with rate Providers is that the pool balances (retrieved via `vault.getPoolTokens(poolId)`) are manipulated in two distinct manners before prices are calculated: +Stable Pools apply Stable Math as their pricing equations, whereas all other pools (except Linear Pools) use Weighted Math for pricing equations. The key difference between why rate Providers impact pricing equations for Stable Pools with rate Providers is that the pool balances (retrieved via `vault.getPoolTokens(poolId)`) are manipulated in two distinct manners before prices are calculated: 1. Scale token balances to an 18-decimal fixed point number. 2. Multiply the scaled balance by the rate. From 7add4c8ecccfd3f6b4d40641774c94ae47092e50 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:41:31 +0200 Subject: [PATCH 31/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: baileyspraggins <62624378+baileyspraggins@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index c37b6a43..d462aca9 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -206,7 +206,7 @@ WeightedPool.getYieldProtocolFeesPoolPercentage => (0, 0) ``` -Whereas in this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0xe2d8d7f705d23c18e7d25e68bf01ac2544cc73806c8e4572135d9bc16790b4f5) the `ATHRateProduct` did not increase +Whereas in this [transaction](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0xe2d8d7f705d23c18e7d25e68bf01ac2544cc73806c8e4572135d9bc16790b4f5), the `ATHRateProduct` did not increase and `(0,0)` is [returned](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L191) since `rateProduct <= athRateProduct`. This indicates that no yield fees were paid in this transaction due to the insufficient increase in the rateProduct. From 9cb4b0dc6f9d1e16bbe51f8a260777b02b2d3ec1 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Fri, 9 Jun 2023 17:51:24 +0200 Subject: [PATCH 32/36] docs: format code snippets --- docs/reference/contracts/rate-providers.md | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index d462aca9..70db0ee6 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -67,16 +67,16 @@ Scaling Example: ``` function _scalingFactors() internal view virtual override returns (uint256[] memory) { - // There is no need to check the arrays length since both are based on `_getTotalTokens` - uint256 totalTokens = _getTotalTokens(); - uint256[] memory scalingFactors = new uint256[](totalTokens); + // There is no need to check the arrays length since both are based on `_getTotalTokens` + uint256 totalTokens = _getTotalTokens(); + uint256[] memory scalingFactors = new uint256[](totalTokens); - for (uint256 i = 0; i < totalTokens; ++i) { - scalingFactors[i] = _getScalingFactor(i).mulDown(_getTokenRate(i)); - } - - return scalingFactors; + for (uint256 i = 0; i < totalTokens; ++i) { + scalingFactors[i] = _getScalingFactor(i).mulDown(_getTokenRate(i)); } + + return scalingFactors; +} ``` ### Composable Stable Pool Swap Example @@ -113,11 +113,11 @@ Scaling Example: ``` function _scalingFactor(IERC20 token) internal view virtual override returns (uint256) { - uint256 baseScalingFactor = super._scalingFactor(token); - uint256 priceRate = _priceRate(token); - // Given there is no generic direction for this rounding, it simply follows the same strategy as the BasePool. - return baseScalingFactor.mulDown(priceRate); - } + uint256 baseScalingFactor = super._scalingFactor(token); + uint256 priceRate = _priceRate(token); + // Given there is no generic direction for this rounding, it simply follows the same strategy as the BasePool. + return baseScalingFactor.mulDown(priceRate); +} ``` ### Meta Stable Pool Swap Example @@ -171,12 +171,12 @@ Rate providers play a crucial role in determining whether yield fees are charged As part of the calculation of the `rateProduct`, the `rateProvider` of the pool tokens are queried for their rates. ``` - /** - * @notice Returns the contribution to the total rate product from a token with the given weight and rate Provider. - */ - function _getRateFactor(uint256 normalizedWeight, IRateProvider provider) internal view returns (uint256) { - return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); - } +/** + * @notice Returns the contribution to the total rate product from a token with the given weight and rate Provider. + */ +function _getRateFactor(uint256 normalizedWeight, IRateProvider provider) internal view returns (uint256) { + return provider == IRateProvider(0) ? FixedPoint.ONE : provider.getRate().powDown(normalizedWeight); +} ``` More details on the implementation can be found [here.](https://github.com/balancer/balancer-v2-monorepo/blob/cbce7d63479dafb4f4ea9ad8cb2dbdbb26edae50/pkg/pool-weighted/contracts/WeightedPoolProtocolFees.sol#L304) From c2c1f1920181e2b349373498232e78b89af6ea57 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:16:26 +0200 Subject: [PATCH 33/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: gerg <69520747+gerrrg@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 70db0ee6..aa615c6b 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -97,7 +97,7 @@ The upscaled token balances are are fed into `_calcOutGivenIn`. ComposableStablePool._calcOutGivenIn ( amplificationParameter = 200000, - balances = ["7860983836140600107855","7672488055538194248508","6366340962316480428138"], + balances = [7860983836140600107855,7672488055538194248508,6366340962316480428138], tokenIndexIn = 1, tokenIndexOut = 0, tokenAmountIn = 163517835854389679, From 5d4105a7ec664d0020ba389e23541162ad643f37 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:16:42 +0200 Subject: [PATCH 34/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: gerg <69520747+gerrrg@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index aa615c6b..0d9bba56 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -137,7 +137,7 @@ The upscaled token balances are fed into `_calcOutGivenIn` MetaStablePool._calcOutGivenIn ( amplificationParameter = 50000, - balances = ["21445684973708525874136","21953505292747563228232"], + balances = [21445684973708525874136,21953505292747563228232], tokenIndexIn = 1, tokenIndexOut = 0, tokenAmountIn = 49980000000000000000 From 0bda0ffa75a4f9f22e01e5134d5e8e7bfebbf7e3 Mon Sep 17 00:00:00 2001 From: mkflow27 <35266877+mkflow27@users.noreply.github.com> Date: Mon, 26 Jun 2023 08:17:00 +0200 Subject: [PATCH 35/36] Update docs/reference/contracts/rate-providers.md Co-authored-by: gerg <69520747+gerrrg@users.noreply.github.com> --- docs/reference/contracts/rate-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index 0d9bba56..b0f32cef 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -197,7 +197,7 @@ WeightedPool._getYieldProtocolFeesPoolPercentage ``` More details on this specific transaction be found [here.](https://dashboard.tenderly.co/mkflow/project/tx/mainnet/0x9e1d45013f4b65f444bb9b2ef823c0d4fd0a53e2b2bad85ba85a8e26c0bed45d?trace=0.2.7.3.7.1.2.2) -No yield fees being minted indicated by zoer return values of +No yield fees being minted indicated by zero return values of ``` WeightedPool.getYieldProtocolFeesPoolPercentage ( From c528c5d2107a70d15125e05b42bc062d8d5defa2 Mon Sep 17 00:00:00 2001 From: Sebastian Marquardt Date: Mon, 26 Jun 2023 15:42:46 +0200 Subject: [PATCH 36/36] docs: make note more concise --- docs/reference/contracts/rate-providers.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/reference/contracts/rate-providers.md b/docs/reference/contracts/rate-providers.md index b0f32cef..e69a08bf 100644 --- a/docs/reference/contracts/rate-providers.md +++ b/docs/reference/contracts/rate-providers.md @@ -52,11 +52,10 @@ Different types of pools utilize rate providers in different contexts. | Managed Pool | ✅ | ❌ | | Liquity Bootstrapping Pool | ❌ | ❌ | -::: note -Stable Pools apply Stable Math as their pricing equations, whereas all other pools (except Linear Pools) use Weighted Math for pricing equations. The key difference between why rate Providers impact pricing equations for Stable Pools with rate Providers is that the pool balances (retrieved via `vault.getPoolTokens(poolId)`) are manipulated in two distinct manners before prices are calculated: - -1. Scale token balances to an 18-decimal fixed point number. -2. Multiply the scaled balance by the rate. +::: note RateProvider Usage +While both Stable and Weighted Pools can have RateProviders, only Stable Pools use them to determine token prices. +- Stable: raw token balances are scaled by the RateProvider values before being fed into StableMath +- Weighted: token rates are used only to determine growth to calculate yield protocol fees ::: Composable Stable Pools and Meta Stable Pools have different implementations for the scaling operations but the outcome is the same.