Summary
The profit-sharing mechanism uses hardcoded weights for each tier, ignoring the customizable power values set during DAO creation. This results in incorrect profit distribution when DAOs have custom tier configurations.
Vulnerability Details
In the MembershipERC1155
contract, the shareOf
function calculates a user's share of profits based on hardcoded weights:
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);
}
However, during DAO creation, tiers can be configured with custom power values:
tierConfig[0] = TierConfig({price: 3200, amount: 320, minted: 0, power: 32});
tierConfig[1] = TierConfig({price: 1600, amount: 160, minted: 0, power: 16});
tierConfig[2] = TierConfig({price: 800, amount: 80, minted: 0, power: 8});
The shareOf
function does not use these custom power values, leading to incorrect calculations of users' profit shares.
POC
function testShareOfDoesNotAccountForActualTierPower() public {
TierConfig[] memory tierConfig = new TierConfig[]();
tierConfig[0] = TierConfig({price: 3200, amount: 320, minted: 0, power: 32});
tierConfig[1] = TierConfig({price: 1600, amount: 160, minted: 0, power: 16});
tierConfig[2] = TierConfig({price: 800, amount: 80, minted: 0, power: 8});
MockUsdc mockUsdc = new MockUsdc();
DAOInputConfig memory daoConfig = DAOInputConfig({
ensname: "testdao.eth",
daoType: DAOType.PUBLIC,
currency: address(mockUsdc),
maxMembers: 1270,
noOfTiers: 3
});
currencyManager.addCurrency(address(mockUsdc));
address dao = membershipFactory.createNewDAOMembership(
daoConfig,
tierConfig
);
uint256 mintAmount = 1000 * 10 ** 18;
mockUsdc.mint(alice, mintAmount);
vm.startPrank(alice);
mockUsdc.approve(address(membershipFactory), 3200);
membershipFactory.joinDAO(dao, 0);
mockUsdc.approve(address(membershipFactory), 1600);
membershipFactory.joinDAO(dao, 1);
mockUsdc.approve(address(membershipFactory), 800);
membershipFactory.joinDAO(dao, 2);
vm.stopPrank();
uint256 actualShare = MembershipERC1155(dao).shareOf(alice);
uint256 expectedUserProfit = (MembershipERC1155(dao).balanceOf(alice, 0) * 32) +
(MembershipERC1155(dao).balanceOf(alice, 1) * 16) +
(MembershipERC1155(dao).balanceOf(alice, 2) * 8);
console.log(actualShare);
console.log(expectedUserProfit);
assert(actualShare >= expectedUserProfit * 2);
}
Impact
Users receive incorrect profit amounts, which can be significantly higher or lower than intended. This undermines the fairness and trust in the DAO, potentially causing disputes and dissatisfaction among members.
Tools Used
Manual review and foundry
Recommendations