Sparkn

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

Lack of Mandatory Distribution Check Mechanism

Summary

The Distributor contract allows for the distribution of tokens to winners based on certain percentages. However, there's no mechanism to ensure the distribution function will ever be called by the owner or centralized entity. Whether the owner is a trusted entity or not, users have no decentralized safety guarantees.

Impact

  • Deploy the Distributor contract.

  • Multiple users send tokens to the proxy contract expecting them to be distributed among winners.

  • The owner never calls the distribute function; tokens remain locked in the proxy contract.

Tools Used

Manual Review

Recommendations

Option #1 - Implement a deadline mechanism to Distributor.sol by which tokens must be distributed. After this deadline any participant can trigger the distribution of the tokens held within the proxy contract.

Option #2 - Implement a claim function, allowing participants to retrieve their shares after the contest deadline if the tokens haven't been distributed.

// Mapping to keep track of tokens by FACTORY_ADDRESS
mapping(address => uint256) public stakedTokens;
// Mapping to keep track of each contributor's share
mapping(address => uint256) public contributorShares;
address public FACTORY_ADDRESS;
address public STADIUM_ADDRESS;
uint256 public distributionDeadline;
uint256 public totalContributed;
// Create a boolean flag for distribution
bool public distributionDone = false;
constructor(
address factoryAddress,
address stadiumAddress
+ uint256 _hoursUntilDeadline
) {
if (factoryAddress == address(0) || stadiumAddress == address(0)) revert Distributor__NoZeroAddress();
FACTORY_ADDRESS = factoryAddress;
STADIUM_ADDRESS = stadiumAddress;
+ distributionDeadline = block.timestamp + (_hoursUntilDeadline * 1 hours);
}
// Option #1: All users can call distribute after contest deadline
function distribute(address token, address[] memory winners, uint256[] memory percentages, bytes memory data) external {
if (block.timestamp < distributionDeadline && msg.sender != FACTORY_ADDRESS) revert Distributor__DistributionNotAllowedYet();
if (block.timestamp >= distributionDeadline && distributionDone) {
revert Distributor__DistributionAlreadyDone();
}
_distribute(token, winners, percentages, data);
distributionDone = true;
}
// Option #2: Individual contributors call the claim function after a deadline where distribute is not called.
// Function to allow contributors to claim their portion of the staked tokens after the contest deadline
// The claim function needs to call the proxy contract to retrieve the tokens.
+ function claim(address token) external {
if (distributionDone) revert Distributor__DistributionDoneCannotClaim();
if (block.timestamp < distributionDeadline) revert Distributor__ClaimingOnlyAllowedAfterDeadline();
}
uint256 claimableAmount = (IERC20(PROXY_ADDRESS).balanceOf(address(this)) * contributorShares[msg.sender]) / totalContributed;
if (claimAmount == 0) revert Distributor__NoAmountToClaim();
// Deduct the claimable amount from staked tokens and reset the contributor's share
stakedTokens[token] -= claimableAmount;
contributorShares[msg.sender] = 0;
IERC20(token).safeTransferFrom(PROXY_ADDRESS, msg.sender, claimableAmount);
}

This ensures if the owner doesn't call the distribute function by the deadline, the tokens don't remain locked in the proxy contract and participants can #1 call distribute, or #2 claim their share of the tokens after the contest deadline has passed.

Support

FAQs

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

Give us feedback!