Ommniverse

CertiKFiles named in the report

CertiK reviewed Ommniverse on 5 Mar 2023: 27 findings, 0 critical; 25 marked fixed, 2 open.

No open critical findings
Open Ommniverse’s security record

The original report was removed from its address (seen 19 Oct 2026).

Trustblock does not review code.

Findings27

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High004
Medium208
Low0013
HighCentralization Related RisksFixed

In the contract **Governance**, the role **Owner** has authority over the following functions: - function createProposal(), to create a proposal. - function executeProposal(), to execute a proposal. Any compromise to the **Owner** account may allow a hacker to take advantage of this authority. In the contract **Fractions**, the role **Owner** has authority over the following functions: - function addMinter(), to add the address `minter` as the `Minter` role. - function removeMinter(), to remove the address `minter` from the `Minter` role. - function updateBaseUri(), to update the base URI of the fractions. Any compromise to the **Owner** account may allow a hacker to take advantage of this authority. In the contract **Fractions**, the role **Minter** has authority over the following functions: - function mint(), to mint any amount of fractions to any address. Any compromise to the **Minter** account may allow a hacker to take advantage of this authority. In the contract **NftVaultFactory**, the role **Owner** has authority over the following functions: - function setFeeReceiver(), to update the fee receiver. - function setFee(), to update the fee. Any compromise to the **Owner** account may allow a hacker to take advantage of this authority. In the contract **Ommniverse**, the role **Owner** has authority over the following functions: - function pause(), to return the contract to a normal state. - function unpause(), to trigger a stopped state. - function mint(), to mint `OMMI` ERC20 tokens to any address. Any compromise to the **Owner** account may allow a hacker to take advantage of this authority. In the contract **VaultFactory**, the role **Owner** has authority over the following functions: - function setFeeReceiver(), set update the fee receiver. - function setFee(), to update the fee. - setAuctionLength(), to update the auction length. Any compromise to the **Owner** account may allow a hacker to take advantage of this authority.

HighCentralized Control of Contract UpgradeFixed

`Fractions`,`NftVault`, `NftVaultFactory`,`OmmniverseCollection`,`Ommniverse`,`Vault`, and `VaultFactory` 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.

HighFunction `transferFrom` Fails for Transfers To thFixed

All calls of the form `transferFrom(from, dest, amount)` where the `dest` address is zero will fail. ```solidity OmmniToken.transferFrom(msg.sender, address(0), burn) ```

HighPotential Manipulation Of Voting OutcomeFixed

The `vote()` function allows token holders to vote on proposals, and the number of votes cast depends on the user's total quantity of voting tokens. Although each user can only vote once, the voting tokens are not locked, allowing the holder of the voting tokens freely transfer the voting tokens to any address after casting the vote, giving that address more voting power and potentially manipulating the voting outcome. ```solidity=108 function vote(uint256 _proposalId, bool _vote) public { require(proposals[_proposalId].votingPeriod > block.timestamp); require(!voters[_proposalId][msg.sender], "Already voted"); if (_vote) { voteTotals[_proposalId].yes += token.balanceOf(msg.sender); } else { voteTotals[_proposalId].no += token.balanceOf(msg.sender); } voters[_proposalId][msg.sender] = true; emit VoteCast(_proposalId, msg.sender, _vote); } ```

MediumIncorrect Calculation Of `fractionValue`Fixed

In the function `end`, the fee sent to the fee receiver is not correctly calculated, there is a duplicated `liveprice` multiplied in the calculation formula. ```solidity _sendOmmi( ISettings(settings).feeReceiver(), livePrice * livePrice * ((ISettings(settings).fee()) / 1000) ); ```

MediumIncorrect Calculation Of `OMMI` ShareFixed

The variable `address(this).balance` is being used to calculate the share of the `Ommni` token is incorrect. `address(this).balance` represents the balance of ETH in the contract `Vault`, it should use the balance of the `Ommni` token. ```solidity=204 fractionValue = address(this).balance / IFERC1155(fractions).totalSupply(fractionsID); ``` ```solidity=215 uint256 share = bal * fractionValue; IFERC1155(fractions).burn(msg.sender, fractionsID, bal); _sendOmmni(msg.sender, share); ```

MediumIncorrect Parameter In `mint()`Fixed

