Sparkn

CodeFox Inc.
DeFiFoundryProxy
15,000 USDC
View results
Submission Details
Severity: low
Valid

Duplicate winner addresses will be funded

Summary

The _distribute function in Distributor.sol will fund all winners including duplicate addresses.

Vulnerability Details

There's no check to filter duplicate addresses before funding the wallets of winners. Ideally, the function expects the caller will provide accurate data at all times and doesn't bother about uniqueness. Well, in the case the caller makes the mistake of duplicating winner addresses, it will fund them with whatever percentages was sent along the params.

function _distribute(address token, address[] memory winners, uint256[] memory percentages, bytes memory data)
internal
{
// token address input check
if (token == address(0)) revert Distributor__NoZeroAddress();
if (!_isWhiteListed(token)) {
revert Distributor__InvalidTokenAddress();
}
// winners and percentages input check
if (winners.length == 0 || winners.length != percentages.length) revert Distributor__MismatchedArrays();
uint256 percentagesLength = percentages.length;
uint256 totalPercentage;
for (uint256 i; i < percentagesLength;) {
totalPercentage += percentages[i];
unchecked {
++i;
}
}
// check if totalPercentage is correct
if (totalPercentage != (10000 - COMMISSION_FEE)) {
revert Distributor__MismatchedPercentages();
}
IERC20 erc20 = IERC20(token);
uint256 totalAmount = erc20.balanceOf(address(this));

    // if there is no token to distribute, then revert
    if (totalAmount == 0) revert Distributor__NoTokenToDistribute();
    // @audit handle winner address uniqueness
    // @audit handle case for user's address blacklisted by reward token
    uint256 winnersLength = winners.length; // cache length
    for (uint256 i; i < winnersLength;) {
        uint256 amount = totalAmount * percentages[i] / BASIS_POINTS;
        erc20.safeTransfer(winners[i], amount);
        unchecked {
            ++i;
        }
    }

    // send commission fee as well as all the remaining tokens to STADIUM_ADDRESS to avoid dust remaining
    _commissionTransfer(erc20);
    emit Distributed(token, winners, percentages, data);
}

Impact

With the issue of financial loses lingering for winners supporting the solution or pioneering one in a challenge/contest, some addresses may get rewarded wrongly, and others will get nothing. The phrase is true: It's easy for the bank to take money out of your account but hard for them to put it back.

Tools Used

Manual Review / VSCode

Recommendations

Have a logic to only fund unique addresses and filter out duplicates. You can achieve this in a couple of ways but here's one:
have a mapping(address => bool) private seenAddresses;
make another for loop to check for duplicate addresses and populate seenAddresses mapping
check if address has been seen before e.g.:
if (seenAddresses[winner]) {
// revert or skip or do something else;
}
seenAddresses[winner] = true;
then proceed to fund the unique winners addresses

Support

FAQs

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