Sparkn

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

Blacklisted `STADIUM_ADDRESS` by Token can lock the funds forever

Summary

The distribute function inside Distributor.sol attempts to transfer token to the Winners and STADIUM_ADDRESS. If Token Implements a blacklist like USDC Token, then the transfer wouldn't be possible and the funds can get stuck forever.

Vulnerability Details

The distribute function transfer the funds to the Winners and STADIUM_ADDRESS but, this STADIUM_ADDRESS could be blacklisted by tokens that maintains a blacklist. An Example of this token is common USDC token contract.

Here is the link to the USDC contract on polygon: [USDC]
(The Blacklist code is located on line 2569)

Because of this The token transfer wouldn't be possible and the token will get stuck forever in the Proxy contract. There is no way to change the STADIUM_ADDRESS in the contracts.

Here are the Tests that Proves that

The test is using this Example ERC20 With BlackList contract.

  • Link to test in Case of Blacklisted address [Fail Transfer]: [Test]

  • Test Link in Case of Non Blacklisted address [Successful Transfer]: [Test]

The Same is Also Possible if a Winner address is Provided that is listed by the Token. But this doesn't lock the funds or do any kind of harm as The Winners can be passed to the distribute function again with different addresses. It only incur some gas cost for computation.

Impact

This would not make the transfer of fund impossible and the funds will get stuck forever as there is no way to change the STADIUM_ADDRESS. One might think that deploying new Distributor.sol contract with different STADIUM_ADDRESS might solve the issue but that is also not possible because the Proxy.sol contract has the address of the _implementation or Distributor.sol stored as a state variable and there is no way to change that as well.

Tools Used

Manual Review

Recommendations

There are two ways to solve the issue:

  1. Expensive Way: Add an update function in the Proxy contract itself or in the ProxyFactory to update the address of the implementation.
    An Example code:

contract Proxy {
address private implementation;
address private proxyFactory;
function updateImplementation(address newImplementation) external {
if(proxyFactory != msg.sender || newImplementation == address(0)){
revert();
}
implementation = newImplementation;
}
// Other contract functions...
}
contract ProxyFactory {
// Other factory-related code...
function updateImplementation(address proxyAddress, address newImplementation) external onlyOwner {
Proxy(proxyAddress).updateImplementation(newImplementation);
}
}
  1. Less Expensive: Make STADIUM_ADDRESS private state variable instead of immutable and add an update function in the Distributor.sol or ProxyFactory.sol to update the STADIUM_ADDRESS.
    An Example Code

contract Distributor {
address private STADIUM_ADDRESS;
// contract state variables and events
constructor(address initialStadiumAddress) {
if(initialStadiumAddress == address(0)) revert();
STADIUM_ADDRESS = initialStadiumAddress;
}
function updateStadiumAddress(address newStadiumAddress) external {
require(msg.sender != FACTORY_ADDRESS, "Only ProxyFactory Can call this"); // not neccessary factory it could be the owner ass well
STADIUM_ADDRESS = newStadiumAddress;
}
// Other contract functions...
}
contract ProxyFactory {
// Other factory-related code...
// The same thing can be done in the Distributor itself with proper owner check
function updateStadiumAddress(address distributorAddress, address newStadiumAddress) external onlyOwner {
Distributor(distributorAddress).updateStadiumAddress(newStadiumAddress);
}
}

Note
Make proper checks so that the addresses can be updated by the owner only or some other trusted party.

Support

FAQs

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

Give us feedback!