$oDOGE

CertiK2 addresses on Ethereum

CertiK reviewed Ordinal Doge ($oDOGE) on 20 Apr 2023: 12 findings, 0 critical; 1 marked fixed, 11 acknowledged.

No open critical findings
Open the reportOpen Ordinal Doge’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings12

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

SeverityOpenAcknowledgedFixed
Critical000
High010
Medium030
Low071
HighState Variable Initialization In Constructor WithAcknowledged

Code inside the constructor is only executed during the deployment of the contract and only affects the state of the implementation contract. Therefore, it will never be executed in the context of the proxy’s state which makes it irrelevant for an upgradeable contract.

Medium`ERC721` Not Explicitly Transferred In Before AucAcknowledged

Although the contract `TokenVault` implements the contract `ERC721HolderUpgradeable` to receive the ERC-721 tokens for auction, it does not explicitly calls the function `IERC721(token).transferFrom` to transfer the ERC-721 tokens to the current contract address. Thus, the winners may not able to get the auction token in the function `end()`. The function `initialize()` only set the token address and id, but does not transfer the token ownership to the contract address. Since the function `initialize()` can be called by anyone, anyone can create the auction and become the `curator` even if he/she is not the token owner.

MediumFallback Function May Suffer Out-Of-Gas ErrorAcknowledged

In the Ethereum, `send/transfer/call` can be used for ETH transferrings. In the worst case, the fallback function can only rely on 2300 gas being available (for example when `send` or `transfer` is used), leaving little room to perform other operations except basic logging. Therefore, the fallback function of the current contract is not suitable for doing much. The contract `InitializedProxy` will make a delegate call to the `_impl` contract, which may cause the out-of-gas error. ```solidity fallback() external payable { address _impl = logic; assembly { let ptr := mload(0x40) calldatacopy(ptr, 0, calldatasize()) let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0) let size := returndatasize() returndatacopy(ptr, 0, size) switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } ```

MediumIneffective `isContract()` CheckAcknowledged

The implementation of the `isContract` check can not cover all scenarios. The check can be bypassed if the call is from the constructor of a smart contract or when the contract is destroyed. In that case, the codesize will also be zero. The "isContract" function in the OpenZeppelin "Address" library uses the same implementation, but comments mention that "it's unsafe to rely on the check and it can be bypassed". Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol.

LowDivide Before MultiplyAcknowledged

Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=210 uint256 feePerSecond = currentAnnualFee / 31536000; ``` ```solidity=214 uint256 curatorMint = sinceLastClaim * feePerSecond; ``` --- ```solidity=220 feePerSecond = currentAnnualFee / 31536000; ``` ```solidity=221 uint256 govMint = sinceLastClaim * feePerSecond; ``` --- ```solidity=251 uint256 averageReserve = reserveTotal / votingTokens; ``` ```solidity=253 uint256 reservePriceMin = averageReserve * ISettings(settings).minReserveFactor() / 1000; ``` --- ```solidity=268 uint256 averageReserve = (reserveTotal - (old * weight)) / (votingTokens - weight); ``` ```solidity=270 uint256 reservePriceMin = averageReserve * ISettings(settings).minReserveFactor() / 1000; ```

LowLack of reasonable boundaryAcknowledged

The function `initialize()` does not contain the necessary boundary checks for `fee`. Also, the variable `maxCuratorFee` does not have a reasonable boundary.

LowLogic Issue On Auction Price SettingAcknowledged

The contract `TokenVault` is an ERC20 token. The holders of this token can propose the auction price of a certain ERC721 token, with the holding balance as the voting weight. The final auction start price will be the average of the proposed prices. The one who calls the function `initialize()` will mint the initial tokens to the `curator` and set the initial price for the `curator`. The token can be transferred and the voted price will be changed because of the balance changes. If the auction is successfully ended, all the holders can get the ETH from the auction in proportion to the user balance. If the `curator` does not perform any transfer, only the `curator` and the `govAddress` will get the price voting power by claiming fees. However, the newly minted tokens do not automatically contribute to the price voting. In the code snippet below, the `if` condition `_from != address(0)` will skip the voting price update for mint. ```solidity=287 function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal virtual override { if (_from != address(0) && auctionState == State.inactive) { ... //CetiK comment: update the voting price based on new balances ```

LowMissing Zero Address ValidationAcknowledged

Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity=106 settings = _settings; ``` - `_settings` is not zero-checked before being used. --- ```solidity=114 token = _token; ``` - `_token` is not zero-checked before being used. --- ```solidity=118 curator = _curator; ``` - `_curator` is not zero-checked before being used. --- ```solidity=146 curator = _curator; ``` - `_curator` is not zero-checked before being used. --- ```solidity=175 curator = _curator; ``` - `_curator` is not zero-checked before being used.

LowOpenZeppelin Library Code Included in Source CodeAcknowledged

It is highly recommended NOT to include OpenZeppelin library code directly in source code, but import the original library to minimize risk because even slight changes to the library code may lead to critical/major vulnerabilities/bugs. For Solidity version 0.6.x, the latest OpenZeppelin version 3.4.2 should be used.

LowPotential Front-Running RiskAcknowledged

Since there is no access restriction on the function `initialize()`, malicious hackers can execute the function `initialize()` to get the `curator` role, get new ERC20 tokens minted, and set a new auction price.

Show 1 more
LowUnused VariableAcknowledged

The variable `vaultClosed` is declared but not used. ```solidity=75 bool public vaultClosed; ```

Contracts in scope2

AddressChainSource code
0x7b0fce54574d9746414d11367f54c9ab94e53dcaChain: EthereumSource code: Verified source
0x796a4503b444a71b331c9556bef0815237ddeabcChain: EthereumSource code: Verified source

Part of Ordinal Doge’s security record

Ordinal Doge
Audits1 audit by 1 firmLast audit Apr 2023, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open Ordinal Doge’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