CertiK reviewed Umbrella Network (Umbrella 2) on 21 Mar 2022: 8 findings, 0 critical; none marked fixed.
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 | 3 | 0 | 0 |
| Medium | 0 | 0 | 0 |
| Low | 5 | 0 | 0 |
In the contract `Blacklisted` the role `owner` has authority over the following functions: - `setupBlacklist()` Any compromise to the `owner` account may allow a hacker to take advantage of this authority. In the contract `LockSettings` the role `owner` has authority over the following functions: - `removePeriods()` - `setLockingTokenSettings()` Any compromise to the `owner` account may allow a hacker to take advantage of this authority. In the contract `OnDemandToken` the role `owner` has authority over the following functions: - `setupMinter()` - `setupMinters()` Any compromise to the `owner/minter` account may allow a hacker to take advantage of this authority. In the contract `OnDemandTokenBridgable` the role `owner` has authority over the following functions: - `setupBridge()` Any compromise to the `owner` account may allow a hacker to take advantage of this authority. In the contract `SwappableTokenV2` the role `owner` has authority over the following functions: - `startEarlySwap()` - `setDailyCup()` Any compromise to the `owner` account may allow a hacker to take advantage of this authority. In the contract `rUMB2/rUMB2BSC` the role `owner/minter` has authority over the following functions: - `mint()` Any compromise to the `owner/minter` account may allow a hacker to take advantage of this authority. In the contract `StakingLockable` the role `owner` has authority over the following functions: - `finishFarming()` Any compromise to the `owner` account may allow a hacker to take advantage of this authority.
According to the following codes, the user's balance drops divided by the constant variable `ONE(1e18)` and compared with the daily limit. ```solidity (uint256 limit, bool freshLimit) = _currentLimit(data); require(limit != 0, "swapping period not started OR limit"); uint256 amountToSwap = balanceOf(msg.sender); require(amountToSwap != 0, "you dont have tokens to swap"); uint32 amountWoDecimals = uint32(amountToSwap / ONE); require(amountWoDecimals <= limit, "daily CUP limit"); swapData.usedLimit = uint32(freshLimit ? amountWoDecimals : data.usedLimit + amountWoDecimals); swapData.swappedSoFar += amountWoDecimals; ``` In solidity, the division truncation problem exists for division operations. Hence, when the user's balance is below 1e18, like 0.9e18. The `usedLimit` and `swappedSoFar` will record incorrectly. As a result, the user can swap 0.9e18token for many times. The actual number of tokens swapped can easily exceed the daily limit.
According to the following codes, the function `_beforeTokenTransfer()` will check the `from` and `to` addresses and calls the function `_mint()` to mint tokens when the balance is not enough. ```solidity function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal virtual override { if (_from != address(0) && _to != address(0) && bridges[msg.sender]) { uint256 balance = balanceOf(msg.sender); if (balance < _amount) { _mint(msg.sender, _amount - balance); } } } ``` The `OnDemandTokenBridgable._beforeTokenTransfer()` will be called in the `rUMB2._beforeTokenTransfer()` and rUMB2 has a total supply limit. The function `_mint()` doesn't check if the total supply limit exceeds. As a result, the total supply may exceed the total supply limit. Any compromise to the `bridges` account may allow a hacker to take advantage of this authority to mint the tokens without limit.
`public` functions that are never called by the contract could be declared as `external`. `external` functions are more efficient than `public` functions.
According to the following codes, the function `swapForUMB()` is used to swap the user's all rUMB2 tokens to be UMB tokens. ```solidity function swapForUMB() external { SwapData memory data = swapData; (uint256 limit, bool freshLimit) = _currentLimit(data); require(limit != 0, "swapping period not started OR limit"); uint256 amountToSwap = balanceOf(msg.sender); require(amountToSwap != 0, "you dont have tokens to swap"); uint32 amountWoDecimals = uint32(amountToSwap / ONE); require(amountWoDecimals <= limit, "daily CUP limit"); swapData.usedLimit = uint32(freshLimit ? amountWoDecimals : data.usedLimit + amountWoDecimals); swapData.swappedSoFar += amountWoDecimals; if (freshLimit) swapData.dailyCupTimestamp = uint32(block.timestamp); _burn(msg.sender, amountToSwap); umb.swapMint(msg.sender, amountToSwap); emit LogSwap(msg.sender, amountToSwap); } ``` However, there exists a daily cap for the swap amount. As a result, the user can't swap when the balance is over the daily cap.
According to the following codes, users can choose to lock their tokens for a period to get additional rewards. This additional rewards is calculated through `balances[_user].lockedWithBonus`. ```solidity function _lockTokens(address _user, address _token, uint256 _amount, uint256 _period) internal notPaused { uint256 multiplier = multipliers[_token][_period]; require(multiplier != 0, "invalid period or not supported token"); uint256 stakeBonus = calculateBonus(_amount, multiplier); _stake(_token, _user, _amount, stakeBonus); _addLock(_user, _token, _amount, _period, multiplier); } ``` After the locked period, users can get their token back. However, users may choose not to unlock the token in time, at which point their staked tokens is no different from a common stake, but any enjoy additional rewards.
According to the following codes, the rewards are distributed to users through minting. ```solidity function _getReward(address _user, address _recipient) internal nonReentrant updateReward(_user) returns (uint256 reward) { reward = balances[_user].rewards; if (reward != 0) { balances[_user].rewards = 0; OnDemandToken(address(rUmb2)).mint(_recipient, reward); emit RewardPaid(_user, reward); } } ``` The `rUmb2` token has a max mint limit. As a result, the reward distribution may fail due to this limit.
SafeMath from OpenZeppelin is not used in the following functions which makes them possible for overflow/underflow and will lead to an inaccurate calculation result. - `SwappableTokenV2().swapForUMB()` - `StakingLockable.notifyRewardAmount()` - `StakingLockable.rewardPerToken()` - `StakingLockable.earned()` - `StakingLockable._stake()` - `StakingLockable._addLock()` - `StakingLockable._withdrawUnlockedTokens()`
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (8)
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