As of 3 Nov 2026, EML has 1 audit report on Trustblock, by CertiK, dated 13 Jun 2023. That report lists no open critical findings. It also holds 1 deployed contract on 1 chain.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
Statuses are the security firm’s own, from each 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.
Open the reportOne 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`.
Open the reportIn 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); } ```
Open the reportAddresses 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.
Open the reportThe 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.
Open the reportSince 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.
Open the reportThe 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); ```
Open the reportOne 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.
Open the reportThe 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.
Open the report| Address | Chain | Source code |
|---|---|---|
| 0x03dd…10620x03dde9e5bb31ee40a471476e2fccf75c67921062 | Chain: Ethereum | Source code: Verified source |
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="eml-protocol"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something 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