Platform: Code4rena
Start Date: 28/09/2023
Pot Size: $36,500 USDC
Total HM: 5
Participants: 115
Period: 6 days
Judge: 0xDjango
Total Solo HM: 1
Id: 290
League: ETH
Rank: 94/115
Findings: 1
Award: $4.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Bauchibred
Also found by: 0x3b, 0xDetermination, 0xMosh, 0xScourgedev, 0xTheC0der, 0xTiwa, 0xWaitress, 0xdice91, 0xfusion, 0xpiken, 0xprinc, 0xweb3boy, ArmedGoose, Aymen0909, Breeje, Brenzee, Daniel526, DavidGiladi, DeFiHackLabs, Flora, Fulum, HChang26, Hama, IceBear, J4X, Krace, KrisApostolov, Maroutis, Mirror, MohammedRizwan, Norah, PwnStars, SPYBOY, TangYuanShen, Testerbot, ThreeSigma, Tricko, al88nsk, alexweb3, ast3ros, berlin-101, bin2chen, blutorque, btk, d3e4, deth, e0d1n, ether_sky, ge6a, gkrastenov, glcanvas, hals, imare, inzinko, jkoppel, jnforja, joaovwfreire, josephdara, kutugu, lotux, lsaudit, mahdirostami, merlin, n1punp, nadin, neumo, nisedo, nobody2018, oakcobalt, orion, peanuts, pep7siup, pina, ptsanev, rokinot, rvierdiiev, said, santipu_, sashik_eth, seerether, squeaky_cactus, terrancrypt, tonisives, twicek, vagrant, xAriextz, y4y
4.3669 USDC - $4.37
https://github.com/code-423n4/2023-09-venus/blob/main/contracts/Tokens/Prime/Prime.sol#L970-L979 https://github.com/code-423n4/2023-09-venus/blob/main/contracts/Tokens/Prime/Prime.sol#L40
At the _incomeDistributionYearly function, the Prime contract utilizes a BLOCKS_PER_YEAR constant defined on deployment that will account for the amount of blocks per year at the deployed chain.
function _incomeDistributionYearly(address vToken) internal view returns (uint256 amount) { uint256 totalIncomePerBlockFromMarket = _incomePerBlock(vToken); uint256 incomePerBlockForDistributionFromMarket = (totalIncomePerBlockFromMarket * _distributionPercentage()) / IProtocolShareReserve(protocolShareReserve).MAX_PERCENT(); amount = BLOCKS_PER_YEAR * incomePerBlockForDistributionFromMarket; uint256 totalIncomePerBlockFromPLP = IPrimeLiquidityProvider(primeLiquidityProvider) .getEffectiveDistributionSpeed(_getUnderlying(vToken)); amount += BLOCKS_PER_YEAR * totalIncomePerBlockFromPLP; }
This value is then utilized for calculation of the APR. The issue is there is no deterministic way to predict the amount of blocks a year will have due to the nature of block validation and leap years.
The calculated APR value will have different imprecision values depending on the chain: at chains with higher block rates, the impact should be bigger, as shown at the proof of concept. Even though the function that utilizes this variable is view-only (does not modify the chain's state), the risk lies on false claims that a front-end could display and other protocols could attempt to integrate this function creating risks if the purpose is composability. Moreover, the Prime system is made to create value for the XVS token and by displaying wrong values, taking part on such a system may become less attractive.
This source code is not in scope but illustrates that the team has once implemented a constant for the amount of blocks per year at the BNB chain at the @venusprotocol/isolated-pools package:
/// @dev The approximate number of blocks per year that is assumed by the interest rate model uint256 constant BLOCKS_PER_YEAR = 10_512_000;
This constant is based on the assumption that the BNB chain will have 3 second block times - therefore running 28800 blocks per day -> 10512000 blocks per year.
We can draw the assumption that the team will utilize constants to determine the amounts of block per year. In a production setting, however, the amount of blocks per year is not a constant, as shown by the following data:
From Ethereum Block Count and Rewards Chart:
Year | Total blocks | Avg blocks per day |
---|---|---|
2015 | 778483 | Not considered |
2016 | 2140035 | 5863.109589 |
2017 | 1920279 | 5261.038356 |
2018 | 2155929 | 5906.654795 |
2019 | 2204651 | 6040.139726 |
2020 | 2371753 | 6497.953425 |
2021 | 2351036 | 6441.194521 |
2022 | 2392024 | 6553.490411 |
2023 | 1936615 | 7172.648148 |
From BNB Smart Chain Block Count and Rewards Chart:
Year | Total blocks | Avg blocks per day | Blocks lost per day | Avg blocks lost per year | Days not accounted for |
---|---|---|---|---|---|
2020 | 3593318 | 28746.544 | 53.456 | 19564.896 | 0.679336667 |
2021 | 10376343 | 28428.33699 | 371.6630137 | 135657 | 4.7103125 |
2022 | 10423989 | 28558.87397 | 241.1260274 | 88011 | 3.0559375 |
2023 | 7785821 | 28729.96679 | 70.03321033 | 25562.12177 | 0.887573673 |
From Arbitrum Smart Chain Block Count and Rewards Chart:
Year | Total blocks | Avg blocks per day |
---|---|---|
2021 | 4221288 | Not considered |
2022 | 45849623 | 125615.4055 |
2023 | 85848543 | 316784.2915 |
As shown above, the block amounts are very hard to predict and will lead to considerable imprecision on the APR calculations for staking positions on the smaller end, even on BNB, which has a relatively stable block rate.
Consider the graph from the documentation and the following reduction:
The graph shows that positions close to the 1000 XVS range, will earn around 2.5-3% APR. On a not so bad case, consider BNB in 2021. The imprecision would lead to 4.7 days not accounted for, approximately 1,28% of the year. This means the view function would display to users 1.28% more APR than the actual value. On a worst case scenario, consider a contract deployed on Arbitrum in the beginning of 2023. The imprecision would lead to extra 191196 blocks accounted for per day. That would lead to a decrease of over 2/3 on the APR displayed to users. That would be a 2.5-3% APR turned to 0.75-1%.
Manual review
The team should make the BLOCKS_PER_YEAR variable dynamic, so that it can be updated as the chain evolves. Additionally, a disclaimer should be added to the documentation, warning users that the APR displayed is an estimate and may not reflect the actual value.
Math
#0 - c4-pre-sort
2023-10-06T01:38:23Z
0xRobocop marked the issue as duplicate of #76
#1 - c4-judge
2023-10-31T20:42:29Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-11-03T02:06:52Z
fatherGoose1 marked the issue as grade-b