Project

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

Tier Minted Tracking Not Updated During Tier Upgrades for Sponsored DAOs

Summary

The upgradeTier function in contract MembershipFactory fails to update the tier minting counts when users upgrade their tiers in sponsored DAOs. While the function correctly burns and mints the appropriate membership NFTs, it does not update the minted counter in the corresponding tier configurations. This leads to inaccurate tracking of tier availability and could potentially allow bypassing of tier membership limits.

Vulnerability Details

The issue exists in the upgradeTier function of the MembershipFactory contract:

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);
}

When a user upgrades their tier:

  1. Two tokens are burned from their current tier (fromTierIndex)

  2. One token is minted in the next higher tier (fromTierIndex - 1)

  3. However, the minted counter in daos[daoMembershipAddress].tiers[index].minted is not updated for either tier

This contrasts with the joinDAO function which properly maintains these counters:

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.");
// ... other code ...
daos[daoMembershipAddress].tiers[tierIndex].minted += 1;
// ... rest of the function
}

The minted counter is critical because it's used to enforce tier capacity limits, as seen in the require statement in joinDAO. When these counters become out of sync with the actual number of minted tokens, the tier capacity enforcement mechanism becomes unreliable.

Impact

The failure to update tier minting counts during upgrades has several implications:

1.Inaccurate Tier Availability Tracking

  • Lower tiers may appear to have more availability than they actually do, as burned tokens aren't decremented from the minted count

  • Higher tiers may appear full when they actually have space available, as newly minted tokens aren't reflected in the minted count

2.Potential Tier Limit Bypass

  • If a tier's minted count becomes significantly out of sync with reality, users might be able to join tiers that should be full

  • This could lead to tiers exceeding their intended capacity limits set by amount

3.Inconsistent State

  • The actual number of NFTs minted (tracked by ERC1155) will not match the DAO's internal tracking

  • This makes it difficult for the protocol to maintain accurate membership statistics and could affect governance mechanisms that rely on tier information

4.Administrative Confusion

  • DAO administrators may make decisions based on incorrect tier occupancy data

  • This could affect pricing strategies, membership drives, or other DAO operations

Tools Used

Manual Review

Recommendations

The upgradeTier function should be modified to properly update the tier counters. Here's the recommended fix:

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.");
// Update the minted counts before burning/minting
+ daos[daoMembershipAddress].tiers[fromTierIndex].minted -= 2;
+ daos[daoMembershipAddress].tiers[fromTierIndex - 1].minted += 1;
// Perform the token operations
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 2);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), fromTierIndex - 1, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, fromTierIndex - 1);
}
Updates

Lead Judging Commences

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

Support

FAQs

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