Summary
The joinDAO
function lacks a critical check to ensure that users have sufficient balance to cover the total cost of joining a DAO, which includes both platform fees and tier price. This oversight can lead to transaction failures and a poor user experience.
Vulnerability Details
The joinDAO
) function does not verify whether the user has enough tokens in their balance or if they have approved the contract to spend the required amount. This can lead to failed transactions if the user’s balance is insufficient.
function joinDAO(address daoMembershipAddress, uint256 tierIndex) external {
require(daos[daoMembershipAddress].noOfTiers > tierIndex, "Invalid tier.");
require(daos[daoMembershipAddress].tiers[tierIndex].amount > daos[daoMembershipAddress].tiers[tierIndex].minted, "Tier full.");
uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
uint256 platformFees = (20 * tierPrice) / 100;
daos[daoMembershipAddress].tiers[tierIndex].minted += 1;
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), daoMembershipAddress, tierPrice - platformFees);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), tierIndex, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, tierIndex);
}
Impact
If the user does not have enough balance to cover the total cost, the transferFrom
calls will fail, reverting the entire transaction and causing frustration for users.
An attacker could exploit this vulnerability by attempting to join a DAO with insufficient funds, potentially blocking legitimate users from executing their transactions.
Tools Used
Manual Review
Recommendations
Ensure that the user has enough balance to cover the total cost (platform fees
+ tier price
) using the balanceOf
function of the ERC20 token and also ensure that the user has approved the contract to spend the required amount of tokens on their behalf.
function joinDAO(address daoMembershipAddress, uint256 tierIndex) external {
require(daos[daoMembershipAddress].noOfTiers > tierIndex, "Invalid tier.");
require(daos[daoMembershipAddress].tiers[tierIndex].amount > daos[daoMembershipAddress].tiers[tierIndex].minted, "Tier full.");
+ uint256 totalCost = tierPrice + platformFees; // Total cost to join the DAO
+ require(IERC20(daos[daoMembershipAddress].currency).balanceOf(_msgSender()) >= totalCost, "Insufficient balance.");
+ require(IERC20(daos[daoMembershipAddress].currency).allowance(_msgSender(), address(this)) >= totalCost, "Insufficient allowance.");
uint256 tierPrice = daos[daoMembershipAddress].tiers[tierIndex].price;
uint256 platformFees = (20 * tierPrice) / 100;
daos[daoMembershipAddress].tiers[tierIndex].minted += 1;
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), owpWallet, platformFees);
IERC20(daos[daoMembershipAddress].currency).transferFrom(_msgSender(), daoMembershipAddress, tierPrice - platformFees);
IMembershipERC1155(daoMembershipAddress).mint(_msgSender(), tierIndex, 1);
emit UserJoinedDAO(_msgSender(), daoMembershipAddress, tierIndex);
}