Q1 2025 Web3 Security ReportAccess control failures led to $1.63 billion in losses
Discover report insights
  • Hacken
  • Audits
  • onchain-foundation
  • [SCA] Onchain Foundation / Mintdash Contracts / Jan2025
Onchain Foundation logo

Onchain Foundation

Audit name:

[SCA] Onchain Foundation / Mintdash Contracts / Jan2025

Date:

Mar 19, 2025

Table of Content

Introduction
Audit Summary
System Overview
Potential Risks
Findings
Appendix 1. Definitions
Appendix 2. Scope
Appendix 3. Additional Valuables
Disclaimer

Want a comprehensive audit report like this?

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

NameSmart Contract Code Review and Security Analysis Report for Onchain Foundation
Audited ByViktor Lavrenenko
Approved ByGrzegorz Trawinski
Websitehttps://onchain.org/
Changelog07/02/2025 - Preliminary Report
19/03/2025 - Final Report
PlatformEthereum, Arbitrum, Polygon, Base, BSC
LanguageSolidity
TagsERC2981, NFT, Royalties, Airdrop, MerkleProof
Methodologyhttps://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
    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

Audit Summary

10Total Findings
6Resolved
3Accepted
1Mitigated

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 of ERC721Drop 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(), and ERC721DropMinter::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() and transferOwnership() 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() and withdrawTokens() 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-8670Unsafe Minting in ERC721Drop Contract Can Permanently Lock NFTs
accepted

Medium
F-2025-8625Lack Of Storage Gaps for Base Contracts Can Lead To Collisions
mitigated

Low
F-2025-8674Missing maxSupply Enforcement in mint() Function Allows Exceeding Token Supply Limit
fixed

Low
F-2025-8624Insufficient Validation of Royalty Percentage in _updateRoyalties() function
fixed

Low
F-2025-8626Lack of _disableInitializers() Call in Constructor of Upgradeable Contracts Exposes System to Unauthorized Initialization
accepted

Observation
F-2025-8682Missing Validation for Empty merkleRoot in Allowlist Minting
fixed

Observation
F-2025-8673Missing Zero Address Validation
fixed

Observation
F-2025-8672Missing Array Length Validation in airdrop() Function Can Cause Out-of-Bounds Errors
fixed

Observation
F-2025-8668Redundant NatSpec Parameters in DropStructs.sol
fixed

Observation
F-2025-8623Potential Loss of Ownership Control in Contract Using OwnableUpgradeable
accepted

Observation
1-10 of 10 findings

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

Repositoryhttps://github.com/blank-development/mintdash-contracts
Commit0d6dca0a13f52c630b51daab15def584f422252c
Retest Commit370f2ec0b50b947166a1614d8cd913e184b731e5
WhitepaperN/A
RequirementsN/A
Technical RequirementsN/A

Assets in Scope

core
ERC721DropMetadata.sol - core/ERC721DropMetadata.sol
interface
IERC721DropMetadata.sol - core/interface/IERC721DropMetadata.sol
IPayout.sol - core/interface/IPayout.sol
Payout.sol - core/Payout.sol
ERC721Drop.sol - ERC721Drop.sol
ERC721DropMinter.sol - ERC721DropMinter.sol
interface
IERC721Drop.sol - interface/IERC721Drop.sol
IERC721DropMinter.sol - interface/IERC721DropMinter.sol
lib
DropStructs.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.Passed5M
ERC721DropMinter::mintPublic() should always revert if the msg.sender is not in the allowedPayers.Passed5M
ERC721DropMinter::mintPublic() should never revert if the NFT quantity does not exceed the maxSupply.Passed5M
ERC721DropMinter::updatePayer() should always update the payer successfully if the payer is not address(0).Passed5M
ERC721DropMinter::updatePlatformFeeRecipient() should never revert if the feeRecipient is not address(0).Passed5M
ERC721DropMinter::updateCollectionPlatformFee() should always update the collection platform fee if the fee does not exceed the FEE_DENOMINATOR.Passed5M
ERC721DropMinter::updateDefaultPlatformFee() should always update the collection platform fee if the fee does not exceed the FEE_DENOMINATOR.Passed5M
ERC721Drop::airdrop() should always distribute the tokens successfully if the totalMinted does not exceed the maxSupply limit.Passed5M
ERC721Drop::updateMaxSupply() should always update the maxSupply successfully if the newMaxSupply is not greater than 2 ** 64 - 1.Passed5M
ERC721Drop::updateRoyalties() should always update the default royalties successfully if the new feeNumerator is not greater than 10000.Passed5M
  • Invariant

    ERC721DropMinter::mintPublic() should always increase totalSupply and totalMinted values.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721DropMinter::mintPublic() should always revert if the msg.sender is not in the allowedPayers.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721DropMinter::mintPublic() should never revert if the NFT quantity does not exceed the maxSupply.

    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 the FEE_DENOMINATOR.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721DropMinter::updateDefaultPlatformFee() should always update the collection platform fee if the fee does not exceed the FEE_DENOMINATOR.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721Drop::airdrop() should always distribute the tokens successfully if the totalMinted does not exceed the maxSupply limit.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721Drop::updateMaxSupply() should always update the maxSupply successfully if the newMaxSupply is not greater than 2 ** 64 - 1.

    Test Result

    Passed

    Run Count

    5M

    Invariant

    ERC721Drop::updateRoyalties() should always update the default royalties successfully if the new feeNumerator 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.

Disclaimer