The parameter `address(this)` passed in function `IFERC1155(fractions).mint()` is incorrect, the parameter should be the address of the vault. ```solidity=89 uint256 fractionId = IFERC1155(fractions).mint(address(this), _amount); ``` ```solidity=46 function mint( address vault, uint256 amount ) external onlyMinter returns (uint256) { count++; idToVault[count] = vault; _mint(msg.sender, count, amount, "0"); _totalSupply[count] = amount; return count; } ```

MediumLack Of `amount` ValidationFixed

As per the code comments of the `start()` function, `reservePrice` in `OMMI` must be sent to the contract from the caller. Hence there should be a validation of the amount to ensure that the starting auction price is not smaller than the reserve price.

MediumLack Of Auction State ValidationFixed

In the `buyFraction()` function, there should be a validation to ensure that users can only buy NFT fractions before the NFT auction starts.

MediumPotential Overwrite Previous Vesting ScheduleFixed

There is no validation that an existing vesting schedule was created for the beneficiary, so the previous vesting schedule of the beneficiary will be overwritten. ```solidity=37 mapping(address => VestingSchedule) vestingSchedules; ``` ```solidity=88 vestingSchedules[_beneficiary] = VestingSchedule( _beneficiary, _start, cliff, _duration, _slicePeriodSeconds, _amount, 0 ); ```

Show 10 more
MediumRedundant Modifier `payable`Fixed

The functions that are declared `payable` can receive ether. As per the description in the code comments, the functions `buyFraction()`, `start()`, and `bid()` are not designed for receiving ether, so they are redundant to have the `payable` modifier. ```solidity=108 /// @notice buys fractions of the Nft. Must send price in Ommni function buyFraction(uint256 _amount) external payable nonReentrant { ``` ```solidity=154 /// @notice kick off an auction. Must send reservePrice in Ommni function start(uint256 _amount) external payable nonReentrant { ```

MediumStart An Auction Before Fractions Are Sold OutFixed

In the function `start()`, there is no validation to ensure that the auction does not start before fractions of the NFT are sold out.

LowCalculate Release Amount From CliffFixed

In the function `_computeReleasableAmount`, the release amount is calculated from the cliff time instead of the start time. We would like to confirm with the client if the current implementation aligns with the original project design.

LowLack of reasonable boundaryFixed

The variables do not have reasonable boundaries, so they can be given arbitrary values after deploying.

LowMissing Emit EventsFixed

Functions that affect the status of sensitive variables should be able to emit events as notifications to customers.

