C4 Report

How Velodrome is Addressing Issues in the C4 Contest

The Code4rena contest results were released on August 8, 2022 and are available here (opens in a new tab).

Below details how the Velodrome team addressed these issues prior to their mainnet deploy on Optimism.

High Risk (6)

[H-01] Users can get unlimited votes (opens in a new tab)

[H-02] VotingEscrow's merge and withdraw aren't available for approved users (opens in a new tab)

[H-03] User rewards stop accruing after any _writeCheckpoint calling action (opens in a new tab)

[H-04] Bribe Rewards Struck In Contract If Deposited During First Epoch (opens in a new tab)

[H-05] Voting overwrites checkpoint.voted in last checkpoint, so users can just vote right before claiming rewards (opens in a new tab)

[H-06] Attacker can block LayerZero channel (opens in a new tab)

Medium Risk (17)

[M-01] Gauge set can be front run if bribe and gauge constructors aren't run atomically (opens in a new tab)

[M-02] VeloGovernor: proposalNumerator and team are updated by team, not governance (opens in a new tab)

  • “Issue” is expected behavior.

[M-03] Alter velo receptions computation (opens in a new tab)

  • See judge’s comments, attack could only be pulled by deployer and wasn’t.

[M-04] Malicious user can populate rewards array with tokens of their interest reaching limits of MAX_REWARD_TOKENS (opens in a new tab)

  • Judge’s comment is accurate, does allow team to change real reward tokens (but so far non-issue).

[M-05] Bribe.sol is not meant to handle fee-on-transfer tokens (opens in a new tab)

[M-06] Voting tokens may be lost when given to non-EOA accounts (opens in a new tab)

  • Confirmed, see judge’s comment.

[M-07] RedemptionSender should estimate fees to prevent failed transactions (opens in a new tab)

[M-08] Temporary DOS by calling notifyRewardAmount() in Bribe/Gauge with malicious tokens (opens in a new tab)

[M-09] Owner's delegates should be decreased in _burn() (opens in a new tab)

[M-10] Rewards aren't updated before user's balance change in Gauge's withdrawToken (opens in a new tab)

[M-11] Griefing Attack By Extending The Reward Duration (opens in a new tab)

[M-12] Rewards can be locked in Bribe contract because distributing them depends on base token reward amount and Gauge.deliverBribes() is not always called by Voter.distribute() (opens in a new tab)

[M-13] Bribe Rewards Not Collected In Current Period Will Be Lost Forever (opens in a new tab)

[M-14] Wrong reward distribution in Bribe because deliverReward() won't set tokenRewardsPerEpoch[token][epochStart] to 0 (opens in a new tab)

  • Will be addressed with an upcoming fix (will be linked here once contracts are live). In the interim, relevant parties (i.e. protocols who wish to bribe) should get in touch with our team.

[M-15] Wrong calculation for the new rewardRate[token] can cause some of the late users can not get their rewards (opens in a new tab)

[M-16] Wrong DOMAIN_TYPEHASH definition (opens in a new tab)

  • Not addressed in mainnet deploy, but see judge’s comment.

[M-17] WeVE (FTM) may be lost forever if redemption process is failed (opens in a new tab)

  • Error won’t happen as long as contracts were initialized correctly (which they were in prod).