CertiK reviewed Token Liquidity Foundation on 14 Mar 2023: 6 findings, 1 critical; 4 marked fixed, 2 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 | 1 |
| High | 1 | 0 | 1 |
| Medium | 0 | 0 | 0 |
| Low | 1 | 0 | 2 |
It is possible for a user to arbitrarily increase their balance by performing self-transfers. The cause is that the function `_transferInternal()` uses a possibly incorrect value for the recipient's balance. ```solidity function _transferInternal(address sender, address recipient, uint256 amount) private returns (bool) { notZeroAddress(recipient); notZeroAddress(sender); uint256 senderBalance = balanceOf[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); if (amount > 0) { if (senderBalance == amount) { // account is now zero so one less holder holders--; } uint256 recipientBalance = balanceOf[recipient]; if (recipientBalance == 0) { // account is has moved from zero one more holder holders++; } unchecked { balanceOf[sender] = senderBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. balanceOf[recipient] = recipientBalance + amount; } } ``` The variable `senderBalance` is assigned to be `balanceOf[sender]` and the variable `recipientBalance` is assigned to be `balanceOf[recipient]` and both values are changed after the assignments. This means that if the sender and recipient are the same address, the change `balanceOf[sender] = senderBalance - amount` will not occur as the change of `balanceOf[recipient] = recipientBalance + amount` will overwrite it. Furthermore, since `sender == recipient`, we have `recipientBalance == senderBalance`, the original starting balance of the user, which gets increased by `amount`. This allows a user to transfer tokens to themselves to increase their token balance. Furthermore, the number of holders can be incorrect with a self-transfer if `amount == senderBalance`. In this situation, `holders` will decrease by one, but will not increase as `recipientBalance` is the balance of the sender before the transfer. This causes the `holders` variable to be lower than expected.
It is possible for a bridge to burn anyone's tokens as the `allowance` check when burning mixes up the token owner and token spender. The linked function allows `msg.sender` to burn a certain amount of tokens from the account associated with `from`. Before calling `_burnInternal()` to burn the token from the `from` account, the function first checks the `allowance` and `amount`. ```solidity=213 function burn(address from, uint256 amount) external bridgingAllowed { address burningAccount = _msgSender(); require(allowance[burningAccount][from] >= amount, "Unauthorized attempt to burn"); require(balanceOf[from] >= amount, "Attempt to burn more than holding"); _burnInternal(amount, from, burningAccount); } ``` The check uses `allowance[burningAccount][from]`, suggesting that the token spender is the first parameter and the token owner is the second parameter. However, in the function `approve()`, the first parameter is actually the token owner and the second parameter is the token spender. Thus, this function can allow a privileged user (approved by calling the `approveBridgeMintBurn()` function) to burn tokens from any account without the permission of the account owners. ```solidity=387 function approve(address spender, uint256 amount) external initialized returns (bool) { notZeroAddress(spender); address account = _msgSender(); allowance[account][spender] = amount; // Announce the change in approval of the spender of liquid tokens emit Approval(account, spender, amount); return true; } ``` Therefore, the privileged user can call `approve(targetAddress, type(uint256).max)` to increase the allowance `allowance[msg.sender][targetAddress]`, and then call `burn(targetAddress, amount)` to burn tokens from the "targetAddress".
The linked variables are increased when the functions `mint()`, `burn()`, `liquify()`, and `solidify()` are invoked. There is no function to decrease these state variables, forcing an integer overflow can cause a denial-of-service attack.
The return value of the internal call is not used. ```solidity=410 _decreaseAllowanceInternal(sender, spender, amount); ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (22)
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