Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Tier Capacity Limit Bypass in DAO Membership Upgrade Function

Summary

The upgradeTier function in MembershipFactory contract lacks validation for tier capacity limits when minting tokens to a higher tier. This allows the minting of membership tokens beyond a tier's configured capacity, violating core DAO membership constraints.

Vulnerability Details

The upgradeTier function in MembershipFactory contract allows users to upgrade their tier within a sponsored DAO by burning tokens from a lower tier and minting tokens in a higher tier. However, it fails to validate the target tier's capacity limits before minting:

function upgradeTier(address daoMembershipAddress, uint256 fromTierIndex) external {
require(daos[daoMembershipAddress].daoType == DAOType.SPONSORED, "Upgrade not allowed.");
require(daos[daoMembershipAddress].noOfTiers >= fromTierIndex + 1, "No higher tier available.");
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 2);
@> IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), fromTierIndex - 1, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, fromTierIndex - 1);
}

The key issue:
Before minting to the target tier (fromTierIndex - 1), the function does not validate if the tier has available capacity by checking tiers[fromTierIndex - 1].amount > tiers[fromTierIndex - 1].minted. Without this check, the function will mint tokens even if the target tier is already at its maximum capacity.

The ERC1155 token standard has no built-in maximum supply limit per token ID, so this business rule must be enforced at the factory contract level. Currently, this lack of validation allows the creation of membership tokens beyond the DAO's configured tier limits.

For context, the tier capacity check is properly implemented in the joinDAO function:

function joinDAO(address daoMembershipAddress, uint256 tierIndex) external {
require(daos[daoMembershipAddress].noOfTiers > tierIndex, "Invalid tier.");
require(daos[daoMembershipAddress].tiers[tierIndex].amount > daos[daoMembershipAddress].tiers[tierIndex].minted, "Tier full.");
// ... rest of the function
}

This inconsistency means tier limits are enforced for direct joining but can be bypassed through the upgrade path.

Impact

The lack of tier capacity validation in the upgrade path can lead to:

1.Violation of DAO Tier Structure:

  • Members can be minted into tiers that are already at maximum capacity

  • This breaks the carefully designed membership distribution model

  • The tier's amount parameter becomes ineffective as a capacity limit for upgrades

2.Inconsistent Access Control:

  • While tier limits are enforced for direct joining via joinDAO, they can be bypassed through upgradeTier

  • This creates an unfair advantage for existing members who can upgrade

  • Undermines the DAO's ability to maintain planned tier distributions

3.Business Logic Violation:

  • DAOs may have specific reasons for tier capacity limits (e.g., voting power distribution, reward allocation)

  • Bypassing these limits could affect governance mechanisms

  • Future protocol features that rely on tier distribution assumptions may malfunction

The impact is significant as it affects the core business logic of DAO membership management, though it does not directly compromise token security or enable theft of assets.

Tools Used

Manual Review

Recommendations

Add tier capacity validation before minting in the upgradeTier function:

function upgradeTier(address daoMembershipAddress, uint256 fromTierIndex) external {
require(daos[daoMembershipAddress].daoType == DAOType.SPONSORED, "Upgrade not allowed.");
require(daos[daoMembershipAddress].noOfTiers >= fromTierIndex + 1, "No higher tier available.");
+ require(daos[daoMembershipAddress].tiers[fromTierIndex - 1].amount > daos[daoMembershipAddress].tiers[fromTierIndex - 1].minted, "Target tier is full");
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 2);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), fromTierIndex - 1, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, fromTierIndex - 1);
}

This validation ensures:

  1. Target tier capacity limits are enforced consistently across all membership paths

  2. The DAO's tier structure and distribution model remains intact

  3. Alignment with the capacity validation already present in joinDAO

Updates

Lead Judging Commences

0xbrivan2 Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.