CertiK reviewed Ordify on 22 Mar 2024: 29 findings, 0 critical; 16 marked fixed, 2 acknowledged, 11 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 | 2 | 0 |
| Medium | 1 | 0 | 2 |
| Low | 10 | 0 | 14 |
When transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived to the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction. As a result, the following data will not be accurate: ```solidity=438 totalOwed += tokenAmount; require(tokenTarget >= totalOwed, "soldout"); stableRaised += stableAmount; require(stableTarget >= stableRaised, "soldout"); ``` The issue exist within the `Launchpad.sol`, `Staking.sol` and `Vesting.sol` contracts. If deflationary tokens are used within those contracts, it could lead to a possible loss for the project and the users.
The Launchpad contract owner is allowed to finish the sale via `finishSale` function, however, there is no limitation on the invocation time.
`idoTokenToStableRation` is considered as the convention rate between the sale token and payment asset. The following concerns are raised with the rate: 1. The decimals of stablecoins can be different from the normal ERC20 tokens. For example, the USDC on ETH is 6 decimals. And in the tests stable coins are considered as 18 decimals, which may raise issues during the actual deployment. 2. There is a concern about truncation when `stableTarget` is larger than `tokenTarget * BASIS_POINT_RATE_CONVERTER`, which may cause the token to receive to be 0 while `_buy` and `getUserContribution` functions.
In the function `setName()`, the given name can be duplicated across the staking definitions.
In the vesting contract, the contract owner will create vesting for user, however, the available balance validation can be bypassed when `depositCheck` is false, we would like to check with the team if any documentation related to the token deposit for vestings while non `depositCheck`. ```solidity=160 if (depositCheck) { uint256 totalTokenAvailable = totalDepositedAmount + totalAmountClaimed - totalAmountAllocated; require(totalTokenAvailable >= amountAllocated, "not enough token deposited"); } ``` Similarly, in the `Staking` contract, the user can only claim the reward and withdraw staking with `withdraw` function. However, if not enough rewards exist, the staking will be blocked: ```solidity=431 require(rewardBalance >= reward, "rewardBalance is too low"); ```
In the Staking contract, `WithdrawFee` and `GlobalWithdrawFee` play significant roles in the context of staking definitions and user withdrawals. However, the nuances between them and their specific use cases are not entirely clear from the contract's documentation or code comments.
The Launchpad contract allows the user to purchase the target token with the stable token. And based on the comments, the target token distribution should be finished by the vesting contract, however, there is no specific invocation on `getUserContribution` in the `Vesting` contract.
The vesting contract features two functions, `requestRefund` and `pullBackRequestRefund`, which allow users to initiate or revoke refund requests. However, to adequately address the following issues, more information is needed: 1. Users might not have motivations to refund through `requestRefund` if no following actions to refund assets. 2. In scenarios where additional processes are involved, such as off-chain token refunds to users, individuals could exploit the system by using `requestRefund`, get refund token, and then `pullBackRequestRefund` to continue the vesting process. 3. After executing `pullBackRequestRefund`, users are able to keep the vested tokens from the period between the initial refund and the pull back without any penalty.
Functions that update state variables should emit relevant events as notifications.
The function `_buy` will calculate the amount the user may receive during the sale round1 or round2: ```solidity=436 uint256 tokenAmount = (stableAmount * idoTokenToStableRation) / BASIS_POINT_RATE_CONVERTER; ``` The function `getUserContribution` will calculate the amount the user may receive among both round1 and round2: ```solidity=309 contributedStable = userDetailsRecord.contributedRound1 + userDetailsRecord.contributedRound2; tokensToReceive = (contributedStable * idoTokenToStableRation) / BASIS_POINT_RATE_CONVERTER; ``` However, due to potential truncation during the calculations, the values from `_buy` (across round1 and round2) may not equal the return value of `getUserContribution` on `tokensToReceive`. As a result, more tokens may needed during the token distribution.
The function `_checkWhitelistSignature` will used in `buyRound1` and `buyRound2` to verify the amount of token that the user can purchase: ```solidity=472 function _checkWhitelistSignature(bytes calldata signature, address user, uint256 whitelistedAmount) private view { bytes32 signedMessageHash = keccak256(abi.encode(address(this), user, whitelistedAmount)).toEthSignedMessageHash(); require( signedMessageHash.recover(signature) == verifyingAddressECDSA, "signature not valid" ); } ``` However, the given signature only contains the contract address, msg sender, and whitelisted amount. As a result, the can be reused in both rounds. And according to the allowance calculation, the user max purchase can be `whitelistedAmount + whitelistedAmount * round2Multiplier`.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (5)
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