CertiK reviewed Edge Matrix Computing (EdgeMatrix Computing Protocol 2(EMC 2)) on 2 Feb 2024: 11 findings, 1 critical; 6 marked fixed, 5 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 | 0 |
| Medium | 0 | 0 | 1 |
| Low | 4 | 0 | 4 |
According to the design, anyone holding a signature issued by the manager can bind to a node and become the beneficiary of that node. Beneficiaries can deposit into the node and create vesting schedules for anyone. However, the current signature verification design cannot determine uniqueness and may have the following potential risks: 1. Signed messages are not properly verified with the current chain ID, thus allowing attackers to perform replay attacks across chains. Hardcoded or cached chain ID values are also vulnerable since a hard fork may occur and change the chain ID in the future. 2. The `_nodeId` is not used as a parameter, and there is no check to verify whether the node is already bound to a beneficiary. Although hackers cannot get more tokens out because stakers can only withdraw their own quota. However, this issue can be exploited to disrupt the normal stake/withdraw operations of the protocol. ```solidity modifier verifyAddressSigner(string memory nonce, bytes memory signature) { require(!verifiedState[nonce], "signature validation failed"); bytes32 messageHash = keccak256(abi.encodePacked(msg.sender, nonce)); require( manager == messageHash.toEthSignedMessageHash().recover(signature), "signature validation failed" ); verifiedState[nonce] = true; _; } ```
According to the design, the node's beneficiary could create the vesting schedule through the `releaser` contract. The start time of the schedule is 15 minutes after the schedule is created. ```solidity releaser.createVestingSchedule( _beneficiary, block.timestamp + 15 * 60, 1, DurationUnits.Days30, _amount); ``` However, according to the design of contract `ReleaseVesingV1`, when `minStartDays` is greater than 0, the schedule will not be created successfully. ```solidity function createVestingSchedule( address _beneficiary, uint256 _start, uint256 _duration, DurationUnits _durationUnits, uint256 _amountTotal ) external { ... require( _start >= block.timestamp + minStartDays * 1 days, string.concat("VestingContract: invalid start time") ); ... } ```
This [Checks-Effects-Interactions Pattern](https://docs.soliditylang.org/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern) is a best practice for writing secure smart contracts that involves performing all state changes before making any external function calls. ### External call(s) ```solidity=168 token.safeTransferFrom(msg.sender, address(this), _amount); ``` * 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=169 node.amount = node.amount.add(_amount); ``` ```solidity=170 node.accumulated = node.accumulated.add(_amount); ``` --- ### External call(s) ```solidity=217 SafeERC20.safeIncreaseAllowance(token, releaseContract, _amount); ``` * 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)` ```solidity=219 releaser.createVestingSchedule( _beneficiary, block.timestamp + 15 * 60, 1, DurationUnits.Days30, _amount ); ``` ### State variables written after the call(s) ```solidity=227 node.amount = node.amount.sub(_amount); ``` ```solidity=229 node.debt = node.debt.add(_amount); ```
`USDT` is a non-standard `ERC20` token, its `transfer` and `transferFrom` functions do not yield a return value. If the input token refers to `USDT`, any attempt to transfer `USDT` using these functions will result in a revert.
The `setLimit()` function lacks of sanity check, the `minLimit` represents minimum amount of a single deposit, and `maxLimit` represents the upper limit of the node balance (if `maxLimit` is 0, it means there is no limit). If `maxLimit` is set less than `minLimit` (maxLimit is not 0), deposits will not be possible. ```solidity function setLimit(uint256 _minLimit, uint256 _maxLimit) public onlyManager { minLimit = _minLimit; maxLimit = _maxLimit; } ```
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. **MerkleClaim.sol** ```solidity=468 token = token_; ``` - `token_` is not zero-checked before being used. **NodeStake.sol** ```solidity=122 token = IERC20(_token); ``` - `_token` is not zero-checked before being used. --- ```solidity=123 releaseContract = _releaseContract; ``` - `_releaseContract` is not zero-checked before being used. --- ```solidity=124 manager = _manager; ``` --- ```solidity=143 manager = _manager; ``` - `_manager` is not zero-checked before being used, the signature verification fails because `ECDSA.recover()` never returns a 0 address.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (2)
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