Introduction
We express our gratitude to the Onchain Foundation team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
Onchain Foundation is a project that facilitates the minting process of ERC721A NFT tokens.
Document | |
---|---|
Name | Smart Contract Code Review and Security Analysis Report for Onchain Foundation |
Audited By | Viktor Lavrenenko |
Approved By | Grzegorz Trawinski |
Website | https://onchain.org/→ |
Changelog | 07/02/2025 - Preliminary Report |
19/03/2025 - Final Report | |
Platform | Ethereum, Arbitrum, Polygon, Base, BSC |
Language | Solidity |
Tags | ERC2981, NFT, Royalties, Airdrop, MerkleProof |
Methodology | https://hackenio.cc/sc_methodology→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Onchain Foundation
- Audited By
- Viktor Lavrenenko
- Approved By
- Grzegorz Trawinski
- Website
- https://onchain.org/→
- Changelog
- 07/02/2025 - Preliminary Report
- 19/03/2025 - Final Report
- Platform
- Ethereum, Arbitrum, Polygon, Base, BSC
- Language
- Solidity
- Tags
- ERC2981, NFT, Royalties, Airdrop, MerkleProof
- Methodology
- https://hackenio.cc/sc_methodology→
Review Scope | |
---|---|
Repository | https://github.com/blank-development/mintdash-contracts→ |
Commit | 0d6dca0 |
Review Scope
- Commit
- 0d6dca0
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
Functional requirements are missing:
The project's purpose is not described.
Business logic is not provided.
Uses cases are not described.
Project's features are missing.
Technical description is missing:
Key functions descriptions are missing for the files in scope.
Architectural overview is missing.
Roles and authorization are described.
No information on used technologies is provided.
Code quality
Best practices are not followed.
The development environment is configured.
Test coverage
Code coverage of the project is 84% (branch coverage).
Deployment and basic user interactions are covered with tests.
Negative cases coverage is missed.
Interactions by several users are tested thoroughly.
System Overview
Onchain Foundation is a set of smart contracts responsible for the minting of NFT tokens with the following contracts:
ERC721DropMinter
- a contract that facilitates the minting ofERC721Drop
collections through various stages, including public minting, allowlist minting, and token-gated minting.ERC721Drop
- serves as a foundational contract for creating and managing ERC721 token drops. It allows for the initialization of token properties, minting of tokens, and support for royalty standard.ERC721DropMetadata
- a contract that provides metadata management and minting functionalities for ERC721 tokens in a drop format. It serves as an abstract base contract, allowing derived contracts to implement specific minting logic while managing essential metadata features.Payout
- a contract that manages the distribution of funds and royalties for ERC721 token projects. It provides functionalities for updating payout addresses, managing royalties, and withdrawing funds, ensuring that the financial aspects of the token drop are handled securely and efficiently.DropStructs
- a contract defines a series of data structures used for managing various minting stages in an ERC721 token drop. These structs encapsulate the configuration parameters necessary for public minting, allowlist minting, and token-gated minting, providing a clear and organized way to handle the different stages of a token drop.
Privileged roles
The allowed payers of the collection can mint ERC721Drop token collections via
ERC721DropMinter::mintPublic()
,ERC721DropMinter::mintAllowlist()
, andERC721DropMinter::mintTokenGated()
functions if the recipient of the mint is not msg.sender.The owner of the
ERC721DropMinter
contract can:update the platform fee recipient via
updatePlatformFeeRecipient()
function.update the default platform fee via
updateDefaultPlatformFee()
function.update the collection platform fee via
updateCollectionPlatformFee()
function.renounce and transfer ownership via
renounceOwnership()
andtransferOwnership()
functions.
The owner of the
ERC721Drop
contract can:start the airdrop process via
airdrop()
function.update the max supply of tokens via
updateMaxSupply()
function.update the base URI via
updateBaseURI()
function.update the provenance hash via
updateProvenanceHash()
function.update the allowed minter address via
updateAllowedMinter()
function.update the payout address of the ERC721Drop collection via
updatePayoutAddress()
function.update the royalties via the
updateRoyalties()
function.withdraw all funds or all tokens via
withdrawAllFunds()
andwithdrawTokens()
functions.
The collection owner can:
update public mint stage data via
ERC721DropMinter::updatePublicMintStage()
function.update allowlist mint stage data via
ERC721DropMinter::updateAllowlistMintStage()
function.update token gated mint stage data via
ERC721DropMinter::updateTokenGatedMintStage()
function.update the configuration of three mint stages via
ERC721DropMinter::updateConfiguration()
function.update the creator payout address via the
ERC721DropMinter::updateCreatorPayoutAddress()
function.update the list of collection allowed payers via the
ERC712DropMinter::updatePayer()
function.
The allowed list of minters can mint new ERC721Drop tokens directly via the
ERC721Drop::mint()
function.
Potential Risks
Single Points of Failure and Control: The project is fully or partially centralized, introducing single points of failure and control. This centralization can lead to vulnerabilities in decision-making and operational processes, making the system more susceptible to targeted attacks or manipulation.
Administrative Key Control Risks: The digital contract architecture relies on administrative keys for critical operations. Centralized control over these keys presents a significant security risk, as compromise or misuse can lead to unauthorized actions or loss of funds.
Flexibility and Risk in Contract Upgrades: The project's contracts are upgradable, allowing the administrator to update the contract logic at any time. While this provides flexibility in addressing issues and evolving the project, it also introduces risks if upgrade processes are not properly managed or secured, potentially allowing for unauthorized changes that could compromise the project's integrity and security.
Absence of Upgrade Window Constraints: The contract suite allows for immediate upgrades without a mandatory review or waiting period, increasing the risk of rapid deployment of malicious or flawed code, potentially compromising the system's integrity and user assets.
Owner's Unrestricted State Modification: The absence of restrictions on state variable modifications by the owner leads to arbitrary changes, affecting contract integrity and user trust, especially during critical operations like minting phases. In addition to that, the collection owners can change stages data including the price of the token, timeline and list of allowed recipients while the stage is active.
Front-running During The Initialization Of The Implementation Contract: If the deployment and the initialization of the implementation contracts, which are ERC721DropMinter
and ERC721Drop
, will not be handled within one transaction, it can cause the front-running of the ERC721DropMinter::initialize()
and ERC721Drop::initialize()
functions by the attacker which will lead to unexpected system behavior.
Reliance on Off-Chain Systems: The functionality and security of system contracts may rely on external off-chain systems or processes that are not covered by this audit. This creates a risk that these off-chain dependencies could fail, be manipulated, or behave unexpectedly, potentially leading to unintended behavior of the system.
Support of ERC20 tokens: The payment tokens for the minting stages are chosen from the list of tokens supported by the backend system. If the non ERC20 token address will specified in the Stage data, there is a risk that it will harm the functionality of the minting process.
Simultaneous Support for Multiple Stages Within the Same Collection: The same NFT collection can be used in 3 different stages simultaneously. While it can be an intended behavior, it creates a risk that minters will prefer a less stricter set of rules offered by another minting stage and minting function.
Risk of Incorrect or Malicious Recipients: The recipient addresses for royalties, payouts, and fees are determined by the project owner. If incorrect or malicious addresses as well as smart-contracts without fallback or receive functions are set, either intentionally or due to human error, it could result in irreversible fund loss or misallocation.
Risk of Incorrect Cap Values for Minting and Supply: The per-wallet minting limits, maximum supply constraints, and other cap values are controlled by privileged users, such as the owner and collection administrators. If these limits are set too restrictively (e.g., too low), they can unnecessarily constrain user participation, hinder the intended functionality of the system, and negatively impact the overall user experience.
Risk of Extremely Large Quantity Numbers: Extremely large quantity
amounts (e.g. > 5000) may result in some marketplaces and indexers to drop some Transfer
events, and cause some mints to not appear.
Findings
Code ― | Title | Status | Severity | |
---|---|---|---|---|
F-2025-8670 | Unsafe Minting in ERC721Drop Contract Can Permanently Lock NFTs | accepted | Medium | |
F-2025-8625 | Lack Of Storage Gaps for Base Contracts Can Lead To Collisions | mitigated | Low | |
F-2025-8674 | Missing maxSupply Enforcement in mint() Function Allows Exceeding Token Supply Limit | fixed | Low | |
F-2025-8624 | Insufficient Validation of Royalty Percentage in _updateRoyalties() function | fixed | Low | |
F-2025-8626 | Lack of _disableInitializers() Call in Constructor of Upgradeable Contracts Exposes System to Unauthorized Initialization | accepted | Observation | |
F-2025-8682 | Missing Validation for Empty merkleRoot in Allowlist Minting | fixed | Observation | |
F-2025-8673 | Missing Zero Address Validation | fixed | Observation | |
F-2025-8672 | Missing Array Length Validation in airdrop() Function Can Cause Out-of-Bounds Errors | fixed | Observation | |
F-2025-8668 | Redundant NatSpec Parameters in DropStructs.sol | fixed | Observation | |
F-2025-8623 | Potential Loss of Ownership Control in Contract Using OwnableUpgradeable | accepted | Observation |
Identify vulnerabilities in your smart contracts.
Appendix 1. Definitions
Severities
When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.
Reference on how risk scoring is done is available through the repository in our Github organization:
Severity | Description |
---|---|
Critical | Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation. |
High | High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation. |
Medium | Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category. |
Low | Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution. |
Severity
- Critical
Description
- Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.
Severity
- High
Description
- High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.
Severity
- Medium
Description
- Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.
Severity
- Low
Description
- Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
Potential Risks
The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.
Appendix 2. Scope
The scope of the project includes the following smart contracts from the provided repository:
Scope Details | |
---|---|
Repository | https://github.com/blank-development/mintdash-contracts→ |
Commit | 0d6dca0a13f52c630b51daab15def584f422252c |
Retest Commit | 370f2ec0b50b947166a1614d8cd913e184b731e5 |
Whitepaper | N/A |
Requirements | N/A |
Technical Requirements | N/A |
Scope Details
- Commit
- 0d6dca0a13f52c630b51daab15def584f422252c
- Retest Commit
- 370f2ec0b50b947166a1614d8cd913e184b731e5
- Whitepaper
- N/A
- Requirements
- N/A
- Technical Requirements
- N/A
Assets in Scope
core/ERC721DropMetadata.sol
core/interface/IERC721DropMetadata.sol
core/interface/IPayout.sol
core/Payout.sol
ERC721Drop.sol
ERC721DropMinter.sol
interface/IERC721Drop.sol
interface/IERC721DropMinter.sol
lib/DropStructs.sol
Appendix 3. Additional Valuables
Verification of System Invariants
During the audit of Onchain Foundation], Hacken followed its methodology by performing fuzz-testing on the project's main functions. [Echidna →, a tool used for fuzz-testing, was employed to check how the protocol behaves under various inputs. Due to the complex and dynamic interactions within the protocol, unexpected edge cases might arise. Therefore, it was important to use fuzz-testing to ensure that several system invariants hold true in all situations.
Fuzz-testing allows the input of many random data points into the system, helping to identify issues that regular testing might miss. A specific Echidna fuzzing suite was prepared for this task, and throughout the assessment, 10 invariants were tested over 5,000,000 runs. This thorough testing ensured that the system works correctly even with unexpected or unusual inputs.
Invariant | Test Result | Run Count |
---|---|---|
ERC721DropMinter::mintPublic() should always increase totalSupply and totalMinted values. | Passed | 5M |
ERC721DropMinter::mintPublic() should always revert if the msg.sender is not in the allowedPayers . | Passed | 5M |
ERC721DropMinter::mintPublic() should never revert if the NFT quantity does not exceed the maxSupply . | Passed | 5M |
ERC721DropMinter::updatePayer() should always update the payer successfully if the payer is not address(0). | Passed | 5M |
ERC721DropMinter::updatePlatformFeeRecipient() should never revert if the feeRecipient is not address(0). | Passed | 5M |
ERC721DropMinter::updateCollectionPlatformFee() should always update the collection platform fee if the fee does not exceed the FEE_DENOMINATOR . | Passed | 5M |
ERC721DropMinter::updateDefaultPlatformFee() should always update the collection platform fee if the fee does not exceed the FEE_DENOMINATOR . | Passed | 5M |
ERC721Drop::airdrop() should always distribute the tokens successfully if the totalMinted does not exceed the maxSupply limit. | Passed | 5M |
ERC721Drop::updateMaxSupply() should always update the maxSupply successfully if the newMaxSupply is not greater than 2 ** 64 - 1 . | Passed | 5M |
ERC721Drop::updateRoyalties() should always update the default royalties successfully if the new feeNumerator is not greater than 10000. | Passed | 5M |
Invariant
ERC721DropMinter::mintPublic()
should always increasetotalSupply
andtotalMinted
values.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::mintPublic()
should always revert if the msg.sender is not in theallowedPayers
.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::mintPublic()
should never revert if the NFT quantity does not exceed themaxSupply
.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::updatePayer()
should always update the payer successfully if the payer is not address(0).Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::updatePlatformFeeRecipient()
should never revert if the feeRecipient is not address(0).Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::updateCollectionPlatformFee()
should always update the collection platform fee if the fee does not exceed theFEE_DENOMINATOR
.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721DropMinter::updateDefaultPlatformFee()
should always update the collection platform fee if the fee does not exceed theFEE_DENOMINATOR
.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721Drop::airdrop()
should always distribute the tokens successfully if thetotalMinted
does not exceed themaxSupply
limit.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721Drop::updateMaxSupply()
should always update themaxSupply
successfully if thenewMaxSupply
is not greater than2 ** 64 - 1
.Test Result
- Passed
Run Count
- 5M
Invariant
ERC721Drop::updateRoyalties()
should always update the default royalties successfully if the newfeeNumerator
is not greater than 10000.Test Result
- Passed
Run Count
- 5M
Additional Recommendations
The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.
To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:
Detecting changes to ownership or critical permissions.
Monitoring large or unexpected transactions and minting events.
Pausing operations when irregularities are identified.
These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.