CertiK reviewed UBD Network on 14 Jul 2023: 9 findings, 0 critical; 5 marked fixed, 4 open.
The original report was removed from its address (seen 17 Oct 2026).
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 | 1 |
| Medium | 0 | 0 | 0 |
| Low | 1 | 0 | 4 |
```solidity=100 function setDistributionToken(address _token) external onlyOwner { distributionToken = IERC20Mint(_token); emit DistributionTokenSet(_token); } ``` The `UBDNLockerDistributor` contract allows the owner to change the distribution token at any point in time. This creates a couple of potential issues: **Changing Distribution Token to an Unwanted Token or Empty Address**: The owner of the contract can change the distribution token to a useless token or even an empty address (0x0). In such a case, users who previously purchased the distribution token would end up with worthless tokens or no tokens at all, resulting in the loss of their stablecoins. **Insufficient Balance for New Distribution Token**: The owner could change the distribution token during the distribution period to a new token that the contract does not have a sufficient balance of. And there are no checks in place to ensure that the contract has a sufficient balance of the new token. If the contract does not have a sufficient balance and a user tries to claim their tokens, the `claimTokens()` function will fail, potentially causing a loss of funds for the users.
The `buyTokensForExactStable()` function allows users to purchase distribution tokens by paying with a certain amount of stablecoins. However, because Ethereum transactions don't execute immediately and are added to a pool of pending transactions, other users (or bots) could potentially manipulate the outcome of the function call by watching the transaction pool and placing their own transaction with a higher gas price. The code uses the `_calcTokensForExactStable()` function to determine the amount of distribution tokens that will be minted and locked for the user based on the amount of stablecoins they're spending. This calculation is based on the current state of the contract at the time of execution. If another user executes the same function just before the original transaction gets mined, the `distributedAmount` would have increased and the calculated tokens to be received by the original user will decrease. This means the original user may receive fewer tokens than they originally anticipated.
A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. ### External call(s) ```solidity=59 IERC20Mint(_paymentToken).safeTransferFrom(msg.sender, owner(),_inAmount); ``` * This function call executes the following external call(s). * In `SafeERC20._callOptionalReturn`, * `returndata = address(token).functionCall(data,"SafeERC20: low-level call failed")` * In `Address.functionCallWithValue`, * `(success,returndata) = target.call{value: value}(data)` ### State variables written after the call(s) ```solidity=67 distributedAmount += outAmount; ``` ```solidity=66 _newLock(msg.sender, outAmount); ``` * This function call executes the following assignment(s). * In `UBDNLockerDistributor._newLock`, * `userLocks[_user].push(Lock(_lockAmount,block.timestamp + LOCK_PERIOD))`
The linked Solidity code in the smart contract unnecessarily queries the number of decimals for both the payment token and the distribution token within a loop. These queries lead to extra gas costs, as they are repeatedly fetching data that is not changing throughout the loop execution. ```solidity while (inA > 0) { (curPrice, curRest) = _priceInUnitsAndRemainByRound(curR); if ( // calc out amount inA * (10**distributionToken.decimals()) / (curPrice * 10**IERC20Mint(_paymentToken).decimals()) > curRest ) { // Case when inAmount more then price of all tokens // in current round outAmount += curRest; inA -= curRest * curPrice * 10**IERC20Mint(_paymentToken).decimals() / (10**distributionToken.decimals()); ++ curR; } else { // Case when inAmount less or eqal then price of all tokens // in current round outAmount += inA * 10**distributionToken.decimals() / (curPrice * 10**IERC20Mint(_paymentToken).decimals()); return outAmount; } } ```
The linked variables assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.
The report names a repository or files. Open the report to see its scope.
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