Project

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

Pricing Does Not Follow Binary Relationship

Summary

The per-tier pricing strategy configured by DAO deployers is customizable, but the representative claim to those tiers are fixed.

Vulnerability Details

DAOs in OWP are operated by holders of MembershipERC1155s, which is split into multiple per-token tiers:

function mint(address to, uint256 tokenId, uint256 amount) external override onlyRole(OWP_FACTORY_ROLE) {
totalSupply += amount * 2 ** (6 - tokenId); // Update total supply with weight
_mint(to, tokenId, amount, "");
}

Each tokenId corresponds to a tier.

Notice that the totalSupply is computed as a function of the tokenId, the smaller the tokenId, the larger the relative increase in totalSupply - this is because each token corresponds to a proportional claim to shares (and subsequently the profit share):

function shareOf(address account) public view returns (uint256) {
return (balanceOf(account, 0) * 64) + /// @audit 64x
(balanceOf(account, 1) * 32) +
(balanceOf(account, 2) * 16) +
(balanceOf(account, 3) * 8) +
(balanceOf(account, 4) * 4) +
(balanceOf(account, 5) * 2) +
balanceOf(account, 6); /// @audit 1x
}

https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/tokens/MembershipERC1155.sol#L166C5-L177C6

This behaviour, where a token possesses a fixed claim to the DAO funds inversely proportional to 2 ** tokenId, is common across all collections.

This means there is an intrinsic underlying exchange rate for all tokenIds.

However, this implicit pricing mechanism is not respected when creating tiered tokens:

for (uint256 i = 0; i < tierConfigs.length; i++) {
require(tierConfigs[i].minted == 0, "Invalid tier config");
dao.tiers.push(tierConfigs[i]); /// @audit Adds a new tier.
}

https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/MembershipFactory.sol#L85C9-L88C10

struct TierConfig {
uint256 amount;
uint256 price; /// @audit a tier can have any price chosen by the creator.
uint256 power;
uint256 minted;

Here, the creator can opt to specify any price per tier, meaning that even rational pricing systems are liable to unfair value extraction if they do not respect the power-of-two law:

uint256 priceTier0 = 7 ether; /// @audit Claims 64x underlying compared to `priceTier6`.
uint256 priceTier1 = 6 ether;
uint256 priceTier2 = 5 ether;
uint256 priceTier3 = 4 ether;
uint256 priceTier4 = 3 ether;
uint256 priceTier5 = 2 ether;
uint256 priceTier6 = 1 ether;

Therefore any pricing distribution which does not exhibit at least exhibit power of two growth will be liable to value extraction.

Impact

Inefficiencies in pricing structure can be exploited to gain an unfair market share.

Tools Used

Manual Review

Recommendations

When creating a new DAO, omit the price field and instead use a token price multiplier:

require(daos[daoMembershipAddress].tiers[tierIndex].amount > daos[daoMembershipAddress].tiers[tierIndex].minted, "Tier full.");
- uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
+ uint256 tierPrice = (2 ** (daos[daoMembershipAddress].noOfTiers - (tierIndex + 1))) * daos[daoMembershipAddress].priceMultiplier;
uint256 platformFees = (20 * tierPrice) / 100;
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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