Sparkn

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

Commission fee is paid when tokens are recovered

Summary

If a sponsor accidentally sends the wrong tokens to the proxy, they can be recovered using distributeByOwner after the expiration time. However, in this process, a 5% commission fee is also paid to the stadium address.

Vulnerability Details

  1. Contest set and a token is agreed upon.

  2. A sponsor accidentally sends the wrong token.

  3. The sponsor waits until the contest and expiration time are over.

  4. The owner recovers the tokens using distributeByOwner function.

  5. In this process, the function is called, which is used to distribute winnings to the winners and send a 5% commission fee to the stadium address.

Here is a PoC that can be added to test/integration/ProxyFactoryTest.t.sol:

function testCommissionFeeIsPayedUponRecovery() public {
vm.startPrank(tokenMinter);
MockERC20(jpycv1Address).mint(user2, 100_000 ether);
vm.stopPrank();
//Owner sets up contest
vm.startPrank(factoryAdmin);
bytes32 randomId = keccak256(abi.encode("Jason", "001"));
proxyFactory.setContest(organizer, randomId, block.timestamp + 8 days, address(distributor));
vm.stopPrank();
bytes32 salt = keccak256(abi.encode(organizer, randomId, address(distributor)));
address proxyAddress = proxyFactory.getProxyAddress(salt, address(distributor));
//Sponsors send tokens to proxy
vm.startPrank(sponsor);
MockERC20(jpycv2Address).transfer(proxyAddress, 10000 ether);
vm.stopPrank();
//Another sponsor accidentally sends wrong tokens
vm.startPrank(user2);
MockERC20(jpycv1Address).transfer(proxyAddress, 10000 ether);
vm.stopPrank();
//Check that user2 has spend all tokens
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 0);
bytes memory data = createData();
//Organizer distributes all correct tokens
vm.warp(9 days);
vm.startPrank(organizer);
address proxy = proxyFactory.deployProxyAndDistribute(randomId, address(distributor), data);
vm.stopPrank();
//Data to call distribute to return user2's tokens
address[] memory tokens_ = new address[](1);
tokens_[0] = jpycv1Address;
address[] memory winners = new address[](1);
winners[0] = user2;
uint256[] memory percentages_ = new uint256[](1);
//Only 95% of the tokens can be returned, as otherwise, the distribute function will revert
percentages_[0] = 9500;
bytes memory dataRecover = abi.encodeWithSelector(Distributor.distribute.selector, jpycv1Address, winners, percentages_, "");
//After the expiration time, the owner can send the tokens to user2
vm.warp(16 days);
vm.startPrank(factoryAdmin);
proxyFactory.distributeByOwner(
proxy, organizer, randomId, address(distributor), dataRecover
);
vm.stopPrank();
//Shows that user2 has 500 tokens less than what he had before, as he had to pay a 5% commission fee
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 9500 ether);
}

Impact

Sponsor loses 5% of its tokens when sending the wrong tokens

Tools Used

Manual Review, AuditWizard

Recommendations

Implement a recovery function like this in Distributor.sol:

function recover(address token, address receiver, uint256 amount) external {
if (msg.sender != FACTORY_ADDRESS) {
revert Distributor__OnlyFactoryAddressIsAllowed();
}
if (token == address(0)) revert Distributor__NoZeroAddress();
if (!_isWhiteListed(token)) {
revert Distributor__InvalidTokenAddress();
}
if(IERC20(token).balanceOf(address(this)) < amount) revert Distributor__TooFewTokens();
IERC20(token).safeTransfer(receiver, amount);
}

Make sure that the organizer cannot call this function.

Support

FAQs

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