The following function source code highlights potential issues with tier configuration updates. As indicated by @>
markers, the tierConfigs.length
may not match dao.tiers.length
. Although minted
values in dao.tiers[i]
are synchronized when they overlap, cases where tierConfigs.length
is greater than dao.tiers.length
do not perform minted-related checks, potentially causing inaccurate updates. Furthermore, if tierConfigs.length
is less than dao.tiers.length
, the existing minted
data may be inadvertently overwritten.
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;
@> for (uint256 i = 0; i < tierConfigs.length; i++) {
@> if (i < dao.tiers.length) {
@> tierConfigs[i].minted = dao.tiers[i].minted;
}
}
delete dao.tiers;
for (uint256 i = 0; i < tierConfigs.length; i++) {
@> dao.tiers.push(tierConfigs[i]);
maxMembers += tierConfigs[i].amount;
}
if(maxMembers > dao.maxMembers){
dao.maxMembers = maxMembers;
}
dao.noOfTiers = tierConfigs.length;
return daoAddress;
}
pragma solidity 0.8.22;
import {Test} from "forge-std/Test.sol";
import {OWPERC20} from "contracts/shared/testERC20.sol";
import {MembershipFactory} from "contracts/dao/MembershipFactory.sol";
import {CurrencyManager} from "contracts/dao/CurrencyManager.sol";
import {MembershipERC1155} from "contracts/dao/tokens/MembershipERC1155.sol";
import { DAOConfig, DAOInputConfig, TierConfig, DAOType, TIER_MAX } from "contracts/dao/libraries/MembershipDAOStructs.sol";
contract MembershipFactoryPoc is Test {
MembershipFactory membershipFactory;
CurrencyManager currencyManager;
MembershipERC1155 membershipImplementation;
address owpWallet = makeAddr("owpWallet");
address membershipNftAddress;
OWPERC20 owpERC20;
address userOne = makeAddr("userOne");
function setUp() public {
owpERC20 = new OWPERC20("OWP","OWP");
currencyManager = new CurrencyManager();
currencyManager.addCurrency(address(owpERC20));
membershipImplementation = new MembershipERC1155();
membershipFactory = new MembershipFactory(address(currencyManager),owpWallet,"",address(membershipImplementation));
owpERC20.mint(userOne,100e18);
vm.prank(userOne);
owpERC20.approve(address(membershipFactory),type(uint256).max);
}
function test_updateDAOMembership() public {
DAOInputConfig memory dAOInputConfig = DAOInputConfig("test",DAOType.PUBLIC,address(owpERC20),400,4);
TierConfig[] memory tierConfig = new TierConfig[]();
tierConfig[0] = TierConfig(1,10000,1,0);
tierConfig[1] = TierConfig(1,5000,1,0);
tierConfig[2] = TierConfig(1,2500,1,0);
tierConfig[3] = TierConfig(1,1250,1,0);
membershipNftAddress = membershipFactory.createNewDAOMembership(dAOInputConfig,tierConfig);
uint256 snapshot = vm.snapshotState();
tierConfig = new TierConfig[](5);
tierConfig[0] = TierConfig(1,10000,1,1);
tierConfig[1] = TierConfig(1,5000,1,1);
tierConfig[2] = TierConfig(1,2500,1,1);
tierConfig[3] = TierConfig(1,1250,1,1);
tierConfig[4] = TierConfig(1,625,1,1);
membershipFactory.updateDAOMembership("test",tierConfig);
TierConfig[] memory checkTierConfig = membershipFactory.tiers(membershipNftAddress);
assertEq(checkTierConfig[4].minted,1);
vm.expectRevert();
vm.prank(userOne);
membershipFactory.joinDAO(address(membershipNftAddress),4);
vm.revertToState(snapshot);
vm.prank(userOne);
membershipFactory.joinDAO(address(membershipNftAddress),3);
assertEq(MembershipERC1155(membershipNftAddress).balanceOf(userOne,3),1);
tierConfig = new TierConfig[](3);
tierConfig[0] = TierConfig(1,10000,1,1);
tierConfig[1] = TierConfig(1,5000,1,1);
tierConfig[2] = TierConfig(1,2500,1,1);
membershipFactory.updateDAOMembership("test",tierConfig);
tierConfig = new TierConfig[](4);
tierConfig[0] = TierConfig(1,10000,1,0);
tierConfig[1] = TierConfig(1,5000,1,0);
tierConfig[2] = TierConfig(1,2500,1,0);
tierConfig[3] = TierConfig(1,1250,1,0);
membershipFactory.updateDAOMembership("test",tierConfig);
vm.prank(userOne);
membershipFactory.joinDAO(address(membershipNftAddress),3);
assertEq(MembershipERC1155(membershipNftAddress).balanceOf(userOne,3),2);
}
}
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];
+ // Prevent the original data from being cleared
+ require(tierConfigs.length >= dao.tiers.length, "Invalid tier count.");
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 it is newly added, then its minted should default to 0. If it is not newly added, it will synchronize the original data in if
+ tierConfigs[i].minted = 0;
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;
}