Project

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

Lack of Minted Count Update in upgradeTier Function Causes Critical Membership Limit Enforcement Failure, Allowing Unlimited Tier Exceedances

Summary

The absence of minted count updates in the upgradeTier function leads to a critical accounting error in the membership tracking system. This vulnerability allows the contract to lose synchronization between the actual minted NFTs and the recorded minted count, effectively breaking the membership limits enforcement. When combined with the burn and mint operations, this desynchronization can result in the contract's ability to exceed the intended tier capacity limits, potentially undermining the entire membership tier structure and its associated economic model.

The issue lies in the upgradeTier function's implementation where it performs NFT burns and mints without correspondingly updating the minted count in the DAO's tier configuration. The contract maintains a TierConfig struct for each tier that includes a minted counter, which is crucial for enforcing tier capacity limits. While the joinDAO function correctly increments this counter, the upgradeTier function fails to maintain this state, creating a divergence between the actual token supply and the recorded minted count.

https://github.com/Cyfrin/2024-11-one-world/blob/main/contracts/dao/MembershipFactory.sol#L155

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

Consider a scenario where a tier has a maximum capacity of 100 memberships and currently shows 100 minted tokens. When a user upgrades from this tier, their token is burned, but the minted count remains at 100. Subsequently, the contract's tier enforcement checks would prevent new joins to this tier (as minted = max), even though the actual supply is now 99. Conversely, when minting to the new tier, the minted count isn't incremented, allowing the tier to potentially exceed its intended capacity limit. Over time, as more users upgrade tiers, this desynchronization compounds, potentially allowing an unlimited number of mints to what should be capacity-restricted tiers.

Recommended Fix

function upgradeTier(address daoMembershipAddress, uint256 fromTierIndex) external {
DAOConfig storage dao = daos[daoMembershipAddress];
require(dao.daoType == DAOType.SPONSORED, "Upgrade not allowed.");
uint256 targetTierIndex = fromTierIndex - 1;
require(targetTierIndex >= 0, "Already at highest tier.");
// Verify target tier has capacity
require(dao.tiers[targetTierIndex].amount > dao.tiers[targetTierIndex].minted, "Target tier full.");
// Update minted counts before token operations
dao.tiers[fromTierIndex].minted -= 1;
dao.tiers[targetTierIndex].minted += 1;
// Perform token operations
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 1);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), targetTierIndex, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, targetTierIndex);
}

The fix ensures proper state synchronization by updating the minted counts for both the source and target tiers. It also adds a capacity check for the target tier before proceeding with the upgrade. This maintains the integrity of the tier system and ensures that membership limits are properly enforced throughout the upgrade process.

Updates

Lead Judging Commences

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

Support

FAQs

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