CertiK reviewed OPEX Token (OPX Token) on 25 Oct 2023: 13 findings, 0 critical; none marked fixed.
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 | 3 | 0 | 0 |
| Medium | 0 | 0 | 0 |
| Low | 10 | 0 | 0 |
In the contract `MyToken` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and perform, including but not limited to, the following operations: - Pause/Unpause the contract at will. - Add/remove users to the blacklist. - Mint an indefinite amount to any account at will. - Burn tokens of all the blacklisted users.  In the contract `Ownable` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and transfer ownership to another account. Note that the ownership cannot be renounced because the function is overridden and set as a no-op. 
In the contract `MyToken`, the role `owner` has the authority to update the token balance of an arbitrary account without sanity restriction. Any compromise to the `owner` account may allow a hacker to take advantage of this authority and manipulate users' balances. The attacker could also update his/her balance to a large number by minting, selling these tokens, and causing the token price to drop.
All of the tokens are sent to the contract deployer or one or several externally-owned account (EOA) addresses. This is a centralization risk because the deployer or the owner(s) of the EOAs can distribute tokens without obtaining the consensus of the community. Any compromise to these addresses may allow a hacker to steal and sell tokens on the market, resulting in severe damage to the project.
The function `transferFrom` of the contract `MyToken` prevents blacklisted users to receive or send tokens with the following lines: ```solidity=743 require(isBlocked[from] == false, "From address is blacklisted!"); require(isBlocked[to] == false, "To address is blacklisted!"); ``` Although, a blacklisted user could still be approved by another user and call the transfer from. Because the `_msgSender()` is not checked.
Boolean constants can be used directly and do not need to be compared to true or false. ```solidity=732 require(isBlocked[msg.sender] == false, "This address is blacklisted!"); ``` --- ```solidity=743 require(isBlocked[from] == false, "From address is blacklisted!"); ``` --- ```solidity=744 require(isBlocked[to] == false, "To address is blacklisted!"); ``` --- ```solidity=752 require(isBlocked[msg.sender] == false, "This address is blacklisted!"); ``` --- ```solidity=757 require(isBlocked[account] == true, "User is not in black list!"); ```
A local variable is shadowing another component defined elsewhere. This means that when the contract accesses the variable by its name, it will use the one defined locally, not the one defined in the other place. The use of the variable may lead to unexpected results and unintended behavior. ```solidity=708 constructor(uint256 initialsupply, address supplyaddress, uint8 decimals, string memory name, string memory symbol) ERC20(name, symbol) { ``` - Local variable `decimals` in `MyToken.constructor` shadows: - Function `decimals` in `MyToken` - Function `decimals` in `ERC20` - Function `decimals` in `IERC20Metadata` --- ```solidity=708 constructor(uint256 initialsupply, address supplyaddress, uint8 decimals, string memory name, string memory symbol) ERC20(name, symbol) { ``` - Local variable `name` in `MyToken.constructor` shadows: - Function `name` in `ERC20` - Function `name` in `IERC20Metadata` --- ```solidity=708 constructor(uint256 initialsupply, address supplyaddress, uint8 decimals, string memory name, string memory symbol) ERC20(name, symbol) { ``` - Local variable `symbol` in `MyToken.constructor` shadows: - Function `symbol` in `ERC20` - Function `symbol` in `IERC20Metadata` --- ```solidity=733 address owner = _msgSender(); ``` - Local variable `owner` in `MyToken.transfer` shadows the function `owner` in `Ownable`.
There should always be events emitted in the sensitive functions that are controlled by centralization roles.
It's recommended to check if `from == msg.sender` because transferring from yourself to others equals directly transferring to others. The condition rarely happens that the user approve for himself/herself.
It's recommended to check if `msg.sender == to` in the function `transfer()` since transferring to self makes no sense.
`public` functions that are never called by the contract could be declared `external`.
`transferOwnership()` sets the new `owner` without guaranteeing they are able to actuate transactions on-chain.
The linked functions are equivalent to the compiler-generated getter functions for the respective variables.
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 |
|---|---|---|
| 0xa3fb…8bd80xa3fb72cbf2e07dc1b34aea569ed40755fd978bd8 | Chain: Polygon (POS) | 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