CertiK reviewed Y8U on 18 Apr 2024: 16 findings, 0 critical; 11 marked fixed, 5 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 2 | 0 | 0 |
| Medium | 0 | 0 | 1 |
| Low | 3 | 0 | 10 |
The `tgeTimestamp` is designed to be called once at token generation to mark when vestings start. If this is called after the token generation, then vestings will reset so that tokens will vest for a longer period than intended. The functions `setMerkleRootPrivateSale()`, `setMerkleRootStrategicSale()`, and `setMerkleRootStrategicSale2()` are designed to be called once to set the merkle roots for the respective sales. These should be set only once after the merkle tree for each sale is created, otherwise, it may be set to a new merkle root for a different merkle tree. If these are known prior to deployment, these variables can be made immutable and they can be set upon construction.
In the function `calculateClaimableStrategicSale()` the maximum claimable amount should be `totalAllocation`. However, if `monthOfDistribution = 22`, then the calculated `totalClaimable` will be approximately `1.0000002 * totalAllocation` which exceeds this amount. This will be set to `totalAllocation` in the later `if`-block so that it does not create issues. Instead of using the `if`-block, the case when `monthOfDistribution = 22` can instead by handled in the same way as when `monthOfDistribution > 22` eliminating the need for the `if`-block and reducing gas costs. In the function `claimTeam()` the maximum claimable amount should be `100_000_000 * (10 ** uint256(18))`. However, if `monthOfDistribution = 46`, then the calculated `totalClaimable` will be `100_000_008 * (10 ** uint256(18))` which exceeds this amount. This will be set to `100_000_000 * (10 ** uint256(18))` in the later `if`-block so that it does not create issues. Instead of using the `if`-block, the case when `monthOfDistribution = 46` can instead by handled in the same way as when `monthOfDistribution > 46` eliminating the need for the `if`-block and reducing gas costs. In the function `claimMarketing()` the maximum claimable amount should be `95_000_000 * (10 ** uint256(18))`. However, if `monthOfDistribution = 42`, then the calculated `totalClaimable` will be `95_000_004 * (10 ** uint256(18))` which exceeds this amount. This will be set to `95_000_000 * (10 ** uint256(18))` in the later `if`-block so that it does not create issues. Instead of using the `if`-block, the case when `monthOfDistribution = 42` can instead by handled in the same way as when `monthOfDistribution > 42` eliminating the need for the `if`-block and reducing gas costs. In the function `claimTreasury()` the maximum claimable amount should be `100_000_000 * (10 ** uint256(18))`. However, if `monthOfDistribution = 36`, then the calculated `totalClaimable` will be `100_000_008 * (10 ** uint256(18))` which exceeds this amount. This will be set to `100_000_000 * (10 ** uint256(18))` in the later `if`-block so that it does not create issues. Instead of using the `if`-block, the case when `monthOfDistribution = 36` can instead by handled in the same way as when `monthOfDistribution > 36` eliminating the need for the `if`-block and reducing gas costs. In the function `claimAiMining()` the maximum claimable amount should be `100_000_000 * (10 ** uint256(18))`. However, if `monthOfDistribution = 39`, then the calculated `totalClaimable` will be `100_000_008 * (10 ** uint256(18))` which exceeds this amount. This will be set to `100_000_000 * (10 ** uint256(18))` in the later `if`-block so that it does not create issues. Instead of using the `if`-block, the case when `monthOfDistribution = 39` can instead by handled in the same way as when `monthOfDistribution > 39` eliminating the need for the `if`-block and reducing gas costs.
In the function `claimPrivateSale()`: - The check if `monthOfDistribution >= 4` can be removed, because all other cases when `monthOfDistribution < 4`, are already handled. - `else if (monthOfDistribution > 23)` can be simplified to `else`. This is because all other cases are already handled. - The case when `monthOfDistribution = 23` can be handled in the same way as when `monthOfDistribution > 23`. This is because it will calculate approximately the same value (as there may be a slight rounding error) so that it can save gas by not having to do the calculation. In the function `calculateClaimableStrategicSale()`: - The check if `monthOfDistribution >= 4` can be removed, because all other cases when `monthOfDistribution < 4`, are already handled. - `else if (monthOfDistribution > 22)` can be simplified to `else`. This is because all other cases are already handled. In the function `calculateClaimableStrategicSale2()`: - The check if `monthOfDistribution >= 4` can be removed, because all other cases when `monthOfDistribution < 4`, are already handled - `else if (monthOfDistribution > 23)` can be simplified to `else`. This is because all other cases are already handled. - The case when `monthOfDistribution = 23` can be handled in the same way as when `monthOfDistribution > 23`. This is because it will calculate approximately the same value (as there may be a slight rounding error) so that it can save gas by not having to do the calculation. In the function `claimAirdrop()`: - The check if `monthOfDistribution >=1` can be removed, as `monthOfDistribution = (block.timestamp - tgeTimestamp) / 30 days + 1`, which is always greater than or equal to 1. - `else if (monthOfDistribution > 5)` can be simplified to `else`. This is because all other cases are already handled. In the function `claimDevelopment()`: - If `monthOfDistribution = 49`, then `totalClaimable = 99_999_984 * (10 ** uint256(18))` so that only 16 more tokens will become claimable in month 50. If this works as intended, then it should be left as is. However, if all tokens are to be unlocked when `monthOfDistribution = 49`, then it can be handled in the same way as the case when `monthOfDistribution > 49`. In the function `claimEcosystem()`: - The check if `monthOfDistribution >=1` can be removed, as `monthOfDistribution = (block.timestamp - tgeTimestamp) / 30 days + 1`, which is always greater than or equal to 1. - The check if `monthOfDistribution >= 5` can be removed. This is because the first `if`-block is executed if `monthOfDistribution < 5`, thus this block is only reached if `monthOfDistribution >= 5`. - `else if (monthOfDistribution > 40)` can be simplified to `else`. This is because all other cases are already handled. - The case when `monthOfDistribution = 40` can be handled in the same way as when `monthOfDistribution > 40`. This is because it will calculate the same value` so that it can save gas by not having to do the calculation.
Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=110 uint256 monthlyReleaseForMonths4To23 = totalAllocation * 970_000 / 20_000_000; // Monthly release for months 4 to 23 ``` ```solidity=111 totalClaimable = totalAllocation * 600_000 / 20_000_000 + (monthOfDistribution - 3) * monthlyReleaseForMonths4To23; ``` --- ```solidity=150 uint256 monthlyReleaseForMonths4To22 = totalAllocation * 1_157_895 / 25_000_000; // Monthly release for months 4 to 22 ``` ```solidity=151 totalClaimable = totalAllocation * 3_000_000 / 25_000_000 + (monthOfDistribution - 3) * monthlyReleaseForMonths4To22; ``` --- ```solidity=191 uint256 monthlyReleaseForMonths4To23 = totalAllocation * 485_000 / 10_000_000; // Monthly release for months 4 to 23 ``` ```solidity=192 totalClaimable = totalAllocation * 300_000 / 10_000_000 + (monthOfDistribution - 3) * monthlyReleaseForMonths4To23; ``` This can cause issues when users should be able to withdraw their `totalAllocation`, but a small amount will be left and cannot be withdrawn until another month has passed.
The following functions emit the event `Claimed(msg.sender, claimable)`, which can make it hard to distinguish from the emitted events what the funds where claimed for. - `claimEcosystem()` - `claimAiMining()` - `claimDevelopment()` - `claimTreasury()` - `claimMarketing()` - `claimTeam()` - `claimAirdrop()`
The functions `claimPrivateSale()`, `claimStrategicSale()`, and `claimStrategicSale2()` all utilize a merkle root and proof to determine if the `msg.sender` does have the input `totalAllocation`. If the contract is used on multiple chains and the same merkle trees are used, then if an address is allocated on one chain, then they will also be able to claim it on the other chain.
State variables that never change should be declared as `constant` to save gas. The following variables are set and never changed: - `totalClaimedPublicSale` - `totalClaimedLiquidityExchangesMM`
The comment in the function `claimAirdrop()` states that "Entire allocation is available after 46 months". However, it is available after 5 months.
In the function `calculateClaimablePrivateSale()`, in all cases, `totalClaimable` is always less than or equal to `totalAllocation`. Thus `totalClaimable > totalAllocation` will never be true, so the `if`-block will never be executed. In the function `calculateClaimableStrategicSale2()`, in all cases, `totalClaimable` is always less than or equal to `totalAllocation`. Thus `totalClaimable > totalAllocation` will never be true, so the `if`-block will never be executed. In the function `claimAirdrop()`, in all cases, `totalClaimable` is always less than or equal to `5_000_000 * (10 ** uint256(18))`. Thus `totalClaimable > AIRDROP` will never be true, so the `if`-block will never be executed. In the function `claimDevelopment()`, in all cases, `totalClaimable` is always less than or equal to `100_000_000 * (10 ** uint256(18))`. Thus `totalClaimable > DEVELOPMENT` will never be true, so the `if`-block will never be executed. In the function `claimEcosystem()`, in all cases, `totalClaimable` is always less than or equal to `360_000_000 * (10 ** uint256(18))`. Thus `totalClaimable > ECOSYSTEM` will never be true, so the `if`-block will never be executed.
The following checks will always pass and can be removed: - In the function `claimAirdrop()`, the check that `totalClaimedAirdrop + claimable <= AIRDROP`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedAirdrop`, so that `totalClaimedAirdrop + claimable = totalClaimable`, which is always less than or equal to `AIRDROP`. - In the function `claimTeam()`, the check that `totalClaimedTeam + claimable <= TEAM`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedTeam`, so that `totalClaimedTeam + claimable = totalClaimable`, which is always less than or equal to `TEAM`. - In the function `claimMarketing()`, the check that `totalClaimedMarketing + claimable <= MARKETING`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedMarketing`, so that `totalClaimedMarketing + claimable = totalClaimable`, which is always less than or equal to `MARKETING`. - In the function `claimTreasury()`, the check that `totalClaimedTreasury + claimable <= TREASURY`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedTreasury`, so that `totalClaimedTreasury + claimable = totalClaimable`, which is always less than or equal to `TREASURY`. - In the function `claimDevelopment()`, the check that `totalClaimedDevelopment + claimable <= DEVELOPMENT`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedDevelopment`, so that `totalClaimedDevelopment + claimable = totalClaimable`, which is always less than or equal to `DEVELOPMENT`. - In the function `claimAiMining()`, the check that `totalClaimedAiMining + claimable <= AI_MINING`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedAiMining`, so that `totalClaimedAiMining + claimable = totalClaimable`, which is always less than or equal to `AI_MINING`. - In the function `claimEcosystem()`, the check that `totalClaimedEcosystem + claimable <= ECOSYSTEM`. - At this point in the logic `claimable = totalClaimable - alreadyClaimed` or it reverted and `alreadyClaimed = totalClaimedEcosystem`, so that `totalClaimedEcosystem + claimable = totalClaimable`, which is always less than or equal to `ECOSYSTEM`.
The functions `calculateClaimablePrivateSale()`, `calculateClaimableStrategicSale()`, and `calculateClaimableStrategicSale2()` are public and can be called by anyone to determine the amount they should be able to claim from a sale. The functions do not make any checks on the `tgeTimestamp`, so they will return the wrong value if the `tgeTimestamp` has not been set. This is because it will default to zero so that `monthOfDistribution` will be the amount of months since January 1st, 1970. This will result in them showing that the input `totalAllocation` is claimable, when the actual claimable amount is zero.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (1)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us