CertiK reviewed XPROJECT (Xpro) on 9 Jun 2023: 21 findings, 0 critical; 7 marked fixed, 3 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 | 1 | 2 |
| Medium | 0 | 2 | 2 |
| Low | 11 | 0 | 3 |
All `XPRO` tokens are sent to the contract owner when deploying the contract. This is a potential centralization risk as the deployer can distribute `XPRO` tokens without the consensus of the community.
Generally, deflationary tokens are incompatible with DEX and special rules need to be coded for DEX addresses eg. excluding them from reward. Otherwise, a hacker can exploit the protocol using the reflection mechanism. In a reflection mechanism, users are charged a fee every time they make a trade. This fee is used to reward users who hold tokens, but it does not trigger a transfer of tokens. Instead, it only modifies a coefficient. In this mechanism, the user possesses two types of token amounts: `tAmount` and `rAmount`. `tAmount` represents the actual amount of tokens, while `rAmount` denotes the number of tokens after reflection. The rate between the two is calculated as $$$\frac{rTotal}{tTotal}$$$. ```solidity=447 function balanceOf(address account) public view override returns (uint256) { if (_isExcluded[account]) return _tOwned[account]; return tokenFromReflection(_rOwned[account]); } ``` ```solidity=499 function tokenFromReflection(uint256 rAmount) public view returns(uint256) { require(rAmount <= _rTotal, "Amount must be less than total reflections"); uint256 currentRate = _getRate(); return rAmount.div(currentRate); } ``` ```solidity=584 function _getRate() private view returns(uint256) { (uint256 rSupply, uint256 tSupply) = _getCurrentSupply(); return rSupply.div(tSupply); } ``` The balance of accounts that include rewards is calculated by $$$\frac{rAmount}{rate}$$$, where the rate is determined base on the total supply. In the `deliver()` function, it destroys the caller's tokens and reduces the value of `rTotal`. As a result, the rate decreases, which leads to an increase in the tokens reflected by other users, which means the attacker may manipulate the balance in `XPRO` pool. ```solidity=481 function deliver(uint256 tAmount) public { address sender = _msgSender(); require(!_isExcluded[sender], "Excluded addresses cannot call this function"); (uint256 rAmount,,,,,,) = _getValues(tAmount); _rOwned[sender] = _rOwned[sender].sub(rAmount); _rTotal = _rTotal.sub(rAmount); _tFeeTotal = _tFeeTotal.add(tAmount); } ``` Furthermore, the attacker may amplify the attack by utilizing the `skim` function in Uniswap/Pancakeswap. This allows the attacker to retrieve the difference between the balance and reserve on the `XPRO` token from the pool and subsequently execute another invocation of the `deliver()` function. As a result, the deposit balance on the `XPRO` token within the pool is amplified. ```solidity // force balances to match reserves function skim(address to) external lock { address _token0 = token0; // gas savings address _token1 = token1; // gas savings _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0)); _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1)); } ```
In the contract `XPRO`, function `includeInReward()` allows the contract owner to include an account in the reward distribution, which means that the account will receive a share of the rewards. ```solidity=512 function includeInReward(address account) external onlyOwner() { require(_isExcluded[account], "Account is already included"); for (uint256 i = 0; i < _excluded.length; i++) { if (_excluded[i] == account) { _excluded[i] = _excluded[_excluded.length - 1]; _tOwned[account] = 0; _isExcluded[account] = false; _excluded.pop(); break; } } } ``` However, variable `_rOwned[account]` is not updated in the function `includeInReward()`, which will make the accounts included siphon off the tokens out of the balances of all token holders. The Rate was higher at the moment of the `excludeFromReward(account)` call, so the `_rOwned[account] / _tOwned[account]` ratio is bigger than expected for accounts included in the reward.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (1)
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