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: 64/115
Findings: 2
Award: $21.61
🌟 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/PrimeLiquidityProvider.sol#L332 https://github.com/code-423n4/2023-09-venus/blob/main/contracts/Tokens/Prime/Prime.sol#L288
Function addMarket
does not check if added market is already initialized in PrimeLiquidityProvider
. If by mistake a market is added prior to being initialized in the PLP contract, then all operations involving accrueInterestAndUpdateScore
e.g. used in Comptroller will revert protocol-wide. This may effectively cause part of protocol to temporarily stop working, until the token is initialized on PLP side. Below description describes which parts are at risk in that state.
Assume a market is added to Prime.sol
without being first initialized at PrimeLiquidityProvider
. The steps will be following:
New market is added using addMarket by trusted party. At first, nothing really happens and just the market and its mappings are "initialized" at Prime.sol
side.
As comptroller has checks on each input/output to control by using prime.accrueInterestAndUpdateScore
, e.g. here and in all other functions that use accrueInterestAndUpdateScore
, that means each operation on the comptroller related to this market will cause following things to happen:
call accrueInterestAndUpdateScore in Prime, further call subroutines:
call _executeBoost, pass the entry conditions in first 3 lines,
call accrueInterest, and finally call
_primeLiquidityProvider.accrueTokens(underlying)
Here in line 252 there is call to _ensureTokenInitialized
:
function _ensureTokenInitialized(address token_) internal view { uint256 lastBlockAccrued = lastAccruedBlock[token_]; if (lastBlockAccrued == 0) { revert TokenNotInitialized(token_); } }
So at this point any operation on this market, will revert, until the token is not made initialized in the PLP contract.
Moreover, aside of newly-added-but-not-initialized market related operations being unoperable, any loop that come from Prime.sol
and iterates over all markets, calling _executeBoost
, will encounter the same issue, so until new market is added in Prime and not initialized in PLP, following functions will revert due to being depended on accrueInterest
:
Overall, since this requires trusted party mistake the likelihood is low, but the consequences are rather significant which is temporary pause of significant part of the protocol, so rating as medium.
Manual approach
Include initialization of market's token in the PLP contract when adding new market to Prime.sol
to avoid possiblity that protocol enters such troublesome state, or check if token is already in PLP
upon adding new market to Prime
.
DoS
#0 - 0xRobocop
2023-10-06T23:41:42Z
Consider QA
#1 - c4-pre-sort
2023-10-06T23:41:47Z
0xRobocop marked the issue as low quality report
#2 - c4-judge
2023-11-02T00:59:46Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#3 - fatherGoose1
2023-11-02T01:00:22Z
QA. Helpful insight, but easily fixable by initializing the token in PLP contract.
🌟 Selected for report: 3agle
Also found by: 0x3b, 0xDetermination, 0xweb3boy, ArmedGoose, Bauchibred, Breeje, J4X, hunter_w3b, jamshed, kaveyjoe, oakcobalt, radev_sw, versiyonbir
17.244 USDC - $17.24
XVS Prime is a loyalty programme which rewards of are dependent on participating in both XVS staking and borrowing/lending in venus protocol. The proof of being a member of the Prime program can be minted both by the protocol governance to certain users, or by users themselves if they meet the criteria. Proof of membership is under the hood increased balance in the Prime.sol contract itself. The contract is called a soulbound token, but it is not implementing any official standard, instead it is a custom contract, however it does not change anything because it is functional as it is. The entry point function for minting/revoking tokens is function xvsUpdated which takes user address as a parameter and depending on user’s situation issues or burns user tokens, among other options.
The most important part about Prime is ability to earn rewards for being prime holder. This is the core of the whole idea.
The rewards from this program are accrued per each market added to Prime.sol
and initialized in PrimeLiquidityProvider.sol
, they are accrued over time and are based on score calculated on Cobb-Douglas Production function described in the official documentation for the protocol.
Under the hood, for each market there are following components of the score calculation:
The system works in as I called them “micro cycles” - rewarding periods - as with each update of the score components (balances, capital, xvs, burn, mint) the rewards for past micro cycle (from last accrue timestamp to now) are accrued and accounted, then new score is calculated, and then the system starts new microcycle from current timestamp (index) to another change. This means, special attention should be paid to intervals of updating, if always new rewards are not accounted to past period for example, IF new changes of components are not introduced before accounting previous system state for past interval.
Currently, the protocol mainly updates the rewarding intervals using accrueInterestAndUpdateScore
function, which splits itself into two main logical operations: first is accruing interest using current score, and second is updating the score, reflecting current changes, after the interest is accrued. This order is very important, otherwise there would be massive issues with rewarding if e.g. latest changes would be accounted before clearing all outstanding accrued interest.
Aside of that, the system is somewhat centralized from technical point of view (there are roles that control vital aspects of the protocol) however as specified in the documentation it is managed by a governance. The “vital aspects” are:
Aside of potential centralization risk, which is greatly reduced by using governance, the code has been reviewed and suitable vulnerabilities (if any found) were submitted. The threat modeling shows following areas that could be interesting for the protocol:
Overall the code quality is good and consistent. For me it was easiest to use manual approach since there were some compilation issues with current tests.
40 hours
#0 - 0xRobocop
2023-10-07T05:42:23Z
I think the analysis provided points out the most critical part of the system which is the complex score system. I do not think it provides more context about it of what the team already knows. The threat modeling section could have been more specific around the score system which probably could have help the Venus team to discover a new perspective around it (maybe a question that they did not asked during design and development).
#1 - c4-pre-sort
2023-10-07T05:42:29Z
0xRobocop marked the issue as sufficient quality report
#2 - c4-judge
2023-11-03T20:21:58Z
fatherGoose1 marked the issue as grade-b