Sparkn

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

Distribution of prize to winners will revert if one of the winners is blacklisted in the specific ERC20 token

Summary

If the protocol has a blacklistable ERC20 token like USDC whitelisted in the constructor and a user wins a part of a contest prize that will be paid in USDC, the distribution to pay the different amounts to the winners will revert.

Vulnerability Details

There are ERC20 tokens like USDC that have a logic of blacklisting addresses. If you try to transfer some amount of these tokens to a blacklisted address, it will revert. So if a user wins a part of a contest prize that is paid in a token that is blacklisted, when _distribute is called, it will be reverted due to this feature.

POC
contract AuditTest is StdCheats, Test {
uint256 mainnetFork;
address public owner = makeAddr("owner");
address public organizer = makeAddr("organizer");
address public sponsor = makeAddr("sponsor");
address public stadiumAddress = makeAddr("stadium");
address public blacklistedUser = 0xefEeF8E968a0dB92781ac7B3B7c821909eF10c88;
address public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
ProxyFactory public proxyFactory;
address public implementation;
function setUp() public {
mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"));
vm.selectFork(mainnetFork);
assertEq(vm.activeFork(), mainnetFork);
// Deploy ProxyFactory and Distributor implementation
address[] memory tokens = new address[](1);
tokens[0] = usdc;
vm.prank(owner);
proxyFactory = new ProxyFactory(tokens);
goodImplementation = address(new Distributor(address(proxyFactory), stadiumAddress));
}
function testBlacklistedWinner() public {
vm.selectFork(mainnetFork);
assertEq(vm.activeFork(), mainnetFork);
bytes32 contestId = 0x0000000000000000000000000000000000000000000000000000000000000001;
// Create a contest
vm.startPrank(owner);
proxyFactory.setContest(organizer, contestId, block.timestamp + 10 days, goodImplementation);
// Sponsor funds the event with 1000 usdc
deal(usdc, sponsor, 1000 * 10 ** 6);
assertEq(ERC20(usdc).balanceOf(sponsor), 1000 * 10 ** 6);
address proxyAddress = proxyFactory.getProxyAddress(_calculateSalt(organizer, contestId, goodImplementation), goodImplementation);
vm.prank(sponsor);
ERC20(usdc).transfer(proxyAddress, 1000 * 10 ** 6);
// Contest ends
skip(11 days);
// Check blacklisted user
(bool success, bytes memory response) = usdc.staticcall(abi.encodeWithSignature("isBlacklisted(address)", blacklistedUser));
require(success);
(bool isBlacklisted) = abi.decode(response, (bool));
assert(isBlacklisted);
// Distribute money
address[] memory winners = new address[](1);
winners[0] = blacklistedUser;
uint256[] memory percentages = new uint256[](1);
percentages[0] = 9500;
bytes memory distributeData = abi.encodeWithSignature("distribute(address,address[],uint256[],bytes)", usdc, winners, percentages, "");
vm.startPrank(organizer);
vm.expectRevert();
proxyFactory.deployProxyAndDistribute(contestId, goodImplementation, distributeData);
}
// Helper function
function _calculateSalt(address _organizer, bytes32 _contestId, address _implementation)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(_organizer, _contestId, _implementation));
}
}

Result:

Test result: ok. 1 passed; 0 failed; finished in 3.52s
Revert error: "Blacklistable: account is blacklisted"

Impact

If a blacklisted user for a contest token is declared part of the winning prize, the funds would remain stuck in the contract forever because the distribution function will revert when trying to transfer tokens to the blacklisted user. There is a simple solution to avoid this revert just by not distributing the prize to this user, but that would be unfair.

Tools Used

Manual review + foundry testing

Recommendations

Possible solution:

  1. Filter participants for the contest by providing their addresses in advance to check if they are blacklisted or not in the specific token for the contest.

  2. Once a contest has finished and a blacklisted user has won part of the prize, the organizer could ignore his part and distribute his percentage of the prize to other winners. This way, the function call would not revert, but it would be really unfair.

  3. Do not use ERC20 tokens that have blacklists

Support

FAQs

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