Project

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

Protocol Fee Bypass via Tier Upgrade Mechanism

Summary

The upgradeTier function in MembershipFactory allows users to bypass platform fees by joining at lowest tier and upgrading to higher tiers without paying additional fees, causing significant protocol revenue loss.

Vulnerability Details

In MembershipFactory.sol, the upgradeTier mechanism fails to collect platform fees during tier upgrades in sponsored DAOs:

// Initial join with minimal fees
function joinDAO(address daoMembershipAddress, uint256 tierIndex) external {
uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
uint256 platformFees = (20 * tierPrice) / 100; // Small fee for lowest tier
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
// ...
}
// Upgrade without any fee collection
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);
}

Attack path:

  1. Join at tier 6 (lowest) paying minimal platform fees

  2. Upgrade to higher tiers without additional fee payment

  3. Gain higher tier benefits and profit share weights (64x, 32x, 16x, etc.)

  4. Avoid substantial platform fees meant for higher tiers

Impact

  • Direct protocol revenue loss from bypassed platform fees

  • Example loss calculation:

    • Tier 6 entry: 27.5 USDT (5.5 USDT fee)

    • Should pay for Tier 5: 275 USDT (55 USDT fee)

    • Lost fees per upgrade: ~50 USDT

  • Creates unfair advantage over users paying full fees

  • More severe in sponsored DAOs due to mandatory 7-tier structure

Tools Used

  • Manual code review

Recommendations

Add fee collection to upgradeTier:

function upgradeTier(address daoMembershipAddress, uint256 fromTierIndex) external {
require(daos[daoMembershipAddress].daoType == DAOType.SPONSORED, "Upgrade not allowed.");
uint256 toTierIndex = fromTierIndex - 1;
require(daos[daoMembershipAddress].noOfTiers >= fromTierIndex + 1, "No higher tier available.");
// Collect price difference and fees
uint256 priceDiff = daos[daoMembershipAddress].tiers[toTierIndex].price - daos[daoMembershipAddress].tiers[fromTierIndex].price;
uint256 platformFees = (20 * priceDiff) / 100;
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), daoMembershipAddress, priceDiff - platformFees);
IMembershipERC1155(daoMembershipAddress).burn(_msgSender(), fromTierIndex, 2);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), toTierIndex, 1);
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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