Sparkn

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

Sponsors can deposit multiple whitelisted tokens making it difficult to distribute the reward

Summary

Sponsors who are willing to fund the project can send different whitelisted tokens, making it difficult to distribute the rewards.

Vulnerability Details

After the contest is set up by the owner, either the organizer or sponsors will fund the yet-to-be-deployed proxy address. We assume that many sponsors want to support the project, so each sponsor can send different whitelisted tokens to the proxy contract. For example, Sponsor A can send USDC, and Sponsor B can send JPY tokens. This will result in a contract with different tokens to distribute.

Impact

If the protocol has 20 whitelisted tokens and sponsors send 20 types of tokens to the proxy contract, then the distribute function has to be called 20 times, each time with all the percentage calculations to determine how much each recipient should receive. This approach is not feasible and the system will eventually fail to distribute rewards fairly.

Tools Used

manual review

Recommendations

Instead of accepting all tokens as a fund, the contest should specify which token will be distributed as a reward. To do this, the organizer can specify the token to the owner. At the time of setContest, the owner can explicitly set the reward token in the storage mapping for that contestId.

Below is the example

This should be the updated set contest function with the new mapping of contestIdToRewardToken and new error ProxyFactory__NotWhiteListed()

//new added error
error ProxyFactory__NotWhiteListed();
//new added mapping
mapping(bytes32 => address) public contestIdToRewardToken;
function setContest(address organizer, bytes32 contestId, uint256 closeTime, address implementation,address rewardToken)
public
onlyOwner
{
if (organizer == address(0) || implementation == address(0)) revert ProxyFactory__NoZeroAddress();
if (closeTime > block.timestamp + MAX_CONTEST_PERIOD || closeTime < block.timestamp) {
revert ProxyFactory__CloseTimeNotInRange();
}
if(!whitelistedTokens[rewardToken]) revert ProxyFactory__NotWhiteListed();
bytes32 salt = _calculateSalt(organizer, contestId, implementation);
if (saltToCloseTime[salt] != 0) revert ProxyFactory__ContestIsAlreadyRegistered();
saltToCloseTime[salt] = closeTime;
contestIdToRewardToken[contestId] = rewardToken;
emit SetContest(organizer, contestId, closeTime, implementation);
}

We also need to check if the token sent for distribution is the same as the reward token or not in Distribution.sol

This is the updated code. please note that we are also sending the contestId in the parameter so update your off-chain data accordingly.

The update have one new error Distributor__InvalidRewardToken(); and one check in distribute function

error Distributor__InvalidRewardToken();
function distribute(address token, address[] memory winners, uint256[] memory percentages,bytes32 contestId, bytes memory data)
external
{
if (msg.sender != FACTORY_ADDRESS) {
revert Distributor__OnlyFactoryAddressIsAllowed();
}
if(ProxyFactory(FACTORY_ADDRESS).contestIdToRewardToken(contestId) != token){
revert Distributor__InvalidRewardToken();
}
_distribute(token, winners, percentages, data);
}

Also, make sure to render the reward token in the frontend so sponsors can know which token to send.

Support

FAQs

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

Give us feedback!