Bribe.sol The Bribe.sol contract distributes bribes for a given Gauge. Each Gauge had a Bribe contract attached to it, and each Bribe can accept multiple (up to 16) different tokens as bribes. During each epoch, veALCX stakers can collect bribes for a given gauge if they voted on that gauge in the previous epoch.
the total amount of bribe b on pool p claimable by a veALCX NFT with token-ID i during a given epoch n is equal to the proportion of total veALCX power that that NFT used to vote on pool p
Vulnerability Details
In the Bribe.sol contract, the deposit function increases the totalVoting tracker's amount. However during the withdrawal's execution, the totalVoting's value is NOT decreased.
This missing totalVoting decrease directly impacts the voting power checkpointing and calculations by writing the wrong amount:
Priot to running forge test --match-contract PassthroughGauge -vvvv --fork-url https://eth-mainnet.alchemyapi.io/v2/{YOUR_ALCHEMY_API_KEY}, paste the following code to the src/test/PassthroughGauge.t.sol file:
// SPDX-License-Identifier: GPL-3pragmasolidity ^0.8.15;import"./BaseTest.sol";contractPassthroughGaugeTestisBaseTest {uint256 snapshotWeek =17120807;uint256 platformFee =400; // 4%uint256 DENOMINATOR =10_000; // denominates weights 10_000 = 100%functionsetUp() public {setupContracts(block.timestamp); }// Rewards should be passed through to external gauges// Add tests for gauges as they are addedfunctiontestPassthroughGaugeRewards() public {uint256 tokenId =createVeAlcx(admin, TOKEN_1, MAXTIME,false); hevm.startPrank(admin);uint256 period = minter.activePeriod(); hevm.warp(period);assertEq(sushiGauge.rewardToken(),address(alcx),"incorrect reward token");uint256 sushiBalanceBefore = alcx.balanceOf(sushiPoolAddress);address[] memory pools =newaddress[](1); pools[0] = sushiPoolAddress;uint256[] memory weights =newuint256[](1); weights[0] =5000;// Move forward epoch hevm.warp(period +1weeks); IBribe bribeTest =IBribe(voter.bribes(voter.gauges(pools[0]))); vm.stopPrank(); vm.startPrank(address(voter)); bribeTest.deposit(300*1e18, tokenId);for (uint256 i =0; i < pools.length; i++) { console.log("totalVoting BEFORE the withdrawal", bribeTest.totalVoting()); }// voter.vote(tokenId, pools, weights, 0); bribeTest.withdraw(300*1e18, tokenId);for (uint256 i =0; i < pools.length; i++) { console.log("totalVoting AFTER the withdrawal", bribeTest.totalVoting()); } }}
Notice that the totalVoting amount HAS NOT decreased after the withdrawal.