When a member of a DAO tries to upgrade tier, two membership token is burned and one is minted for the upgraded tier, But the TierConfig[].minted variable is not tracking this, which in turn creates Denail of service for low tiers if it is once filled, later burned through upgrading.
A Dao have 7 tiers, [0,1,2,3,4,5,6], Tier[6] can have at most two tokens (Tier[6].amount=2). A user have two tokens of Tier[6]. If he upgrade tier then he would have one token of Tier[5] and zero token of Tier[6]. Because two tokens of Tier[6] have been burned and one token is minted of Tier[5].
Now if he tries to get Tier[6] token, then he can not get it because Tier[6].minted variable is not updating.And it is still 2, but expected to be zero.
This can become Denail of service if once a lower tier bcomes filled. Later, if it becomes availabe or even empty through upgradation no one can still buy this membership token.
pragma solidity ^0.8.22;
import "forge-std/Test.sol";
import "../contracts/dao/CurrencyManager.sol";
import "../contracts/dao/MembershipFactory.sol";
import "../contracts/dao/libraries/MembershipDAOStructs.sol";
import "../contracts/dao/tokens/MembershipERC1155.sol";
import { OWPERC20 } from "../contracts/shared/testERC20.sol";
contract MembershipFactoryTest is Test {
CurrencyManager public currencyManager;
MembershipERC1155 public membershipImplementation;
MembershipFactory public membershipFactory;
address public daoMembershipAddress;
OWPERC20 public testERC20;
address public owner = makeAddr("owner");
address public DAOCreator = makeAddr("DAOCreator");
address public member1 = makeAddr("member1");
DAOConfig public daoconfig;
DAOInputConfig public daoConfig;
TierConfig[] public tierConfig;
function setUp() public {
vm.startBroadcast(owner);
currencyManager = new CurrencyManager();
membershipImplementation = new MembershipERC1155();
testERC20 = new OWPERC20("OWP", "OWP");
currencyManager.addCurrency(address(testERC20));
testERC20.mint(member1, 10000000000000000000);
membershipFactory = new MembershipFactory(
address(currencyManager),
address(owner),
"https://baseuri.com/",
address(membershipImplementation)
);
vm.stopBroadcast();
}
function testUpgadingTier() public {
vm.startBroadcast(owner);
daoConfig = DAOInputConfig({
ensname: "testdao.eth",
daoType: DAOType.SPONSORED,
currency: address(testERC20),
maxMembers: 100,
noOfTiers: 7
});
tierConfig.push(
TierConfig({ price: 300, amount: 3, minted: 0, power: 12 })
);
tierConfig.push(TierConfig({ price: 200, amount: 3, minted: 0, power: 6 }));
tierConfig.push(TierConfig({ price: 100, amount: 3, minted: 0, power: 3 }));
tierConfig.push(TierConfig({ price: 100, amount: 3, minted: 0, power: 3 }));
tierConfig.push(TierConfig({ price: 100, amount: 3, minted: 0, power: 3 }));
tierConfig.push(TierConfig({ price: 100, amount: 3, minted: 0, power: 3 }));
tierConfig.push(TierConfig({ price: 100, amount: 2, minted: 0, power: 3 }));
daoMembershipAddress = membershipFactory.createNewDAOMembership(
daoConfig,
tierConfig
);
vm.stopBroadcast();
vm.startBroadcast(member1);
testERC20.approve(address(membershipFactory), 1000000000);
membershipFactory.joinDAO(daoMembershipAddress, 5);
membershipFactory.joinDAO(daoMembershipAddress, 6);
membershipFactory.joinDAO(daoMembershipAddress, 6);
membershipFactory.upgradeTier(daoMembershipAddress, 6);
TierConfig[] memory tier = membershipFactory.tiers(daoMembershipAddress);
console.log(tier[6].minted);
console.log(tier[5].minted);
vm.expectRevert("Tier full.");
membershipFactory.joinDAO(daoMembershipAddress, 6);
vm.stopBroadcast();
}
}
Likelihood is High, and Impact is Medium as lower tiers can become unusable when, they really have zero members or zero tokens. Thus the DAO as a whole become mostly unusable.