Platform: Code4rena
Start Date: 07/07/2023
Pot Size: $121,650 USDC
Total HM: 36
Participants: 111
Period: 7 days
Judge: Picodes
Total Solo HM: 13
Id: 258
League: ETH
Rank: 102/111
Findings: 1
Award: $15.92
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: bin2chen
Also found by: 0x11singh99, 0xWaitress, 0xbepresent, ABAIKUNANBAEV, ArmedGoose, Bauchibred, DadeKuma, GREY-HAWK-REACH, GalloDaSballo, Inspecktor, Jeiwan, Kaysoft, MohammedRizwan, Rolezn, Vagner, alexzoid, alymurtazamemon, ayden, banpaleo5, catellatech, dacian, erebus, eyexploit, fatherOfBlocks, grearlake, joaovwfreire, keccak123, kutugu, lanrebayode77, markus_ether, nadin, naman1778, rvierdiiev, squeaky_cactus, volodya, yixxas
15.9228 USDC - $15.92
Number | Issues Details | Count |
---|---|---|
[L-1] | Unrestricted for- loops Making External Calls Could Lead to a DOS Attack | 1 |
[L-2] | Extending the Timestamp Limit to Avoid Future Reverting | 4 |
[L-3] | Validate Address Parameters to Avoid Issues | 12 |
[L-4] | Utilize _safeMint for Safer Minting | 1 |
[L-5] | Add Array Length Checks | 2 |
[L-6] | Implement Reentrancy Guard for the withdraw Function | 5 |
[L-7] | Limitations of type(uint256).max with Some Tokens | 1 |
[L-8] | Adopt safeTransfer for Safer Token Transfers | 1 |
[N-1] | Encourage Uniform Naming Convention for Immutables | 1 |
[N-2] | Apply delete for Variable Clearance | 1 |
[N-3] | Utilizing Underscores for Number Literals | 2 |
[N-4] | Essential Modifications Should Follow a Two-stage Protocol | 4 |
[N-5] | Emitting Events for Significant Parameter Changes | 3 |
[N-6] | Upgrade to Ownable2Step for Improved Ownership Management | 1 |
[N-7] | Adopt safeTransferOwnership for Safer Ownership Transfers | 1 |
for-
loops Making External Calls Could Lead to a DOS Attack</details>File: Vault.sol for (uint w = 0; w < _winners.length; w++) { for (uint p = 0; p < prizeIndicesLength; p++) {
It has been observed that limiting the timestamp variable to fit within a uint32
may lead to a call reverting in the year 2106. To prevent this issue and ensure the continued functionality of the contract beyond that timeframe, it is recommended to extend the timestamp limit.
File: TwabLib.sol uint32 currentTime = uint32(block.timestamp);
File: ObservationLib.sol uint32 beforeOrAtTimestamp = beforeOrAt.timestamp;
File: TwabLib.sol uint32 currentTime = uint32(block.timestamp);
</details>File: TwabLib.sol uint32 currentTime = uint32(block.timestamp);
To prevent transaction reverts and gas wastage, validate address parameters to ensure they are not set to the zero address (0x0).
<details> <summary><i>There are 18 instances of this issue:</i></summary>File: TwabController.sol _to
File: Vault.sol _account
File: PrizePool.sol _to
File: TwabController.sol _from
File: PrizePool.sol _drawManager
File: Vault.sol _recipient
File: PrizePool.sol _winner
File: PrizePool.sol _prizeRecipient
File: PrizePool.sol _feeRecipient
File: Vault.sol yieldFeeRecipient_
File: Vault.sol _receiver
File: TwabController.sol _vault
File: TwabController.sol _to
File: Vault.sol _receiver
File: Vault.sol _owner
File: Vault.sol claimer_
</details>File: Vault.sol _receiver
_safeMint
for Safer MintingTo enhance the safety and security of the minting process in your contract, it is recommended to use the _safeMint
function instead of _mint
. The _safeMint
function incorporates additional checks and safeguards to mitigate potential risks.
</details>File: Vault.sol _mint(_account, _amountOut);
File: Vault.sol function claimPrizes
</details>File: Claimer.sol function claimPrizes
withdraw
FunctionTo prevent reentrancy attacks and ensure the safety of funds in your contract, it is essential to implement a reentrancy guard in the withdraw
function. Without a proper guard, malicious contracts or attackers can exploit potential vulnerabilities and repeatedly call the withdraw
function to drain funds.
File: Vault.sol function _withdraw( address _caller, address _receiver, address _owner, uint256 _assets, uint256 _shares ) internal virtual override { if (_caller != _owner) { _spendAllowance(_owner, _caller, _shares); } _burn(_owner, _shares); _yieldVault.withdraw(_assets, address(this), address(this)); SafeERC20.safeTransfer(IERC20(asset()), _receiver, _assets); emit Withdraw(_caller, _receiver, _owner, _assets, _shares); }
File: PrizePool.sol function withdrawReserve(address _to, uint104 _amount) external onlyDrawManager { if (_amount > _reserve) { revert InsufficientReserve(_amount, _reserve); } _reserve -= _amount; _transfer(_to, _amount); emit WithdrawReserve(_to, _amount); }
File: PrizePool.sol function totalWithdrawn() external view returns (uint256) { return _totalWithdrawn; }
File: Vault.sol function withdraw( uint256 _assets, address _receiver, address _owner ) public virtual override returns (uint256) { if (_assets > maxWithdraw(_owner)) revert WithdrawMoreThanMax(_owner, _assets, maxWithdraw(_owner)); uint256 _shares = _convertToShares(_assets, Math.Rounding.Up); _withdraw(msg.sender, _receiver, _owner, _assets, _shares); return _shares; }
</details>File: PrizePool.sol function withdrawClaimRewards(address _to, uint256 _amount) external { uint256 _available = claimerRewards[msg.sender]; if (_amount > _available) { revert InsufficientRewardsError(_amount, _available); } claimerRewards[msg.sender] -= _amount; _transfer(_to, _amount); emit WithdrawClaimRewards(_to, _amount, _available); }
type(uint256).max
with Some TokensIt is important to note that using type(uint256).max
as the allowance value when calling the approve()
function may not work as expected with certain tokens. While it is commonly used to represent an infinite allowance, some token contracts do not interpret the maximum value as infinite and may have specific implementations or limitations.
</details>File: Vault.sol _asset.safeApprove(address(liquidationPair_), type(uint256).max);
safeTransfer
for Safer Token TransfersConsider using the safeTransfer
function instead of the transfer
function for safer token transfers within your contract.
</details>File: Vault.sol _twabController.transfer(_from, _to, uint96(_shares));
In the current code, certain immutables are named with capital letters and underscores, while others are not. For enhanced code quality, it's recommended to adopt a consistent naming convention for all immutables.
<details> <summary><i>There are 1 instances of this issue:</i></summary>File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_2_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_3_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_4_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_5_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_6_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_7_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_8_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_9_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_10_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_11_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_12_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_13_TIERS;
File: TieredLiquidityDistributor.sol UD60x18 internal immutable CANARY_PRIZE_COUNT_FOR_14_TIERS;
File: TieredLiquidityDistributor.sol uint8 public immutable tierShares;
File: TieredLiquidityDistributor.sol uint8 public immutable canaryShares;
</details>File: TieredLiquidityDistributor.sol uint8 public immutable reserveShares;
delete
for Variable Clearance</details>File: PrizePool.sol claimCount = 0; canaryClaimCount = 0; largestTierClaimed = 0;
To improve the readability of number literals, it is recommended to use underscores as separators within the numbers. This technique enhances code comprehension by visually breaking down large numbers into more manageable segments.
For example, instead of writing 1000000
, you can represent the same value as 1_000_000
. The underscores have no impact on the numerical value but make it easier for developers to read and understand the magnitude of the number at a glance.
File: Vault.sol /// @notice Yield fee percentage represented in integer format with 9 decimal places (i.e. 10000000 = 0.01 = 1%).
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_2_3 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_3_4 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_4_5 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_5_6 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_6_7 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_7_8 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_8_9 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_9_10 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_10_11 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_11_12 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_12_13 = SD59x18.wrap(1000000000000000000);
File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_13_14 = SD59x18.wrap(1000000000000000000);
</details>File: TieredLiquidityDistributor.sol SD59x18 internal constant TIER_ODDS_14_15 = SD59x18.wrap(1000000000000000000);
It's recommended that crucial alterations adhere to a two-stage procedure.
<details> <summary><i>There are 4 instances of this issue:</i></summary>File: Vault.sol function setClaimer(address claimer_) external onlyOwner returns (address) {
File: Vault.sol function setYieldFeeRecipient(address yieldFeeRecipient_) external onlyOwner returns (address) {
File: Vault.sol function setYieldFeePercentage(uint256 yieldFeePercentage_) external onlyOwner returns (uint256) {
</details>File: PrizePool.sol function setDrawManager(address _drawManager) external {
It is important to emit events when modifying state variables to enable effective monitoring using off-chain monitoring tools. Emitting events facilitates the tracking of activities related to critical parameter changes.
<details> <summary><i>There are 3 instances of this issue:</i></summary>File: Vault.sol function setYieldFeeRecipient(address yieldFeeRecipient_) external onlyOwner returns (address) {
File: PrizePool.sol function setDrawManager(address _drawManager) external {
</details>File: Vault.sol function setClaimer(address claimer_) external onlyOwner returns (address) {
Ownable2Step
for Improved Ownership ManagementFile: Vault.sol import { Ownable } from "owner-manager-contracts/Ownable.sol";
File: Vault.sol contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable {
</details>File: Vault.sol ) ERC4626(asset_) ERC20(name_, symbol_) ERC20Permit(name_) Ownable(owner_) {
safeTransferOwnership
for Safer Ownership TransfersConsider utilizing the safeTransferOwnership
function instead of the standard transferOwnership
function for more secure ownership transfers in your contract.
</details>File: Vault.sol import { Ownable } from "owner-manager-contracts/Ownable.sol";
#0 - c4-judge
2023-07-18T19:13:59Z
Picodes marked the issue as grade-b
#1 - PierrickGT
2023-09-07T20:48:33Z
L-1: Added comment about winners array length in this PR: https://github.com/GenerationSoftware/pt-v5-claimer/pull/18 I don't see any problem with the for loop itself though, the transaction would just revert if out of gas.
L-2: fixed in this PR: https://github.com/GenerationSoftware/pt-v5-twab-controller/pull/23
L-3: no line number, so really difficult to know which code is being referenced.
L-4: this is an internal function that calls twabController.mint()
, which has nothing to do with OZ safeMint.
L-5: has been fixed: https://github.com/GenerationSoftware/pt-v5-claimer/blob/7b62fb8c7e40b08631813eec4163a866c3a313bc/src/Claimer.sol#L119
L-6: no need for a reentrancy guard.
L-7: has been removed.
L-8: again, not related to OZ safeMint.
N-1: won't update naming convention
N-2: the code is more legible when assigning 0
than using delete
N-3: won't implement this suggestion
N-4: won't implement a two stage procedures for setter functions
N-5: has already been fixed
N-6 and N-7: we already use a two steps ownership process