CertiK reviewed DaoMax.io (DaoMax.) on 4 Jan 2023: 14 findings, 0 critical; 3 marked fixed, 1 acknowledged, 10 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 | 3 | 0 | 0 |
| Medium | 0 | 1 | 0 |
| Low | 7 | 0 | 3 |
In the contract **ConduitController**, the role **DAO** has authority over the following functions: - function setDelayPeriod(), to set the delay period. Any compromise to the **DAO** account may allow a hacker to take advantage of this authority. In the contract **ConduitController**, the role **Operator** has authority over the following functions: - function proclaimChannel(), to proclaim the channel. - function confirmChannel(), to confirm the channel. - function revokeChannel(), to revoke the channel Any compromise to the **Operator** account may allow a hacker to take advantage of this authority. In the contract **Conduit**, the role **ConduitController** has authority over the following functions: - function transferOwnership(), to transfer the ownership of the conduit. Any compromise to the **ConduitController** account may allow a hacker to take advantage of this authority. In the contract **DEXConfig**, the role **DAO** has authority over the following functions: - function setNameVersion(), to set the name version. - function setFeeRate(), to set the fee rate. - function setFeeRecipient(), to set the fee recipient. - function setMaxRoyaltyRate(), to set the maximum royalty rate. Any compromise to the **DAO** account may allow a hacker to take advantage of this authority. In the contract **Manager**, the role **DAO** has authority over the following functions: - function transferDAO(), to update the address of the `DAO` contract. - function setOperators(), to set the operators. Any compromise to the **DAO** account may allow a hacker to take advantage of this authority. In the contract **Manager**, the role **Operator** has authority over the following functions: - function updateConduitController(), to update the address of the contract `ConduitController`. - function setPlatformDex(), to add/remove the address to/from the platform Dex list. - function setPlatformNft(), to add/remove the NFT of the platform, and add/remove the NFT to/from the white list. - function setNftsAllowed(), to add/remove the NFT to/from the white list. - function setPaymentsAllowed(), to to set the minimum price to be paid. Any compromise to the **Operator** account may allow a hacker to take advantage of this authority.
`DEX`, `Manager`, and `ConduitController` are upgradeable contracts, the owner can upgrade the contract without the community's commitment. If an attacker compromises the account, he can change the implementation of the contract and drain tokens from the contract.
The function `lazyMint()` and `setRoyaltyInfo()` can be called by anyone as they have no access restriction. Although these functions are defined in the abstract contract, the child contract can inherit these external functions. In the current scope of the protocol, there is no child contract found. Potentially, if the child contract inherits the LazyMintableERC1155 and does not override the above-mentioned functions, or override the functions without access control, anyone can call the function `lazyMint()` and mint any amount of ERC1155 tokens with the `tokenId` id, and arbitrarily set the royalty information of `ERC2981` token royalty via calling the function `lazyMint()`/`setRoyaltyInfo()`. **Proof of concept** Anyone can call the functions to mint tokens and set the royalty information.
In the `transferERC1155()` and `transferERC721()` functions, if the NFT tokens are not lazy mint tokens, they will be checked for royalty information and transferred from the `_nftOwner` to the `_nftRecipient`. However, it is necessary to ensure that sufficient allowances have been granted by the `_nftOwner`, allowing the DEX contract to perform this transfer.
Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity=47 dao = _dao; ``` - `_dao` is not zero-checked before being used. --- ```solidity=48 conduitController = _conduitController; ``` - `_conduitController` is not zero-checked before being used. --- ```solidity=79 conduitController = _conduitController; ``` - `_conduitController` is not zero-checked before being used. --- ```solidity=16 owner = _owner; ``` - `_owner` is not zero-checked before being used. --- ```solidity=17 conduitController = _conduitController; ``` - `_conduitController` is not zero-checked before being used. --- ```solidity=39 manager = _manager; ``` - `_manager` is not zero-checked before being used.
The conduit controller can call the function `transferOwnership()` to transfer the ownership of the conduit. Once the ownership is transferred, the original owner will be not able to withdraw the tokens left in the conduit. We would like to confirm with the client if the current implementation aligns with the original project design.
The function `withdraw()` transfers out ETH locked within the contract to the contract owner. ```solidity=58 function withdraw() external onlyOwner { (payable(owner())).transfer(address(this).balance); } ``` However, there is no default fallback function, so the contract cannot receive ETH. Therefore, this function is redundant.
The return value of an external call is not stored in a local or state variable. --- ```solidity=245 requireUserConduit(_tokenOwner).assertCall(_tokenContract, 0, callData); ```
According to the official document of the solidity language("https://docs.soliditylang.org/en/v0.8.7/types.html?highlight=delegatecall#members-of-addresses"), both of the `call()` and `delegatecall()` are low-level functions and should be used with care. Specifically, any unknown contract might be malicious and if you call it, you hand over control to that contract which could in turn call back into your contract, so be prepared for changes to your state variables when the call returns. The regular way to interact with other contracts is to call a function on a contract object (contract.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.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (20)
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