CertiK reviewed EML (EML Token) on 13 Jun 2023: 15 findings, 0 critical; 3 marked fixed, 3 acknowledged, 9 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 | 2 | 0 |
| Medium | 0 | 1 | 0 |
| Low | 9 | 0 | 3 |
The current setup of the `TokenFactory` contract grants the ability to create numerous different clones of tokens based on the same implementation contract. After review of the whitepaper and other documentation, it seems that the only token in the ecosystem is the EML token in which the maximum supply has already been minted. The minting of new "EML token" contracts might have unintended consequences and might cause confusion within the community. The audit team would like to better understand the current implementation and whether there are plans for other tokens in the future.
One or more parameters with `memory` data location are never modified in their functions and those functions are never called internally within the contract. Thus, their data location can be changed to `calldata` to avoid the gas consumption copying from calldata to memory. ```solidity=17 function createProxy(address owner, string memory name, string memory symbol, uint256 initialSupply) public onlyOwner returns (address) { ``` `createProxy` has memory location parameters: `name`, `symbol`. --- ```solidity=408 function initialize(address owner, string memory name, string memory symbol, uint256 initialSupply) public initializer { ``` `initialize` has memory location parameters: `name`, `symbol`. --- ```solidity=573 function batchTransfer(address[] memory recipients, uint256[] memory amounts) public { ``` `batchTransfer` has memory location parameters: `recipients`, `amounts`. --- ```solidity=607 function batchVestedTransfer( ``` `batchVestedTransfer` has memory location parameters: `recipients`, `amounts`, `startsAt`, `periods`, `counts`. --- ```solidity=648 function batchTimeLockedTransfer( ``` `batchTimeLockedTransfer` has memory location parameters: `recipients`, `amounts`, `expiresAt`.
In the EML token implementation, there is a potential missing check on the user's balance when adding a timelock in the `addTimeLock` function. Here is the existing `addTimeLock` function: ```solidity /** * @dev only locker can add time lock */ function addTimeLock( address account, uint256 amount, uint256 expiresAt ) public onlyLocker whenNotPaused { _addTimeLock(account, amount, expiresAt); } ``` There is no verification that `amount` is less than or equal to the balance of `account`. This could lead to potential issues if the function is called with an `amount` that is larger than `account`'s balance. For comparison, the `addVestingLock` function has a proper check on the `amount` and the balance of the `account`: ```solidity /** * @dev only locker can add vesting lock */ function addVestingLock( address account, uint256 amount, uint256 startsAt, uint256 period, uint256 count ) public onlyLocker whenNotPaused { require(amount > 0 && balanceOf(account) >= amount, "VestingLock: amount is 0 or over balance"); _addVestingLock(account, amount, startsAt, period, count); } ```
Addresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. ```solidity=14 logicContract = _logicContract; ``` - `_logicContract` is not zero-checked before being used.
The locker role is able to add and update locks for an account through the following functions: - `addTimeLock()` - `addVestingLock()` - `updateVestingLock()` The concern is that users can front-run those functions by transferring the EML tokens before the lock is imposed, thus bypassing the lock period.
Since the locker contracts are in charge of creating token locks and sending funds to various wallets, the audit team would like to better understand the management of funds and how the team expects to ensure that there are sufficient funds within each locker wallet to operate and continue the distribution of funds to users.
The return values of the `transfer()` and `transferFrom()` calls in the smart contract are not checked. Some ERC-20 tokens' transfer functions return no values, while others return a bool value, they should be handled with care. If a function returns `false` instead of reverting upon failure, an unchecked failed transfer could be mistakenly considered successful in the contract. ```solidity=425 IERC20Upgradeable(tokenAddress).transfer(owner(), tokenAmount); ```
One or more logic contracts do not protect their initializers. An attacker can call the initializer and assume ownership of the logic contract, whereby he/she can perform privileged operations that trick unsuspecting users into believing that she is the owner of the upgradeable contract. ```solidity=406 contract TokenImplementation is Initializable, PausableUpgradeable, OwnableUpgradeable, Supervisable, Burnable, Freezable, Lockable, ERC20Upgradeable { ``` - `TokenImplementation` is an upgradeable contract that does not protect its initializer. ```solidity=408 function initialize(address owner, string memory name, string memory symbol, uint256 initialSupply) public initializer { ``` - `initialize` is an unprotected initializer function.
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.
| Address | Chain | Source code |
|---|---|---|
| 0x03dd…10620x03dde9e5bb31ee40a471476e2fccf75c67921062 | Chain: Ethereum | Source code: Verified source |
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