In the Curve dev channel, we often see a lot of questions about how does Curve calculate the yield? We wrote up the following explainer that we’re also proposing for addition to the comprehensive Curve resources site.
We thought to release the working draft as an article on this humble fanzine, for the purpose of soliciting feedback and expanding any sections that are unclear. Help us by dropping any questions about parts that may remain unclear, or any comments on the article at large.
Note, this article may therefore be modified and updated over time based on user feedback. Therefore you should not consider any of the following as canon, it remains a work in progress! Information here may change and therefore not be accurate.
This article is intended to be detailed but non-technical, outside of a few light mathematical formulas. While we highlight specific smart contract function names that the Curve UI may reference for convenience, no knowledge of coding is otherwise necessary to understand this article.
Developers seeking a more in-depth explanation of these concepts should consult the technical documentation at
https://curve.readthedocs.io/
Types of Yield
In the above screenshot you can see a Curve pool has the potential to offer many different types of yield. The documentation describes the different types of yield here.
It’s important to remember that these numbers are a projections of historical pool performance. The user would get this rate if the pool performance stays exactly the same for one year.
These yield types are:
Base vAPY: Shown on the first line, this number represents the fees that accrue to holders of the LP token based on trading volume. More Info
$CRV Rewards tAPR: Shown on the second line, the rewards tAPR represents the rate of $CRV token emissions one would have earned if the pool has a rewards gauge and the user stakes into this rewards gauge. The number is listed as a range of possible rewards, based on the user’s locked veCRV the size of this boost can vary. More Info
Incentives Rewards tAPR: Some pools also choose to stream rewards in the form of a different token — this is represented on the third line if applicable.
vAPY
stands for “variable annual percentage yield”, this value calculates an annualized estimate of the trading fee yield based on the past day’s trading activity, inclusive of any effect of compounding.
The rewards tAPR
stands for “token annual percentage rate” — token rewards must be claimed manually and therefore do not automatically compound, so “rate” is the more proper term.
Base vAPY
When Curve pools are launched, they receive a value for both the fee
(the overall fee applied to trades) and the admin_fee
(the percentage of this fee that goes to the Curve DAO as opposed to pool LPs). These parameters are directly viewable on the smart contract through the corresponding function names.
These fees are displayed on the Curve UI pool page:
These parameters may also be updated in the future by the Curve DAO by calling the commit_new_fee
method. If the fees are in the process of being changed, these are readable in the smart contract via the future_fee
and future_admin_fee
methods.
The fees are specifically earned or charged every time a user interacts with a pool contract through a transaction which may affect the pool balances. For example, directly calling the exchange
function would rebalance the pool, so a fee clearly applies. If you add or remove liquidity in an imbalanced fashion, this would also adjust the ratios of tokens within the pool and thus be subject to fees. No fees are charged if a user adds coin in a balanced proportion or on removal.
When you call methods to preview how many tokens you might receive for interacting with a pool (ie get_dy
or calc_token_amount
) the values they return are usually but not always inclusive of any fees — the UI calculations are intended to make any corrections where appropriate, but be sure to ask the support team if you have questions.
Theoretically, one could calculate the base vAPY for any period by calculating the fees for every transaction and summing over the entire range. However, the Curve UI utilizes a simpler methodology to calculate the base vAPY, where t
is the time in days:
In other words, the vAPY measures the change in the pool’s "virtual price" between today and yesterday, then annualizes this rate. The "virtual price" is a measure of the pool growth over time, and is viewable directly on the UI.
The UI receives this value directly by calling the get_virtual_price
method on the pool contract.
Every time a transaction occurs that charges a fee, the virtual price is incremented accordingly. Thus, when a pool launches with a virtual price of exactly 1, if the pool’s virtual price is 1.01 at some future time, an LP holding a token has seen the token’s value increase by 1%.
A virtual price of 1.01 means they will get 1% more value back on receiving liquidity. Similarly, new users adding liquidity will receive 1% fewer LP tokens on deposit.
For pegged stablecoin pools, virtual price can therefore easily be utilized to calculate vAPY of the pool since inception with no further calculations necessary. For v2 pools, one must also consider the fluctuating prices of underlying assets.
For developers, here are more details about trade fees from the technical documentation:
Rewards tAPR
The Curve DAO also authorizes some pools to receive bonus rewards from $CRV token emission, as described in the Understanding Gauges section of the documentation. If the pool has an eligible gauge, then the UI displays the range of possible tAPR values users are earning at present, subject to change in the future.
The formula used here to calculate rewards tAPR:
These parameters are obtained from various data sources, mostly on-chain:
crv_price:
The current price of the $CRV token in USD. This could be extrapolated from on-chain data, but the UI relies on the CoinGecko API to fetchthis value.inflation_rate:
The inflation rate of the $CRV token, accessed from therate
function of the $CRV token.relative_weight:
Based on weekly voting, each Curve pool rewards gauge has a weighting relative to all other Curve gauges. This value can be calculated by calling the same function on the Curve gauge controller contract.
working_supply:
Accessed by calling the same function on the specific Curve gauge contract for the pool.asset_price:
The price of the asset — that is, if the pool contains only bitcoin, then the current price of $BTC. For v2 pools, this must be calculated by averaging the specific assets in the pool.virtual_price:
The measure of the pool growth over time, as described above.
The magic number 12614400
is number of seconds in a year (60 * 60 * 24 * 365 = 31536000)
times 0.4. In this case the 0.4 is due to the effect of boosts (minimum boost of 1 / maximum boost of 2.5 = 0.4).
As shown in the UI, all tAPR values are displayed as a range, with the base rate on the left of the arrow representing the default rate one would receive if the user has no boost, and the value on the right of the arrow representing the maximum value a user could receive if the user has the maximum boost, which is 2.5 times higher than the minimum boost. Further details about calculating boosts are provided here.
The full calculation is also detailed in this video
For developers, here are relevant links to the technical documentation:
Incentives tAPR
All pools may permissionlessly stream other token rewards without approval from the Curve DAO. The UI displays these bonus rewards only when applicable. In the example of stETH below, note how the pool is streaming $LDO tokens in addition to $CRV rewards.
Further information on these extra incentives is available in the developer documentation.
Disclaimers!