Project

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

`platformFees`'s are bypassable when joining a DAO with `joinDAO` function

Summary

When users join a dao with the joinDAO function, they are expected to pay the price associated with the tier they are joining. platformFees's are taken from this amount that is being paid. However, when the price of a tier is low enough, platformFees's will return 0 and will be bypassed.

Vulnerability Details

Users join a DAO with 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.");
uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
uint256 platformFees = (20 * tierPrice) / 100;
daos[daoMembershipAddress].tiers[tierIndex].minted += 1;
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), daoMembershipAddress, tierPrice - platformFees);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), tierIndex, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, tierIndex);
}

As observed in this function, platformFees are 20% of the tierPrice. However, when the tierPrice is low enough, due to how solidity handles divisions, platformFees will return 0 when tierPrice < 5.
Consider the scenario of a DAO with 7 tiers and tier prices are set as shown below:

  • Tier 1 = 0.00000256 WBTC (At the time this is worth approximately 0.2 USD)

  • Tier 2 = 0.00000128 WBTC

  • ...

  • Tier 7 = 0.00000004 WBTC

When a user joins Tier 7, the platformFees will be calculated as (20 * 4) / 100 = 0, meaning that platform will not be taking any fees from this transaction when they should be taking 20%. After joining Tier 7, users can now call the upgradeTier function to fill out the remanining tiers while still paying no fees to the protocol.

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

We can calculate the amount of fees in USD that the protocol has missed out as such when amount of users allowed in Tier 1 is 16, Tier 2 is 32...:

((0.2 * 16) * 7) / 20 = 1.12

Keep in mind that gas fees in Polygon are low and WBTC price in USD keeps rising, potentially making this vulnerability more costly for the protocol and even more realistic to happen in future. Amount of users that can be in a tier can be higher too, making protocol miss out on more fees

Impact

protocolFees are bypassable and will not be paid.

Impact: Medium, protocol will miss out on fees.
Likelihood: Low, there is nothing stopping this vulnerability. However, at the time of the audit WBTC's price makes this not so likely to happen.

Tools Used

Manual review

Recommendations

Implement a minimum fee when the protocolFees is 0. An example is shown below.

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;
++ if (platformFees == 0){
platformFees = 1;
}
daos[daoMembershipAddress].tiers[tierIndex].minted += 1;
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), daoMembershipAddress, tierPrice - platformFees);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), tierIndex, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, tierIndex);
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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