Venus Prime - 0xWaitress's results

Earn, borrow & lend on the #1 Decentralized Money Market on the BNB chain.

General Information

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

Venus Protocol

Findings Distribution

Researcher Performance

Rank: 65/115

Findings: 2

Award: $20.06

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Lines of code

https://github.com/code-423n4/2023-09-venus/blob/main/contracts/Tokens/Prime/PrimeLiquidityProvider.sol#L216-L225

Vulnerability details

Impact

balanceOf of reward Tokens is meant to be strictly larger than accrued to in PrimeLiquidityProvider, however the owner has the ability to sweep reward tokens which could revert the accrue function

If the owner sweep the reward Tokens using sweepToken()

    function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external onlyOwner {
        uint256 balance = token_.balanceOf(address(this));
        if (amount_ > balance) {
            revert InsufficientBalance(amount_, balance);
        }

        emit SweepToken(address(token_), to_, amount_);

        token_.safeTransfer(to_, amount_);
    }

Then the balanceOf rewardTokens would not be larger than the accruedAmount tokenAmountAccrued

during accrueTokens, balanceDiff would have underflow.

    function accrueTokens(address token_) public {
        _ensureZeroAddress(token_);

        _ensureTokenInitialized(token_);

        uint256 blockNumber = getBlockNumber();
        uint256 deltaBlocks = blockNumber - lastAccruedBlock[token_];

        if (deltaBlocks > 0) {
            uint256 distributionSpeed = tokenDistributionSpeeds[token_];
            uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));

            uint256 balanceDiff = balance - tokenAmountAccrued[token_];
            if (distributionSpeed > 0 && balanceDiff > 0) {
                uint256 accruedSinceUpdate = deltaBlocks * distributionSpeed;
                uint256 tokenAccrued = (balanceDiff <= accruedSinceUpdate ? balanceDiff : accruedSinceUpdate);

                tokenAmountAccrued[token_] += tokenAccrued;
                emit TokensAccrued(token_, tokenAccrued);
            }

            lastAccruedBlock[token_] = blockNumber;
        }
    }

Impact: accrueTokens, which is an essential flow for releaseFund and _setTokenDistributionSpeed would be reverted if owner sweep the reward tokens.

Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

Tools Used

if a token has a non-zero distribution speed, validate and revert the sweepToken.

Assessed type

Context

#0 - c4-pre-sort

2023-10-05T01:02:54Z

0xRobocop marked the issue as primary issue

#1 - 0xRobocop

2023-10-05T01:03:05Z

Consider QA

#2 - c4-pre-sort

2023-10-05T01:03:11Z

0xRobocop marked the issue as low quality report

#3 - c4-pre-sort

2023-10-06T00:30:08Z

0xRobocop marked the issue as high quality report

#4 - c4-sponsor

2023-10-24T16:28:51Z

chechu marked the issue as disagree with severity

#5 - chechu

2023-10-24T16:29:24Z

Consider QA

sweepToken is a privilege function, that would be executed in a VIP, with the votes of the Venus community, only in edge cases. There would be several options: for example, if part of the balance has already been allocated, we could sweep less than 100% of the balance. In the worst case (in an emergency), we would prefer to break the accounting of the contracts, recover as many tokens as possible, and later recover the accounting

#6 - c4-sponsor

2023-10-24T18:38:51Z

chechu (sponsor) acknowledged

#7 - c4-judge

2023-10-31T17:09:56Z

fatherGoose1 changed the severity to QA (Quality Assurance)

#8 - fatherGoose1

2023-10-31T17:11:04Z

Agree with sponsor. Besides being a privileged action, it could be fixed by directly transferring back the token if the original sweep were made in error.

#9 - c4-judge

2023-11-03T01:41:37Z

fatherGoose1 marked the issue as grade-b

Findings Information

🌟 Selected for report: DavidGiladi

Also found by: 0x3b, 0xWaitress, 0xhacksmithh, 0xprinc, hihen, jkoppel, lsaudit, oakcobalt, pavankv, pontifex

Labels

bug
G (Gas Optimization)
grade-b
sufficient quality report
edited-by-warden
G-11

Awards

15.6862 USDC - $15.69

External Links

[G-1] pendingScoreUpdates can be updated at the end of updateScores instead of using pendingScoreUpdates-- at every loop, with the use of an intermediate variable in memory.

    function updateScores(address[] memory users) external {
        if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired();
        if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired();

        for (uint256 i = 0; i < users.length; ) {
            address user = users[i];

            if (!tokens[user].exists) revert UserHasNoPrimeToken();
            if (isScoreUpdated[nextScoreUpdateRoundId][user]) continue;

            address[] storage _allMarkets = allMarkets;
            for (uint256 j = 0; j < _allMarkets.length; ) {
                address market = _allMarkets[j];
                _executeBoost(user, market);
                _updateScore(user, market);

                unchecked {
                    j++;
                }
            }

            pendingScoreUpdates--;
            isScoreUpdated[nextScoreUpdateRoundId][user] = true;

            unchecked {
                i++;
            }

            emit UserScoreUpdated(user);
        }
    }

Recommendation

move pendingScoreUpdates-- outside of the loop and replace it with the use of a counter.

uint256 counter = 0;
...
---            pendingScoreUpdates--;
+++            counter++;
...
pendingScoreUpdates -= counter;

#0 - c4-pre-sort

2023-10-06T00:29:41Z

0xRobocop marked the issue as sufficient quality report

#1 - c4-judge

2023-11-02T02:30:11Z

fatherGoose1 marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter