CertiK reviewed Seedify (Seedify NFT Launchpad) on 23 Apr 2024: 30 findings, 0 critical; 20 marked fixed, 10 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 | 2 |
| Medium | 0 | 0 | 7 |
| Low | 8 | 0 | 11 |
In contract `RestrictedWritable`, the role `DEFAULT_ADMIN_ROLE` has authority over the following functions: - `grantRole()`: grant `role` to `account`. - `revokeRole()`: revoke `role` from `account`. - `closeSale()`: close the sale manually. - `openSale()`: open sale manually. - `pauseSale()`: pause sale manually. - `resumeSale()`: reopen sale manually. - `recoverLostERC20()`: transfer any kinds of `ERC20` tokens to a specified address. - `closePhases()`: close phases manually. - `openPhase()`: open phase manually. - `pausePhase()`: pause phase manually. - `resumePhase()`: reopen phase manually. - `updatePhaseEndDate()`: update a new `endAt` value for a specified phase. - `updatePhaseMaxCapAndMerkleRoot()`: update a new `maxPhaseCap` value and `merkleRoot` value. - `updatePhaseMerkleRoot()`: update a new `merkleRoot` value. - `updatePhaseStartDate()`: update a new `startAt` value for a specified phase. --- In contract `INOFactory`, the role `DEFAULT_ADMIN_ROLE` has authority over the following functions: - `grantRole()`: grant `role` to `account`. - `revokeRole()`: revoke `role` from `account`. - `createINO()`: create a new `Initial NFT Offering` contract. - `updateDefaultINO()`: set a default `Initial NFT Offering` contract. - `setMaxLoop()`: set a loop upper limit for the query function `getInosDetails()`. --- In contract `ERC721ContractMetadataCloneable`, the role `owner` has authority over the following functions: - `setBaseURI()`: sets the base `URI` for the token metadata and emits an event. - `setContractURI()`: sets the contract `URI` for contract metadata. - `emitBatchMetadataUpdate()`: emit an event notifying metadata updates for a range of token ids. - `_setMaxSupply()`: sets the max token supply and emits an event. - `setProvenanceHash()`: sets the provenance hash and emits an event. - `renounceOwnership()`: transfer ownership to zero address. - `transferOwnership()`: transfer ownership to any non-zero address. - `cancelOwnershipTransfer()`: remove potential new owner address. --- In contract `ERC721Base`, the role `ino` has authority over the following functions: - `mint()`: mint `NFT` to specified addresses. In contract `ERC721Base`, the role `owner` has authority over the following functions: - `deleteDefaultRoyalty()`: delete default royalty. - `postmintAllUnsold()`: mint all unsold `NFT` to a specified address. - `postmintAndReduceSupply()`: mint a specified quantity of NFTs, ensuring that the quantity does not exceed the unsold amount. Additionally, set the value of the max supply of `NFT` to the minted number. - `premint()`: mint `NFT` to specified addresses before the sale is started. - `resetTokenRoyalty()`: resets royalty information for the token id back to the global default. - `setDefaultRoyalty()`: sets the royalty information that all ids in this contract will default to. - `setTokenRoyalty()`: sets the royalty information for a specific token id, overriding the global default. - `reduceSupplyTo()`: update the max supply. --- In contract `INORestricted`, the role `DEFAULT_ADMIN_ROLE` has authority over the following functions: - `deployNftToSell()`: deploy `NFT` to sell. - `updateSetPhase()`: update or add a phase. - `updatePhaseMaxMintAndMerkleRoot()`: update `phaseMaxMint` value and `merkleRoot` value. - `updateSetPhases()`: update or add phases. --- In the updated version [78a0bec6117b0dbdc7c15c5c8d779df5dc4ac05e](https://github.com/Seedifyfund/igo-ino-all-in-one/tree/78a0bec6117b0dbdc7c15c5c8d779df5dc4ac05e), the privileged functions are changed as below: In contract `RestrictedWritable`, the changes of the role `DEFAULT_ADMIN_ROLE` are listed below: - `closePhases()`: newly added, close phases. - `closeSale()`: newly added, close the sale. - `updateGrandTotal()` function is removed. Any compromising on functions above, allows the attacker to take advantage of the privileged functionalities, for example, manipulate the NFT setting, mint arbitrary NFTs or control the NFT sale.
In contract `ERC721Base`, the role `_owner` has the authority to mint NFT tokens to an arbitrary address out of the buy events. Any compromise to the `_owner` account may allow a hacker to take advantage of this authority and mint tokens to an address they control.
The `defaultINO` serves as a template contract for creating new contracts. However, if the initialize function of the `defaultINO` is not invoked within the same transaction as the deployment, the `initialize` function for `defaultINO` will remain publicly accessible. This vulnerability poses a risk because a malicious user could potentially call the publicly accessible `initialize` function on the `defaultINO`. By doing so, the malicious user could manipulate the initialization parameters and gain unauthorized control over the `defaultINO` contract. A similar concern was also raised for `nftToClone`.
The function `_setPhase()` is intended to link a designated phase with a specific phase ID. There are two scenarios for utilizing this function: 1.When the phase ID lacks an associated phase. 2.When the phase ID has a previously associated phase. In the second scenario, a new phase will replace the existing one associated with the phase ID, and certain parameters will be verified for the new phase. Conversely, there are no checks performed for the old phase. i.e the function `updatePhaseStartDate()` requires that the referenced phase is not started when updating `startAt`. Does `_setPhase()` need the same check as the new phase's `startAt` may be also changed. ```solidity function updatePhaseStartDate( string calldata phaseId, uint128 startAt ) external override onlyRole(DEFAULT_ADMIN_ROLE) { _isPhase(Status.NOT_STARTED, phaseId); if (startAt < block.timestamp) revert RestrictedWritable_StartInPast(); emit PhaseStartDateUpdated( phaseId, SaleStorage.layout().phases.data[phaseId].startAt, startAt ); SaleStorage.layout().phases.data[phaseId].startAt = startAt; } ``` Besides, there are several duplicate checks in the functions `_checkPhaseData()_isEmptyPhase()` and `_isEmptyPhase()`.
Based on the latest commit 896e5b115e9edf012cfbc92575390306e23d72b0. The functions `setBaseURI()` and `setProvenanceHash()` has been removed from `ERC721Base` contract. And the functionalities can be used via `ERC721ContractMetadataCloneable`. However, the relevant sale status check was removed. We would like to clarify with the client whether it is the intended behavior.
In solidity versions 0.8.13 and 0.8.14, there is an [optimizer bug](https://github.com/ethereum/solidity-blog/blob/499ab8abc19391be7b7b34f88953a067029a5b45/_posts/2022-06-15-inline-assembly-memory-side-effects-bug.md) where, if the use of a variable is in a separate `assembly` block from the block in which it was stored, the `mstore` operation is optimized out, leading to uninitialized memory. The code currently does not have such a pattern of execution, but it does use `mstore`s in `assembly` blocks, so it is a risk for future changes.
Based on the context code, the deployer account or other accounts that obtain `DEFAULT_ADMIN_ROLE` role can create an `INO` through the `createINO()` function of the `INOFactory` contract and complete the initialization work. `DEFAULT_ADMIN_ROLE` role account will become the privileged account of `INO` contract, able to create `NFT` contracts, set related parameters, and influence NFT-related activities. However, according to the `_setOwnerRights()` function, in addition to the `DEFAULT_ADMIN_ROLE` account obtaining privileges, the factory contract can also obtain the same privileges. However, there are no related methods in the `INOFactory` contract to interact with `INO` contract. Therefore, authorizing the factory contract is meaningless. ```solidity function _setOwnerRights(address owner) internal { if (owner == address(0)) { revert RestrictedWritable_Init_OwnerIsZeroAddr(); } _grantRole(DEFAULT_ADMIN_ROLE, owner); _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); } ```
In the given code snippet, before reaching the maximum allocation limit, each purchase in the INO requires that the payment amount must be a multiple of the unit price, whether the payment is made using an ERC20 token or the native token. ```solidity if (bought + buyNow != maxAllocation) { if (buyNow % unitPrice != 0) { revert INO_OnlyUseMultipleOf(unitPrice); } } ``` However, this implementation does not consider the case where the maximum allocation (`maxAllocation`) is not divisible by the unit price. In such a scenario, when attempting to purchase the remaining tokens up to the maximum allocation, the required payment amount may not be a multiple of the unit price, leading to a potential "dust payment" issue.
The NFT-related information such as uri and max cap can be updated by the collection solely after the NFT contract is deployed with the following methods: - `setBaseURI` - `reduceSupplyTo` - `postmintAndReduceSupply` However, this may bring the inconsistency between INOStorage.NFTCollectionData and the NFT/collection contract, we would like to check with the team if the corresponding update logic missing or if it is the intended design.
The contract is serving as the underlying entity to interact with one or more third party protocols **Permit2**. The scope of the audit treats third party entities as black boxes and assumes their functional correctness. However, in the real world, third parties can be compromised and this may lead to lost or stolen assets. In addition, upgrades of third parties can possibly create severe impacts, such as increasing fees of third parties, migrating to new LP pools, etc.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (31)
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