Project

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

Deprecation of DAO Tiers Renders Minted Tokens at Lower Levels Ineffective

Description

In cases where the updateDAOMembership function is used to modify public or private DAO tier configurations by reducing the number of tiers, minted tokens from lower tiers may become effectively valueless. For Public and Private DAOs, the absence of a tier update mechanism or compensation model results in holders of deprecated or eliminated lower-tier tokens experiencing a loss in both token utility and economic value. This is especially critical as users have initially purchased these tokens with an expectation of access, power and participation in DAO profits. The lack of continuity in the value and utility of these tokens could lead to user dissatisfaction and mistrust in the DAO’s governance.

Impact

  • Token holders in deprecated tiers may face a loss in their initial investment, as tokens minted in these lower tiers lose utility, power, and participation in DAO rewards and profits.

  • The inability to uphold token value and utility for holders could damage the DAO’s reputation, as members lose confidence in its ability to maintain equitable and transparent governance.

  • Members may perceive that the DAO is altering tier structures without adequate measures to protect their investments, which could lead to user attrition and reluctance from new members considering participation.

Tools Used

Manual code review and scenario analysis to identify potential impacts of updateDAOMembership on token value and utility

Potential Scenario

  1. A DAO initially has 7 tiers, with each tier offering a specific level of power and profit share, allowing users to mint tokens across all levels in exchange for specific tier benefits. Over time, the DAO decides to streamline its structure by reducing the number of active tiers, deprecating some of the lower levels

  2. Users who previously minted tokens in the now-deprecated lower tiers find themselves at a disadvantage. With the tier update, these tokens may no longer hold utility, reducing their weight in the DAO’s governance processes. Consequently, users who invested in these tokens lose access to the benefits or influence they initially paid for.

  3. Without a compensation mechanism, users who hold these devalued tokens face a loss on their initial investment, potentially leading to frustration and loss of trust in the DAO. This scenario poses a risk to the DAO’s as well as the protocol's reputation as it may appear to have disregarded the interests of its members, impacting user retention and community support

Proof of Concept

  • Install foundry in the project by running the following commands:

    1. Initialize git if not initialized:

    git init
    1. Install foundry:

    npm i --save-dev @nomicfoundation/hardhat-foundry --force
    1. add to hardhat.config.ts:

    require("@nomicfoundation/hardhat-foundry");
    1. Create foundry.toml

    npx hardhat init-foundry
  • Now create a new file in the test folder:

    test/DaoTierDeprecation.t.sol
  • Add these lines to the file:

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.20;
import {Test, console} from "../lib/forge-std/src/Test.sol";
import {MembershipERC1155} from "../contracts/dao/tokens/MembershipERC1155.sol";
import {MembershipFactory} from "../contracts/dao/MembershipFactory.sol";
import {CurrencyManager} from "../contracts/dao/CurrencyManager.sol";
import {DAOInputConfig, DAOType, TierConfig} from "../contracts/dao/libraries/MembershipDAOStructs.sol";
import {OWPERC20} from "../contracts/shared/testERC20.sol";
import {IMembershipERC1155} from "../contracts/dao/interfaces/IERC1155Mintable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
contract PoC is Test {
MembershipERC1155 public membershipERC1155;
MembershipFactory public membershipFactory;
CurrencyManager public currencyManager;
OWPERC20 public currency;
address opWallet;
address deployer;
address daoCreator;
address user;
bytes32 public constant OWP_FACTORY_ROLE = keccak256("OWP_FACTORY_ROLE");
bytes32 public constant DAO_CREATOR = keccak256("DAO_CREATOR");
function setUp() public {
opWallet = makeAddr("opWallet");
deployer = makeAddr("deployer");
daoCreator = makeAddr("daoCreator");
user = makeAddr("user");
vm.startPrank(deployer);
// Deploy MembershipERC1155
membershipERC1155 = new MembershipERC1155();
// Deploy CurrencyManager
currencyManager = new CurrencyManager();
currency = new OWPERC20("Currency", "CUR");
// Deploy MembershipFactory
membershipFactory = new MembershipFactory(address(currencyManager), opWallet, "baseUri", address(membershipERC1155));
currencyManager.addCurrency(address(currency));
vm.stopPrank();
}
function testTierDeprecation() public {
DAOInputConfig memory daoInputConfig = DAOInputConfig({
ensname: "Test.eth",
daoType: DAOType.PUBLIC,
currency: address(currency),
maxMembers: 200,
noOfTiers: 7
});
TierConfig[] memory tierConfigs = new TierConfig[]();
tierConfigs[0] = TierConfig({
amount: 10,
price: 3200,
power: 32,
minted: 0
});
tierConfigs[1] = TierConfig({
amount: 10,
price: 1600,
power: 16,
minted: 0
});
tierConfigs[2] = TierConfig({
amount: 10,
price: 800,
power: 8,
minted: 0
});
tierConfigs[3] = TierConfig({
amount: 10,
price: 400,
power: 4,
minted: 0
});
tierConfigs[4] = TierConfig({
amount: 10,
price: 200,
power: 2,
minted: 0
});
tierConfigs[5] = TierConfig({
amount: 10,
price: 100,
power: 1,
minted: 0
});
tierConfigs[6] = TierConfig({
amount: 10,
price: 50,
power: 1,
minted: 0
});
// Dao creator is creating a public dao with 7 tiers
vm.prank(daoCreator);
address proxy = membershipFactory.createNewDAOMembership(daoInputConfig, tierConfigs);
currency.mint(user, 1e20);
vm.startPrank(user);
currency.approve(address(membershipFactory), 1e20);
// user minting token at tier 7
membershipFactory.joinDAO(proxy, 6);
vm.stopPrank();
// Update DAO tier configuration to 4 tiers
TierConfig[] memory tierConfigsNew = new TierConfig[]();
tierConfigsNew[0] = TierConfig({
amount: 10,
price: 3200,
power: 32,
minted: 0
});
tierConfigsNew[1] = TierConfig({
amount: 10,
price: 1600,
power: 16,
minted: 0
});
tierConfigsNew[2] = TierConfig({
amount: 10,
price: 800,
power: 8,
minted: 0
});
tierConfigsNew[3] = TierConfig({
amount: 10,
price: 400,
power: 4,
minted: 0
});
// External caller is updating the DAO tier configuration
vm.prank(deployer);
membershipFactory.updateDAOMembership("Test.eth", tierConfigsNew);
// Now users token at tier 7 is no longer valid as the DAO has been updated to 4 tiers
// user cannot use that token to exercise any power or rights in the DAO even though he paid for it
// As there is no migration mechanism or upgrade tier mechanism for public and private DAO,
// the token will become valueless to the user
}
}

Run the test:

forge test --match-test testTierDeprecation

Recommended Mitigation

  1. Introduce a migration function to enable users holding tokens in deprecated tiers to exchange their tokens for equivalent value in existing or restructured tiers. This can be done by burning tokens in deprecated tiers and issuing new tokens in active tiers, preserving the initial value users invested. Ensure that the migration function maintains accurate balances and respects token power and profit attributes, thereby securing user investment in a fair manner.

  2. Before deprecating any tier, consider marking it as deprecated and giving users a grace period during which they can redeem or migrate their tokens to other tiers. This ensures a smoother transition for users while preserving their investments.

  3. For users unable or unwilling to migrate, consider establishing an equivalent utility in the DAO for deprecated tokens. This could include offering alternative benefits or converting old tokens into a different utility token within the DAO, thereby retaining some value and utility.

Updates

Lead Judging Commences

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

Support

FAQs

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