CertiK reviewed Decubate (Decubate Audit - Addendum) on 10 Apr 2024: 25 findings, 0 critical; 12 marked fixed, 13 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 | 2 | 0 | 4 |
| Low | 9 | 0 | 8 |
In the contract `DCBCrowdfunding`, - the role `investment` has authority over the following functions: - `setParams`: Sets the state variables. - `setToken`: Sets the sale token address. - the role `layerZero` and `tierMigratorAddr` have authority over the following function: - `registerForAllocation`: Registers user for allocation. - the role `manager` has authority over the following function: - `registerByManager`: Registers users in bulk. In the contract `DCBPlatformVesting`, - the role `factory` has authority over the following functions: - `setCrowdFundingParams`: Sets the state variables for the crowdfunding contract. - `setVestingParams`: Sets the state variables for the vesting contract. - `setToken`: Sets the sale token. - `rescueTokens`: Transfers the vested tokens to the `_receiver` account. - the role `owner` has authority over the following function: - `transferOwnership`: Transfers the ownership to `newOwner`. - `setTokenClaimWhitelist`: Sets the whitelist for token claim contract. - `setCrowdfundingWhitelist`: Sets the whitelist for the crowdfunding contract. - the role `innovator` has authority over the following function: - `claimRaisedFunds`: Claims the raised funds from this contract. In the contract `DCBPowerBoost`, - the role `owner` has authority over the following functions: - `setWhitelist`: Sets the status of `_address` within the whitelist. - the role `whitelisted` has authority over the following functions: - `deposit`: Increases the user's deposit balance. - `withdraw`: Decreases the user's deposit balance. In the contract `DCBTierMigrator`, - the role `manager` has authority over the following functions: - `addNewEvent`: Adds an new event into the `events` list. - `setEvent`: Updates the element at index `_eventId` in the `events` array. - `setHasRegistered`: Sets the status of `hasRegistered` in bulk. - `setContracts`: Sets the `endpoint`, `tiers`, `walletStore` contract addresses. - `setNativeChainId`: Sets the native chain id. - `setMinShareForUser`: Sets the minimum share. - the role `DEFAULT_ADMIN_ROLE` has authority over the following function: - `setManagerRole`: Grants/Revokes the manager role. In the contract `DCBTiers`, the role `owner` has authority over the following functions: - `addTier`: Adds new tiers. - `setTier`: Updates a given tier. - `setBoostValues`: Sets the `boostValues` mapping value. - `setLegacyContracts`: Sets the external dependency contracts. - `setNewContracts`: Sets the `multiAssetStakingContract`, `compounderContract`, and `liquidityLocker` contract addresses. In the contract `DCBTokenClaim`, - the role `_investment` has authority over the following functions: - `setParams`: Sets the parameters for the claim. - `setToken`: Sets the token to be used for claim. - the role `layerZero` and `tierMigratorAddr` have authority over the following function: - `registerForAllocation`: Registers user for allocation. - the role `manager` has authority over the following function: - `registerByManager`: Registers users in bulk. - `setMinTierForClaim`: Sets the minimum tier required for claim. (This function has been removed from the codebase in commit [5335bf0956a2df0d626e54f59b8db0d5ddb49e25](https://github.com/Decubate-com/platform-contracts/commit/5335bf0956a2df0d626e54f59b8db0d5ddb49e25)). In the contract `Tiers`, - the role `owner` has authority over the following functions: - `addTier`: Adds new tiers. - `setTier`: Updates a given tier. - `setBoostValues`: Sets the `boostValues` mapping value. - `setContracts`: Set the external dependency contracts. - the role `DEFAULT_ADMIN_ROLE` has authority over the following function: - `setManagerRole`: Grants/Revokes the manager role. (This function has been removed from the codebase in commit [5335bf0956a2df0d626e54f59b8db0d5ddb49e25](https://github.com/Decubate-com/platform-contracts/commit/5335bf0956a2df0d626e54f59b8db0d5ddb49e25)). Any compromise of the privileged accounts may allow a hacker to take advantage of this authority to modify sensitive state variables, transfer funds, and ultimately damage the entire project, causing significant financial loss.
The listed contracts inherit upgradeable contracts, indicating that they are part of an upgradeable system. Upgradeable contracts often pair with a proxy contract that is responsible for managing contract upgrades. The `privileged roles` of the proxy often have the authority to update the implementation contract. Any compromise of the privileged account could allow a hacker to exploit this authority, potentially altering the implementation contract pointed to by the proxy and thus executing malicious functionality within the implementation contract.
The `setParams` and `setToken` functions are exclusively controlled by the `investment` role, which is responsible for updating crucial state variables like `saleToken`, `dcbAgreement.totalTokenOnSale`, `dcbAgreement.hardcap`, `cbAgreement.startDate`, and `dcbAgreement.minTier`. These variables are pivotal to the project's integrity. Unauthorized modifications could severely impact certain functionalities, potentially eroding user trust. For instance, within the `fundAgreement` function, users can deposit tokens to receive rewards based on the duration of their deposit. However, any changes to `saleToken` or `dcbAgreement.totalTokenOnSale` that precede a user's transaction could lead to the failure of the following `require` statement due to an insufficient balance of tokens: ```solidity=265 require( saleToken.balanceOf(address(vesting)) >= dcbAgreement.totalTokenOnSale, "Tokens not received from project" ); ``` Moreover, alterations to `dcbAgreement.totalTokenOnSale` and `dcbAgreement.hardcap` may disrupt the correlation between the deposited `value` and the calculated `numTokens`, which should ideally increase with the `value`. Such discrepancies can trigger a failure in `vesting.setCrowdfundingWhitelist(msg.sender, numTokens, value);`, as shown below, because the expected increase in `numTokens` and `value` may not occur after the modifications: ```solidity=328 uint256 numTokens = (value * dcbAgreement.totalTokenOnSale) / (dcbAgreement.hardcap); ``` If `numTokens` is less than the value before `dcbAgreement.totalTokenOnSale` and `dcbAgreement.hardcap` modification, then `vesting.setCrowdfundingWhitelist(msg.sender, numTokens, value);` will fail due to the following code snippet: ```solidity=321 function _setWhitelist(address _wallet, uint256 _amount, uint256 _value) internal { ... else { WhitelistInfo storage w = vestingPool.whitelistPool[whitelist.arrIdx]; totalVestedValue += _value - w.value; totalVestedToken += _amount - w.amount; ... ``` Ensuring the integrity and reliability of these variables is crucial for maintaining the project's functionality and user trust.
In the `DCBPlatformVesting` contract, the `_doBuybackAndBurn` function uses the `swapExactTokensForTokens` method to exchange tokens based on the specified `amount`. However, the provided code snippet cannot effectively prevent price slippage, particularly in the context of a sandwich attack. In a sandwich attack, an attacker observes a pending transaction that will affect the price of a token pair on a DEX like Uniswap. The attacker then places one or more transactions before and after the observed transaction, with the aim of manipulating the price to their advantage, thereby causing significant slippage for the victim's transaction. ```solidity=357 uint256[] memory amountsOut = _router.getAmountsOut(amount, path); uint256 amountOut = (amountsOut[amountsOut.length - 1] * 99) / 100; //1% slippage ``` The code snippet calculates `amountOut` based on the current state of the pool (`_router.getAmountsOut(amount, path)`), and then sets a minimum acceptable amount out as 99% of this calculated value. However, if an attacker performs a transaction right before this transaction that significantly alters the pool's state (and hence the price), this transaction's `amountOut` calculation becomes outdated and ineffective in protecting against the new price impact. This function will not revert as `amountsOut[amountsOut.length - 1]` consistently reflects the actual amount received from the swap in this transaction, which is invariably higher than `amountOut`.
In the `DCBTierMigrator` contract, the `registerForEvent` function allows users with a valid signature to register their allocation in two ways, without verifying the `hasRegistered[_eventId][msg.sender]` status. This enables users to register multiple times by repeatedly calling this function.
In the `Tiers` contract, the linked function `getTotalDeposit` calculates user information like deposits, multipliers, and tiers, depending heavily on third-party contracts, including: - `legacyStakingContract`, `legacyMultiAssetStakingContract`, `legacyCompounderContract`, `legacyLiquidityLocker`, `powerBoostContract`, `multiAssetStakingContract`, `compounderContract`, `liquidityLocker`, `_contract`, `compoundStakingContract`, `legacyMasterChef`, `_vault`. If the third-party contracts are compromised, it could affect the calculations of user data, potentially resulting in financial losses for the project.
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 and the transaction may fail due to the validation checks. Reference: https://thoreum-finance.medium.com/what-exploit-happened-today-for-gocerberus-and-garuda-also-for-lokum-ybear-piggy-caramelswap-3943ee23a39f ```solidity=317 paymentToken.safeTransferFrom(_wallet, address(this), paymentAmount); ``` - Transferring tokens by `paymentAmount`. ```solidity=318 _setWhitelist(_wallet, _amount, _value); ``` - The `_value` appears to be used for bookkeeping purposes without compensating the potential transfer fees.
In the `DCBTierMigrator` contract, the `addNewEvent` function lacks validation for its input parameters, raising concerns about data integrity: ```solidity function addNewEvent(Event memory _event) public onlyManager { events.push(_event); eventToId[_event.dstAddress] = events.length - 1; emit EventAdded(_event); } ``` Without these validations could lead to the addition of duplicate `_event` entries. If `eventToId[_event.dstAddress]` is overwritten, it could disrupt the accuracy of the event tracking, potentially affecting the entire project's functionality.
Within the project's framework, the `DCBCrowdfunding.registerForAllocation` function includes a return value. However, when invoked, as shown below, its return value is not checked: ```solidity IEvent(event_.dstAddress).registerForAllocation(msg.sender, uint8(_tier), uint8(_multi), _shares); ``` ```solidity function lzReceive(uint16 _id, bytes calldata _srcAddress, uint64, bytes memory data) public override { ... registerForAllocation(user, tier, multi, shares); } ``` This could lead to unexpected outcomes. For example, if a `false` return is not anticipated, the function should trigger an error to revert the operation.
The listed variables lack checks for valid value ranges; for instance, certain values must not be zero, as zero values could adversely affect other functionalities. In `DCBCrowdfunding` contract: ```solidity=61 dcbAgreement.totalTokenOnSale = p.totalTokenOnSale; dcbAgreement.hardcap = p.hardcap; ``` ```solidity=64 dcbAgreement.startDate = p.startDate; ``` ```solidity=88 dcbAgreement.totalTokenOnSale = p.totalTokenOnSale; dcbAgreement.hardcap = p.hardcap; dcbAgreement.startDate = p.startDate; ``` In `DCBCrowdfunding` contract of commit 8c2e093526af68173f27d8333af864c8bbd51d87: ```solidity=57 durations = p.durations; ``` ```solidity=89 durations = p.durations; uint32 totalDuration = p.durations[0] + p.durations[1] + p.durations[2]; ``` In `DCBPlatformVesting` contract: ```solidity=88 gracePeriod = c._gracePeriod; totalTokenOnSale = c._totalTokenOnSale; ``` ```solidity=113 gracePeriod = c._gracePeriod; totalTokenOnSale = c._totalTokenOnSale; ``` ```solidity=151 vestingPool.start = _start; vestingPool.duration = _duration; ``` In `DCBTiers` contract: ```solidity=88 tierInfo[tierId].minLimit = _minLimit; tierInfo[tierId].maxLimit = _maxLimit; ``` ```solidity=105 boostValues[_contract[i]][_poolId[i]] = _value[i]; ``` In `DCBTokenClaim` contract: ```solidity=62 claimInfo.startDate = p.startDate; claimInfo.endDate = p.endDate; ``` ```solidity=68 tierInfo.push(Tiers({ minLimit: p.tiers[i].minLimit, multi: p.tiers[i].multi })); ``` ```solidity=82 claimInfo.minTier = p.minTier; claimInfo.distAmount = p.distAmount; claimInfo.startDate = p.startDate; claimInfo.endDate = p.endDate; ``` ```solidity=91 tierInfo[i] = Tiers({ minLimit: p.tiers[i].minLimit, multi: p.tiers[i].multi }); ``` In `Tiers` contract: ```solidity=74 tierInfo.push(Tier({ minLimit: _minLimit, maxLimit: _maxLimit, refundFee: _refundFee })); ``` ```solidity=96 tierInfo[tierId].minLimit = _minLimit; tierInfo[tierId].maxLimit = _maxLimit; ``` In `DCBTierMigrator` contract of commit `5335bf0956a2df0d626e54f59b8db0d5ddb49e25`: ```solidity106 minShareForUser = _minShareForUser; ```
The `DCBTierMigrator` contract uses `ECDSAUpgradeable` from the OpenZeppelin library to identify the signer from provided signatures. Since the signatures (`_sig`) are directly submitted by the function callers, there's no guarantee they include the current chain ID. If the `_sig` and `eventHash` do not include the chain ID, this potentially exposes the system to cross-chain replay attacks, allowing the same signature to be used multiple times across different chains. ```solidity=49 address signer = ECDSAUpgradeable.recover(events[_eventId].eventHash, _sig); ```
When a loop allows an arbitrary number of iterations or accesses state variables in its body, the function may run out of gas and revert the transaction. ```solidity=222 for (uint256 i = 0; i < len; ++i) { ``` Function `DCBCrowdfunding.registerByManager` contains a loop and its loop condition depends on parameters: `_users`. --- ```solidity=269 for (uint256 i = 0; i < len; i++) { ``` Function `DCBTiers.getTotalDepositMultiAsset` contains a loop and its loop condition depends on parameters: `_contract`. --- ```solidity=299 for (uint256 i = 0; i < len; i++) { ``` Function `DCBTiers.getTotalDepositLiquidityLocker` contains a loop and its loop condition depends on parameters: `_contract`. --- ```solidity=333 for (uint256 i = 0; i < len; i++) { ``` Function `DCBTiers.getTotalDepositVault` contains a loop and its loop condition depends on parameters: `_contract`. --- ```solidity=362 for (uint256 i = 0; i < len; i++) { ``` Function `DCBTiers.getTotalDepositLegacy` contains a loop and its loop condition depends on state variables: `legacyStakingContract`. --- ```solidity=67 for (uint256 i = 0; i < p.tiers.length; i++) { ``` Function `DCBTokenClaim.initialize` contains a loop and its loop condition depends on parameters: `p`. --- ```solidity=149 for (uint256 i = 0; i < len; i++) { ``` Function `DCBTokenClaim.registerByManager` contains a loop and its loop condition depends on parameters: `_users`. --- ```solidity=299 for (uint256 i = len - 1; i >= 0; i--) { ``` Function `DCBTokenClaim.getTier` contains a loop and its loop condition depends on state variables: `tierInfo`. --- ```solidity=267 for (uint256 i = 0; i < len; i++) { ``` Function `Tiers.getTotalDeposit` contains a loop and its loop condition depends on external calls: `vestingContract.getAllVestingPools`. --- ```solidity=70 while (secondsAccountedFor > timestamp) { ``` Function `DCBPlatformVesting.getYear` contains a loop and its loop condition depends on parameters: `timestamp`. --- ```solidity=142 for (i = ORIGIN_YEAR; i < year; i++) { ``` Function `DCBPlatformVesting.toTimestamp` contains a loop and its loop condition depends on parameters: `year`. --- ```solidity=169 for (i = 1; i < month; i++) { ``` Function `DCBPlatformVesting.toTimestamp` contains a loop and its loop condition depends on parameters: `month`.
These smart contracts do not verify or record the outcomes of the following calls, potentially introducing vulnerabilities from these unhandled results. ```solidity=332 investment.setUserInvestment(msg.sender, address(this), value); ``` --- ```solidity=96 paymentToken.approve(router, type(uint256).max); ``` --- ```solidity=118 paymentToken.approve(router, type(uint256).max); ``` --- ```solidity=198 (uint256 tier, uint256 multi,,,) = ICrowdFunding(address(owner())).userAllocation(msg.sender); ``` --- ```solidity=359 _router.swapExactTokensForTokens(amount, amountOut, path, address(0xdead), block.timestamp); ``` --- ```solidity=39 _addresses.add(_user); ``` --- ```solidity=50 _addresses.remove(_user); ``` --- ```solidity=135 (fee,) = endpoint.estimateFees(event_.dstChainId, event_.dstAddress, data, false, ""); ``` --- ```solidity=148 (uint256 fee,) = endpoint.estimateFees(events[eventId].dstChainId, events[eventId].dstAddress, data, false, ""); ``` --- ```solidity=153 (, uint256 _tier, uint256 _multi, uint256 _shares) = tiers.getTierAndDepositOfUser(user); ``` --- ```solidity=155 (uint256 fee,) = endpoint.estimateFees(events[eventId].dstChainId, events[eventId].dstAddress, data, false, ""); ``` --- ```solidity=270 (,,,,, inputToken,,,,,) = _stakingContract.poolInfo(i); ``` --- ```solidity=273 (tempAmt,,,,) = _stakingContract.users(i, addr); ``` --- ```solidity=300 (,,,,,,,, _pair,) = _liquidityLocker.pools(i); ``` --- ```solidity=302 (uint256 lpTokens,,,,) = _liquidityLocker.users(i, addr); ``` --- ```solidity=336 (,,,,,,,,, token) = legacyMasterChef.poolInfo(i); ``` --- ```solidity=339 (,,,,,, token) = masterChef.poolInfo(i); ``` --- ```solidity=343 (,, tempAmt,) = _vault.users(i, addr); ``` --- ```solidity=363 (tempAmt,,,,) = legacyStakingContract.users(i, addr); ``` --- ```solidity=241 _investment.setUserInvestment(msg.sender, address(this), amount); ``` --- ```solidity=67 verifiedUsers.remove(oldAddress); ``` --- ```solidity=68 verifiedUsers.add(newAddress); ``` --- ```solidity=82 (, uint256 tier,) = tiers.getTierOfUser(_referree); ``` --- ```solidity=101 (, uint256 tier,) = tiers.getTierOfUser(msg.sender); ``` --- ```solidity=285 (,,,,,,,, address token,) = multiFarmContract.poolInfo(i); ``` --- ```solidity=287 (tempAmt,,,,) = multiFarmContract.users(i, addr); ``` --- ```solidity=300 (,,,,,, address token) = compoundStakingContract.poolInfo(i); ``` --- ```solidity=303 (,, tempAmt,) = compounderContract.users(i, addr); ``` --- ```solidity=318 (,,,,, inputToken,,,,,) = multiAssetStakingContract.poolInfo(i); ``` --- ```solidity=321 (tempAmt,,,,) = multiAssetStakingContract.users(i, addr); ``` --- ```solidity=336 (,,,,,,,, _pair,) = liquidityLocker.pools(i); ``` --- ```solidity=338 (uint256 lpTokens,,,,) = liquidityLocker.users(i, addr); ``` --- ```solidity=361 (uint256 reserve0, uint256 reserve1,) = _pair.getReserves(); ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (7)
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