CertiK reviewed FNCY (FNCY Chain) on 22 Mar 2024: 43 findings, 0 critical; 30 marked fixed, 6 acknowledged, 7 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 | 0 | 3 | 2 |
| Medium | 0 | 3 | 4 |
| Low | 7 | 0 | 24 |
When transferring standard ERC20 deflationary tokens, the input amount may not be equal to the received amount due to the charged transaction fee. As a result, an inconsistency in the amount will occur. For example, if a user locks 100 deflationary tokens (with a 10% transaction fee) to the bridge contract, only 90 tokens actually arrive to the bridge. ```solidity if (paymentAmount > 0) { if (paymentToken != address(0)) { ... TransferHelper.safeTransferFrom(paymentToken, msg.sender, launchpadFeeInterceptor, paymentAmount); } else if (paymentToken == address(0)) { ... } ILaunchpadFeeInterceptor(launchpadFeeInterceptor).executeFundsTransfer(projectId, paymentAmount, paymentToken); } ```
For the functions `addProject()` and `updateProject()`, there is a lack of sufficient checks around the input parameters. - For address variables like `settlement` and `paymentTokens`, there are no checks that the addresses are zero addresses. - For timestamp variables like `startTimestamp` and `endTimestamp` there are no checks in relation to the current time `block.timestamp`, therefore they could be set to times in the past. - For numeric values like `price` or `total` it might be helpful to include checks on the lower and/or upper values. - Specifically for the `updateProject()` function, there are no checks on the updated values in comparison to their original values. Changes to values like `total` and `endTimestamp` during the course of the project could lead to unexpected behavior. ```solidity function updateProject( uint256 projectId, INFT nft, uint8 limitPerWallet, uint112 total, uint256 price, uint256 startTimestamp, uint256 endTimestamp, address payable settlement, address[] memory paymentTokens ) public onlyAdmin { Project storage project = projects[projectId]; project.nft = nft; project.limitPerWallet = limitPerWallet; project.total = total; project.price = price; project.startTimestamp = startTimestamp; project.endTimestamp = endTimestamp; project.settlement = settlement; ... ```
There is a potential issue that occurs if a user provides an array `nftIds` with duplicate `nftId` values to a function (such as the `bulkMint()` function) that uses the `_checkHasMinted(address collection, uint256[] memory nftIds)` method, which could bypass the "Already Minted" check for duplicates since all checks are completed before any NFTs are minted in bulk. It is also uncertain the relationship between `nftId` (which is a server generated value) and the parameter input `tokenId` which is used to identify the minted tokens. In the current implementation there could hypothetically be the case where duplicate NFTs that are processed in bulk and have passed the `_checkHasMinted` verification are assigned different tokenIds. This would mean that they will be identified as unique NFTs in the contract even though they were initially duplicates.
Within function `_transferFee()`, transfers to `fee.recipients` in ETH could potentially fail especially if there are any recipients that are smart contract that do not contain an appropriate fallback function. ```solidity=122 require(_transferTo(fee.recipient, payout), "Transfer Fail"); ``` This failure would cause all the transfers to the set fee recipients to fail. If one of the `fee.recipient` addresses is controlled by a malicious actor, they can craft their smart contract's `fallback` function in a way that will always cause it to revert. This would effectively cause the `_transferFee()` function to revert every time, leading to a Denial of Service (DoS) for the functionality of transferring fees.
Within function `_bulkMint,` NFT transfers to `recipients` could potentially fail, especially if any recipients are contracts that do not contain an appropriate `onERC721Received` function. ```solidity=122 if (CallType.MINT == callType) { for (uint256 i = 0; i < _size; i++) { (uint256 rTokenId) = _callMint(collectionConfig.collection, recipients[i], tokenIds[i], uris[i]); rTokenIds[i]=rTokenId; } } else if (CallType.SAFE_TRANSFER_FROM == callType) { for (uint256 i = 0; i < _size; i++) { _callSafeTransferFrom(collectionConfig.collection, collectionConfig.merchant, recipients[i], tokenIds[i]); // Error handling exists in ERC721 } } ``` This failure would cause all the transfers to the recipients to fail. If one of the `recipients` addresses is controlled by a malicious actor, they can craft their smart contract's `onERC721Received` function in a way that will always cause it to revert. This would effectively cause the `_bulkMint` function to revert every time, leading to a Denial of Service (DoS) for the functionality of distributing NFTs.
In function `_transferFee()` the `payout` could potentially be zero due to the division truncation of integers, if the value of `_price.mul(fee.feeRate)` is less than the `FEE_BASIS_POINT`. ```solidity=124 uint256 payout = _price.mul(fee.feeRate).div(FEE_BASIS_POINT); ``` Especially, if the token has a small number of decimals, like 6, then the _price value will tend to be smaller, and it will exacerbate this truncation issue.
The contract is serving as the underlying entity to interact with one or more third-party protocols. 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. ```solidity=38 mapping(address => address) public interceptors; ``` - The contract `NFTExchange` interacts with third-party contracts with `IExchangeFeeInterceptor` interface via `interceptors`. --- ```solidity=27 address public launchpadFeeInterceptor; ``` - The contract `NFTLaunchpad` interacts with third-party contracts with `ILaunchpadFeeInterceptor` interface via `launchpadFeeInterceptor`. --- ```solidity interface INFT { function safeMint(address to, uint256 tokenId, string memory uri) external returns(uint256); } ``` ```solidity struct Project { INFT nft; uint8 limitPerWallet; uint112 total; uint256 price; uint256 startTimestamp; uint256 endTimestamp; uint256 soldCount; address payable settlement; bool fundraising; } ``` - The `NFTLaunchpad` contract interacts with third-party contracts with `INFT` interface via `Project.nft` --- ```solidity function buyFromAnotherChain(address user, uint256 projectId, uint8 count, string memory receipt, uint256 chainId) public onlySpecial onlyAvailable(projectId) ``` - The `NFTLaunchpad` contract may interact with the off-chain bridge server to perform the purchase from another chain. --- ```solidity interface IBaseNFT { function safeMint(address to, uint256 tokenId, string memory uri) external returns(uint256); } ``` ```solidity struct CollectionConfig { address collection; address merchant; bool enabled; CallType callType; } ``` - The `TransferManagement` contract interacts with third-party contracts with `IBaseNFT` interface via `CollectionConfig.collection`. --- ```solidity function sell(address _nftAddress, uint256 _tokenId, uint256 _price, uint256 _deadline) public nonReentrant whenNotPaused ``` - The `NFTExchange` contract interacts with third-party contracts with `IERC721Upgradeable` interface via `_nftAddress`.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (6)
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