Platform: Code4rena
Start Date: 26/05/2022
Pot Size: $75,000 USDT
Total HM: 31
Participants: 71
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 18
Id: 126
League: ETH
Rank: 8/71
Findings: 3
Award: $3,119.75
🌟 Selected for report: 1
🚀 Solo Findings: 1
937.187 USDT - $937.19
Judge has assessed an item in Issue #234 as Medium risk. The relevant finding follows:
uint256 _veAssetEarned = _amount.mul(veTokenMinter.veAssetWeights(address(this))).div( veTokenMinter.totalWeight() ); Recommended Mitigation Ensure that weight is updated to zero when removing operator:
function removeOperator(address _operator) public onlyOwner { updateveAssetWeight(_operator, 0); // modify visibility to public operators.remove(_operator); } Relevant Links https://github.com/code-423n4/2022-05-vetoken/blob/2d7cd1f6780a9bcc8387dea8fecfbd758462c152/contracts/VeTokenMinter.sol#L36-L46 https://github.com/code-423n4/2022-05-vetoken/blob/2d7cd1f6780a9bcc8387dea8fecfbd758462c152/contracts/Booster.sol#L607-L609
#0 - JeeberC4
2022-07-28T19:55:08Z
Duplicate of #120
🌟 Selected for report: shenwilly
2082.6377 USDT - $2,082.64
Governance can burn any amount of VeToken
from any address.
Unlike VE3Token
which is minted when users deposit veAsset and burned when users withdraw, the burn
function in the governance token VeToken.sol
is unnecessary and open up the risk of malicious/compromised governance burning user's token.
Consider removing the function, or modify the burn function so it only allows msg.sender
to burn the token:
function burn(uint256 _amount) external { _burn(msg.sender, _amount); }
#0 - solvetony
2022-06-15T17:28:47Z
We might update readme on that case
#1 - GalloDaSballo
2022-07-25T01:17:58Z
The warden has shown how the operator, which may be the DAO or a privileged Multisig, can burn any tokens.
While the functionality is part of the system for VE3Token as the system uses it to track underlying ownership, burning of balances from arbitrary addresses is a dangerous form of admin privilege.
I'd recommend deleting the burn function
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xf15ers, BouSalman, Chom, Deivitto, Dravee, ElKu, FSchmoede, Funen, GimelSec, Hawkeye, MiloTruck, Picodes, SecureZeroX, SmartSek, TerrierLover, WatchPug, _Adam, asutorufos, berndartmueller, c3phas, catchup, cccz, cogitoergosumsw, cryptphi, csanuragjain, delfin454000, dipp, ellahi, gzeon, hansfriese, horsefacts, hyh, kirk-baird, minhquanym, oyc_109, pauliax, reassor, robee, sashik_eth, shenwilly, simon135, sorrynotsorry, sseefried, unforgiven, xiaoming90, z3s
99.9237 USDT - $99.92
VeTokenMinter.removeOperator
When removing operator, there is no check to make sure that veAssetWeights[operator]
has been removed. If governance forgot to call updateveAssetWeight
before removing the operator, totalWeight
would be inflated and user would get lesser amount when claiming reward:
uint256 _veAssetEarned = _amount.mul(veTokenMinter.veAssetWeights(address(this))).div( veTokenMinter.totalWeight() );
Ensure that weight is updated to zero when removing operator:
function removeOperator(address _operator) public onlyOwner { updateveAssetWeight(_operator, 0); // modify visibility to public operators.remove(_operator); }
VE3DRewardPool.addReward
Duplicates in rewardTokens
could cause irregular behaviour in the system.
Unfortunately we didn't have enough time to test whether a duplicate could break functionalities in the system (especially during VE3DRewardPool.getReward
). Recommend to prevent the possibility of duplication as the duplicated element cannot be removed and it would cost more gas than necessary when iterating over rewardTokens
.
Add a check to ensure _rewardToken
isn't already added:
function addReward( address _rewardToken, address _veAssetDeposits, address _ve3TokenRewards, address _ve3Token ) external onlyOwner { rewardTokenInfo[_rewardToken].veAssetDeposits = _veAssetDeposits; rewardTokenInfo[_rewardToken].ve3TokenRewards = _ve3TokenRewards; rewardTokenInfo[_rewardToken].ve3Token = _ve3Token; if (!rewardTokens.contains(_rewardToken)) { rewardTokens.add(_rewardToken); } }
_stakeAddress
Malicious depositor could call VeAssetDepositor.deposit
with a custom _stakeAddress
contract, leading to unexpected behaviour.
function deposit( uint256 _amount, bool _lock, address _stakeAddress ) public { ... //mint here ITokenMinter(minter).mint(address(this), _amount); //stake for msg.sender IERC20(minter).safeApprove(_stakeAddress, _amount); IRewards(_stakeAddress).stakeFor(msg.sender, _amount); ... }
For instance, someone could supply a contract that did nothing when stakeFor
is called, leaving the token in the contract and unused allowance.
While it currently doesn't negatively impact the system, future changes on the code might not consider this and could potentially make a false assumption. In addition, analytics that track Deposited
event might record wrong values.
Consider adding a token balance check before and after stakeFor
to ensure token is actually staked, or alternatively, add a whitelist for _stakeAddress
.
#0 - GalloDaSballo
2022-07-09T18:08:04Z
Valid Low, TODO: See if is Med
Invalid, the library does dup checks by default
Valid finding
2L
#1 - GalloDaSballo
2022-07-28T17:26:42Z
TODO - Raise:
Updated Score
1L