Project

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

Unexpected Effects in Profit Calculation from Tier Re-Indexing

Summary

Modifying the tier configurations through the updateDAOMembership function can disrupt the fixed weight assignments in the shareOf function, leading to inaccurate and unfair profit distributions among DAO members.

Vulnerability Details

The shareOf function in the MembershipERC1155 contract calculates each member's share of profits based on a fixed weight mapping tied to specific tier indices:

/// @notice Calculates the share of total profits for an account
/// @param account The account to query
/// @return The weighted share of the account
function shareOf(address account) public view returns (uint256) {
return (balanceOf(account, 0) * 64) +
(balanceOf(account, 1) * 32) +
(balanceOf(account, 2) * 16) +
(balanceOf(account, 3) * 8) +
(balanceOf(account, 4) * 4) +
(balanceOf(account, 5) * 2) +
balanceOf(account, 6);
}

This approach assumes that tier indices are static and correspond to predefined weights. However, the updateDAOMembership function in the MembershipFactory contract allows DAO administrators to reconfigure tiers, including adding, removing, or reordering them:

/// @notice Updates the tier configurations for a specific DAO
/// @param ensName The ENS name of the DAO
/// @param tierConfigs The new tier configurations
/// @return The address of the updated DAO
function updateDAOMembership(string calldata ensName, TierConfig[] memory tierConfigs)
external onlyRole(EXTERNAL_CALLER) returns (address) {
address daoAddress = getENSAddress[ensName];
require(tierConfigs.length <= TIER_MAX, "Invalid tier count.");
require(tierConfigs.length > 0, "Invalid tier count.");
require(daoAddress != address(0), "DAO does not exist.");
DAOConfig storage dao = daos[daoAddress];
if(dao.daoType == DAOType.SPONSORED){
require(tierConfigs.length == TIER_MAX, "Invalid tier count.");
}
uint256 maxMembers = 0;
// Preserve minted values and adjust the length of dao.tiers
for (uint256 i = 0; i < tierConfigs.length; i++) {
if (i < dao.tiers.length) {
tierConfigs[i].minted = dao.tiers[i].minted;
}
}
// Reset and update the tiers array
delete dao.tiers;
for (uint256 i = 0; i < tierConfigs.length; i++) {
dao.tiers.push(tierConfigs[i]);
maxMembers += tierConfigs[i].amount;
}
// updating the ceiling limit acc to new data
if(maxMembers > dao.maxMembers){
dao.maxMembers = maxMembers;
}
dao.noOfTiers = tierConfigs.length;
return daoAddress;
}

When tiers are reconfigured—such as being reordered or having tiers added/removed—the fixed weight assignments in shareOf no longer align with the new tier structure. This misalignment can result in incorrect profit distributions, where members of higher tiers may receive less profit than intended, and members of lower tiers may receive more.

To replicate and visualize the attack in reference, consider the structure of the DAO with below guidelines:

  • DAO has three tiers with indices 0, 1, and 2, assigned weights 64, 32, and 16 respectively.

  • An administrator calls updateDAOMembership to reorder tiers, placing the highest tier at index 2 instead of 0.

  • The shareOf function now assigns a lower weight to the new index 2 (previously index 0), reducing the profit share of higher-tier members.

// Original tier order
// Tier 0: Weight 64 (Highest)
// Tier 1: Weight 32
// Tier 2: Weight 16 (Lowest)
// Reordered tier configuration
// Tier 0: Weight 16 (Previously Tier 2)
// Tier 1: Weight 32
// Tier 2: Weight 64 (Previously Tier 0)
// shareOf now assigns:
// balanceOf(account, 0) * 64 --> Actually Tier 2's balance * 64
// balanceOf(account, 2) * 16 --> Actually Tier 0's balance * 16
  • This results in higher-tier members receiving less profit and lower-tier members receiving more than intended.

Impact

The system fails to maintain accurate and fair profit shares aligned with the current tier configurations. The fixed weight mapping does not adapt to dynamic changes in tier structures, leading to inconsistencies between tier definitions and profit calculations. Therefore, misaligned profit shares can disrupt the intended economic incentives within the DAO, potentially affecting member engagement and participation.

As the attack completion requires access to the EXTERNAL_CALLER role, even without a malicious intent, administrative errors in tier reconfiguration can cause profit distribution discrepancies.

Tools Used

Manual Review

Recommendations

Modify the shareOf function to reference dynamic weight assignments based on the current tier configuration rather than fixed indices. For that, further store weights within the DAOConfig structure to ensure they align with the actual tier positions.

struct DAOConfig {
string ensname;
DAOType daoType;
TierConfig[] tiers;
+ mapping(uint256 => uint256) tierWeights; // Dynamic weights
address currency;
uint256 maxMembers;
uint256 noOfTiers;
//joined members check
}
function shareOf(address account) public view returns (uint256) {
uint256 totalShare = 0;
DAOConfig storage dao = daos[daoMembershipAddress];
for (uint256 i = 0; i < dao.noOfTiers; i++) {
totalShare += balanceOf(account, i) * dao.tierWeights[i];
}
return totalShare;
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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