CertiK reviewed Seedify (Old IDO & NFT Launchpads Upgrades (Agency Work)) on 3 Jan 2023: 15 findings, 0 critical; 11 marked fixed, 4 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 | 1 | 0 | 2 |
| Medium | 1 | 0 | 1 |
| Low | 2 | 0 | 8 |
In `buyTokens()` it is checked if the `msg.sender` is whitelisted for the input `tier`. If they are not it reverts, but if they are it then increments `buyInOneTier[msg.sender]` by the input `amount`. The name implies that it is keeping track of the amount bought for tier one, however it keeps track of the amount bought by `msg.sender` in all tiers. This can cause issues if a user buys in different tiers. For example, if a user buys 100,000 tokens in tier 1 and also buys 100,000 tokens in tier 2. After the `refundThresholdTime` the user could call `refund(1,200000)`, which will pass all checks provided the `totalBUSDInTiers[0]` is at least 200,000. This happens because both buys increment `buyInOneTier[msg.sender]` so that it is 200,000 before `refund()` is called. It decrements `totalBUSDInTiers[0]` by 200,000 when it should only decrement by 100,000. If enough users try to refund, this can cause users to be unable to refund as when `totalBUSDIntiers[tier-1]` is decremented in `refund()` it may revert due to an underflow error as the proper amount is not subtracted from each tier.
In the `refund()` function, the amount deducted from `totalBUSDReceivedInAllTier` would not be equal to the amount added to `totalBUSDReceivedInAllTier` whenever the value of `tokensPerBUSD` is changed before refunding and after buying the tokens. In the case when the value of `tokensPerBUSD` is decreased, the amount subtracted from the `totalBUSDReceivedInAllTier` would be more than the amount added and could lead to an underflow error even if the `projectOwner` has enough tokens to send to the `msg.sender`.
The `totalUserInTiers[]` should be capturing the number of members in these following arrays. - `whitelistTierOne` - `whitelistTierTwo` - `whitelistTierThree` - `whitelistTierFour` - `whitelistTierFive` - `whitelistTierSix` - `whitelistTierSeven` - `whitelistTierEight` - `whitelistTierNine` But the `totalUserInTiers[]` array is not updated anytime a new user is added with `addWhitelist()` function. In the `constructor()`, value at every concerned index is initialized to `2` without adding any user to these arrays the aforementioned arrays.
Both `buyTokens()` and `refund()` call `getWhitelist()` for each tier to determine which tier a user is in.
The function 'refund()` is never called internally within the contract.
In the function `refund()`, both the fields of `fundUser[msg.sender]` are subtracted from `fundUser[msg.sender]` to update the `fundUser[msg.sender]`, which will always amount to both fields of `fundUser[msg.sender]` equal to zero.
The following checks are not made: - In the `constructor()` of `INOLaunchpadSmartContract`, it is not checked that `_busdPrice` is nonzero. If it is mistakingly set to zero, then the `nftPrice` will also be zero. - In the `constructor()` of `SeedifyFundBUSD`, it is not checked that `_projectOwner` is not the zero address. - In the `constructor()` of `SeedifyFundBUSD`, it is not checked if the `_saleStartTime` is less than the '_saleEndTime'. - In `seedifyFundBUSD`, the `maxAllocPerUserInTiers[i]` should be always greater than or equal to `minAllocaPerUserInTiers[i]`. In the functions `updateTierValues()`, `updateTeirUsersValue()`, and the `constructor()`, it is not checked that the `maxAllocPerUserInTiers[i]` is greater than or equal to `minAllocaPerUserInTiers[i]` for all `tiers` `i`. - In the `constructor()` of `seedifyFundBUSD`, it is not checked that if all the values in the `_tiersValue[]` array are not zero. - In the function `addWhitelist()` of `seedifyFundBUSD`, it is not checked that the input `_address` is not already whitelisted for the input `_tier`. This can cause issues if an address is added multiple times because it will cause the `maxAllocaPerUserInTiers` to be calculated incorrectly.
The `updateTierValues()` does not check if the input `_tiersValue[]` has the value '0' for any valid index, or not. Because of which `totalUserInTiers[i]` could possibly be '0' in the following expression: ```solidity=137 for (uint256 i = 0; i < 9; i++) { tiersMaxCap[i] = _tiersValue[i]; maxAllocaPerUserInTiers[i] = tiersMaxCap[i] / totalUserInTiers[i]; } ```
The variable `totalparticipants` in `SeedifyFundBUSD.sol` and 'totalSupply' in `INOLaunchPadSmartContract.sol` are declared and initialized in the constructor, but never used. In the contract SeedifyFundBUSD.sol, the following variables are declared but never used: - `buyInTwoTier` - `buyInThreeTier` - `buyInFourTier` - `buyInFiveTier` - `buyInSixTier` - `buyInSevenTier` - `buyInEightTier` - `buyInNineTier`
The return value of the transfer()/transferFrom() call is not checked in any case of the `refund()` function.
The following variables assigned in the constructor can be declared as `immutable`. - `projectOwner` Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (2)
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