Summary
the joinDAO
method checks for the minted
value of a tier before minting NFTs in the tier. However the NFTs minted in a tier are burned when they are used in upgrading to the next tier.
This means an user who has minted all the tier NFTS and then used it to upgrade to the next tier, will not be in posession of the NFT, and the NFT will not be in circulation as well.
However the end result is that no more tier NFTs for that particular tier can be minted again because of the minted
check in joinDAO
require(daos[daoMembershipAddress].tiers[tierIndex].amount > daos[daoMembershipAddress].tiers[tierIndex].minted, "Tier full.");
https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/MembershipFactory.sol#L142
This is in stark contrast to the case when the user buys an upgraded tier NFT in the first place, leaving the lower tiers unaffected.
Vulnerability Details
joinDAO checks for minted variable exceeding amount available for a tier NFT
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.");
uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
uint256 platformFees = (20 * tierPrice) / 100;
UpgradeTier doesn't change the minted
value of the tier amount when burning tier NFTs.
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);
}
Using this, an user can mint till the amount for a tier is reached, then upgradeTier. This will disallow anyone else from getting the initial tier minted.
Impact
No tier NFTs can be minted for tiers that are not the top-most tier (The tiers that have another tier to upgrade to are vulnerable)
Tools Used
manual analysis
Recommendations
update the minted value for the upgradeTier
method, that is,
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.");
+ daos[daoMembershipAddress].tiers[fromTierIndex].minted -= 2
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 2);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), fromTierIndex - 1, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, fromTierIndex - 1);
}