Prime tokens
Last updated
Last updated
Only available on BNB chain
This technical article explains implementation details of the Venus Prime program. The overview of the Venus Prime program can be checked here.
(Main explanation of Prime rewards)
Qualifiable supply and borrow amounts limits are set by the staked XVS limit and the market multiplier. The USD values of the tokens and the USD value of XVS will be taken into account to calculate these caps. The following pseudocode shows how is calculated considering the caps:
Significance of α
A higher α value increases the weight of stake contributions when determining rewards and decreases the weight of supply/borrow contributions. The value of α is between 0-1 (both excluded).
A default weight of 0.5 has been evaluated as a good ratio and is not likely to be changed. A higher value would only be needed if Venus wanted to attract more XVS stake from the Prime token holders at the expense of supply/borrow rewards.
Here is an example to show how the score is impacted based on the value of α:
rewardIndex
and sumOfMembersScore
are global variables in supported markets used when calculating rewards. sumOfMembersScore
represents the current sum of all the Prime token holders score and rewardIndex
needs to be updated whenever a user’s staked XVS or supply/borrow changes.
Whenever a user’s supply/borrow or XVS Vault balance changes we will recalculate the rewards accrued and add them to their account:
In Comptroller (specifically in the PolicyFacet
), after executing any operation that could impact the Prime score or interest, we accrue the interest and update the score for the Prime user by calling accrueInterestAndUpdateScore
.
In the XVSVault
, after depositing or requesting a withdrawal, the function xvsUpdated
is invoked, to review the requirements of Prime holders.
This is how we will calculate the user rewards:
Then we will update the userRewardIndex
(interests[market][account]
) to their current global values.
Every market in Venus (including Isolated Lending markets) contributes to the rewards that the Prime contract will distribute, following the protocol tokenomics. The following diagram shows the current implementation for the flow of funds.
Rewards will be distributed to Prime users only in USDT, USDC, BTC and ETH tokens. Other tokens will have to be converted to the tokens used for rewarding users in Prime. This conversion should follow a configurable (via VIP) distribution table, that initially will be:
USDT
40%
USDC
30%
ETH
25%
BTC
5%
For example:
The CAKE market generates 1,000 CAKE of total income
10% of the CAKE total income should be allocated to Prime → 100 CAKE (following the protocol tokenomics)
We should convert 100 CAKE to USDC, USDT, BTC and ETH, because in Prime the rewards are defined in these tokens
The conversion should follow the previous table:
40 CAKE should be converted to USDT
30 CAKE should be converted to USDC
25 CAKE should be converted to ETH
5 CAKE should be converted to BTC
Interest reserves (part of the protocol income) from Isolated Pools and the Core Pool markets are sent to the PSR (Protocol Share Reserve) contract. Based on the configuration, a percentage of income from all markers is reserved for Prime token holders. The interest reserves will be sent to the PSR periodically (currently every 24 hours, but this can be changed by the community via VIP).
The PSR has a function releaseFunds
that needs to be invoked to release the funds to the destination contracts. We have SingleTokenConverter contracts which receive income from the PSR and convert them to Prime supported reward tokens.
Each SingleTokenConverter
sends funds to PrimeLiquidityProvider
contract which releases the funds to Prime
contract. Distribution speeds for each of the reward token is configured in the PrimeLiquidityProvider
contract and based on those speeds Prime
distributes rewards.
If a user tries to claim their rewards and the Prime
contract doesn’t have enough funds, then we trigger the release of funds from PrimeLiquidityProvider
to Prime
contract in the same transaction i.e., in the claimInterest
function.
The following diagram shows the integration of the SingleTokenConverter
contracts with the Prime contracts:
More information about income collection and distribution can be found here.
Market multipliers and alpha can be updated at anytime and then need to be propagated to all users. Changes will be gradually applied to users as they borrow/supply assets and their individual scores are recalculated. This strategy has limitations because the scores will be wrong in aggregate until all Prime users have interacted with the markets.
To mitigate this issue, Venus will supply a script that will use the permission-less function updateScores
to update the scores of all users. This script won’t pause market and Prime
contracts. The scores will need to be updated in multiple transactions because a single transaction will run out of gas trying to update all scores.
As markets won't be paused, there could be inconsistencies due to user supply/borrow transactions in between updating scores transactions. These inconsistencies will be very minor compared to letting it update gradually when users will borrow/supply.
There are two main objectives for creating this script:
If the Venus community wants to update all users scores when multipliers or alpha are changed then we have an immediate option.
After minting Prime tokens if the Venus community decides to add an existing market to the Prime token program then users scores need to be updated in order for them to being receiving rewards. The scores cannot be applied gradually in this case as the first Prime users in the market will receive disproportionally large rewards until the rest of the users in the market have their scores updated. A script to quickly update score for all users in a market will prevent this scenario.
There is a variable named totalScoreUpdatesRequired
to track how many score updates are pending. This is for tracking purposes and visible to the community.
The goal is to offer a view function that allows the Venus UI to show an estimation of the APR associated with the Prime token and the borrow/supply position of a user.
The steps to perform this calculation are:
Fetch the income per block from PrimeLiquidityProvider
Calculate the user yearly income by multiplying (1) with blocks per year
Calculate the user score and total sum of scores for the market. This we can calculate the total rewards aloocation for the user for an year.
Calculate the capped supply and borrow of the user for the market
Calculate the ratio of allocation of the rewards based on capped supply and borrow of the user
Now borrow and supply APR of user can be calculated based on ratio of capped borrow and supply of the user.
Example:
Income per block 0.00003 USDT
Income per year is 10512000 blocks/year * 0.00003 = 315.36 USDT
Assuming the user score for USDT: 3, and the sum of scores for USDT: 10, then we would have 94.608 USDT (yearly income for this user, generated by Prime)
Assuming the user has the following positions:
borrow: 30 USDT. Let's say it's capped at 15 USDT, so we'll consider 15 USDT
supply: 10 USDT. Let's say it's also capped at 15 USDT, so we'll consider 10 USDT
Allocating the rewards (94.608 USDT), considering these capped versions, we would have:
borrow: 94.608 * 15/25 = 56.76 USDT
supply: 94.608 * 10/25 = 37.84 USDT
Calculating the APR with these allocations, we would have:
borrow: 56.76/30 = 1.89 = 189%
supply: 37.84/10 = 3.78 = 378%
Only the supply and borrow amounts below the cap generate Prime rewards. The supply and borrow amounts above the cap do not generate extra rewards. In the example, if the user supplies more USDT, they won't generate more rewards (because the supply amount to be considered is capped at 15 USDT). Therefore the supply APR would decrease if they supply more USDT.
There will be bootstrap liquidity available for the Prime program. This liquidity:
should be uniformly distributed over a period of time, configurable via VIP
is defined by the tokens enabled for the Prime program
These requirements will be enforced with the PrimeLiquidityProvider
contract:
The Prime
contract has a reference to the PrimeLiquidityProvider
contract
The Prime
contract will transfer to itself the available liquidity from the PrimeLiquidityProvider
as soon as it’s needed when a user claims interests, to reduce the number of transfers
The Prime
contract takes into account the tokens available in the PrimeLiquidityProvider
contract, when the interests are accrued and the estimated APR calculated
Regarding the PrimeLiquidityProvider
:
The PrimeLiquidityProvider
contract maintains a speed per token (see tokenDistributionSpeeds
, with the number of tokens to release each block), and the needed indexes, to release the required funds per block
Anyone can send tokens to the PrimeLiquidityProvider
contract
Only accounts authorized via ACM will be able to change the tokenDistributionSpeeds
attribute
The PrimeLiquidityProvider
provides a view function to get the available funds that could be transferred for a specific token, taking into account:
the current block number
the speed associated with the token
the last time those tokens were released
The PrimeLiquidityProvider
provides a function to transfer the available funds to the Prime
contract.
claimInterest
There is a feature flag to enable/disable the function claimInterest
. When this feature is paused, no users will be able to invoke this function.
The OpenZeppelin PausableUpgradeable
contract is used. Only the claimInterest
function is under control of this pause mechanism.