LowMissing Zero Address ValidationFixed

Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity=84 fractions = _fractions; ``` - `_fractions` is not zero-checked before being used. --- ```solidity=86 underlying = _underlying; ``` - `_underlying` is not zero-checked before being used. --- ```solidity=88 curator = _curator; ``` - `_curator` is not zero-checked before being used. --- ```solidity=34 fnft = _fnft; ``` - `_fnft` is not zero-checked before being used. --- ```solidity=35 ommni = _ommni; ``` - `_ommni` is not zero-checked before being used. --- ```solidity=69 feeReceiver = _feeReceiver; ``` - `_feeReceiver` is not zero-checked before being used.

LowMissing Zero Address ValidationFixed

The cited address input is missing a check that it is not `address(0)`.

LowOwner Of ERC721 Not Receive `OMMI` TokenFixed

Users can buy ERC721 tokens in the `NftVault` contract using `OMMI` tokens, which are sent to the fee receiver. However, no `OMMI` tokens are sent to the ERC721 token's owner. Similarly, In the `Vault` contract, if the ERC721 token is successfully auctioned off, all `OMMI` tokens will be possibly distributed by the fractions owners, and there is no withdrawal function for the vault owner to retrieve the `OMMI` tokens in the contract. We would like to confirm with the client that the current implementation aligns with the original project design.

LowPossibility To Create Contracts With The Same NamFixed

Because there is no validation to guarantee the name and symbol are unique, anybody may use the function `mintContact()` to establish the `UserCllection` contract with the same name and symbol.

LowRedundant casting to payable addressFixed

The aforementioned lines cast the contract to the payable address yet no ether transfer is performed. ```solidity=154 address payable beneficiaryPayable = payable( vestingSchedule.beneficiary ); ```

Show 5 more
LowRedundant SafeMath UsageFixed

Solidity version >=0.8.0 includes checked arithmetic operations and underflow/overflow by default, making SafeMath redundant.

LowThe Purpose Of Burning `Ommi` TokenFixed

When the supply of `OMMI` tokens reaches half of the total supply, half of the `OMMI` tokens paid for buying NFT/ERC1155 will be burned. We failed to find any design documentation for this part of the business logic. ```solidity=84 if (OmmniToken.totalSupply() <= 3000000 * 10 * 18) { uint256 burn = nftPrice / 2; require( OmmniToken.transferFrom( msg.sender, ISettings(settings).feeReceiver(), burn ) && OmmniToken.transferFrom(msg.sender, address(0), burn), "Ommni Transfer failed" ); } ``` ```solidity=125 if (OmmniToken.totalSupply() <= 3000000 * 10 * 18) { uint256 burn = (_amount * omni_price) / 2; require( OmmniToken.transferFrom(msg.sender, address(this), burn) && OmmniToken.transferFrom(msg.sender, address(0), burn), "Ommni Transfer failed" ); } ``` We would like the team to elaborate more about the purpose of burning tokens and the intention of the condition: ```solidity OmmniToken.totalSupply() <= 3000000 * 10 * 18 ```

LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaFixed

The return value of the transfer()/transferFrom() call is not checked. ```solidity=141 OmmniToken.transferFrom(msg.sender, address(this), _amount); ``` --- ```solidity=158 OmmniToken.transferFrom(msg.sender, address(this), _amount); ``` --- ```solidity=197 OmmniToken.transfer(_to, _value); ```

LowUnnecessary receive() and fallback() functionsFixed

The payable receive() and fallback() functions allow ethers to be transferred to the contract by mistake, and there is no way to get ethers out of the contract, so those ethers are locked in the contract and lost forever.

LowUnprotected InitializerFixed

One or more logic contracts do not protect their initializers. An attacker can call the initializer and assume ownership of the logic contract, whereby she can perform privileged operations that trick unsuspecting users into believing that she is the owner of the upgradeable contract. ```solidity=9 contract Fractions is Initializable, ERC1155Upgradeable, OwnableUpgradeable { ``` - `Fractions` is an upgradeable contract that does not protect its initializer. ```solidity=20 function initialize() public initializer { ``` - `initialize` is an unprotected initializer function. --- ```solidity=15 contract NftVault is ``` - `NftVault` is an upgradeable contract that does not protect its initializer. ```solidity=44 function initialize( ``` - `initialize` is an unprotected initializer function. --- ```solidity=11 contract NftVaultFactory is Initializable, OwnableUpgradeable { ``` - `NftVaultFactory` is an upgradeable contract that does not protect its initializer. ```solidity=38 function initialize( ``` - `initialize` is an unprotected initializer function. --- ```solidity=11 contract OmmniverseCollection is ``` - `OmmniverseCollection` is an upgradeable contract that does not protect its initializer. ```solidity=24 function initialize() public initializer { ``` - `initialize` is an unprotected initializer function. --- ```solidity=16 contract Vault is ``` - `Vault` is an upgradeable contract that does not protect its initializer. ```solidity=71 function initialize( ``` - `initialize` is an unprotected initializer function. --- ```solidity=11 contract VaultFactory is ``` - `VaultFactory` is an upgradeable contract that does not protect its initializer. ```solidity=49 function initialize( ``` - `initialize` is an unprotected initializer function.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (14)

  • contracts/OmmniverseToken.sol
  • contracts/VaultFactory.sol
  • contracts/Vault.sol
  • contracts/NftVault.sol
  • contracts/Vesting.sol
  • contracts/UserCollectionFactory.sol
  • contracts/governance/Governance.sol
  • contracts/interfaces/OERC721.sol
  • contracts/OmmniverseCollection.sol
  • contracts/UserCollection.sol
  • contracts/interfaces/ISettings.sol
  • contracts/NftVaultFactory.sol
  • contracts/Fraction.sol
  • contracts/interfaces/IFERC1155.sol

Part of Ommniverse’s security record

Ommniverse
Audits1 audit by 1 firmLast audit Mar 2023, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open Ommniverse’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

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