Sparkn

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

If implementation address or STADIUM_ADDRESS get blocklisted by tokens, Funds will stuck at Proxy Contract.

Summary

In the case of blocklisting occurs for immutable addresses that are used in transfer functions, funds will stuck in Proxy contract.

Vulnerability Details

In order to prove points that I will state later, I am starting with explaining how some variables set during life-cycle of one contest:

  • First, contest start with owner's call to the setContest() and during this call salt which is used in creating proxy is called with "implementation" parameter as shown below:

bytes32 salt = _calculateSalt(organizer, contestId, implementation);
  • Then sponsors send tokens to the proxy contract before its deployment.

  • During deployment we are again using same "implementation" parameter in _deployProxy() as shown below:

function deployProxyAndDistribute(bytes32 contestId, address implementation, bytes calldata data)
public
returns (address)
{
bytes32 salt = _calculateSalt(msg.sender, contestId, implementation);
if (saltToCloseTime[salt] == 0) revert ProxyFactory__ContestIsNotRegistered();
// can set close time to current time and end it immediately if organizer wish
if (saltToCloseTime[salt] > block.timestamp) revert ProxyFactory__ContestIsNotClosed();
address proxy = _deployProxy(msg.sender, contestId, implementation);
_distribute(proxy, data);
return proxy;
}

So we know that "implementation" parameter can not change after setContest() call.

  • Inside our proxy(where we hold our tokens) we have an immutable _implementation that is used for distributing process which again can not change:

contract Proxy {
// implementation address
address private immutable _implementation;
  • And inside our implementation (distributor) we have again an immutable addresses FACTORY_ADDRESS which will call to distribute function (only FACTORY_ADDRESS can call it) and STADIUM_ADDRESS which is the address to receive fees that set in the constructor and they can not change after deployment as shown below:

address private immutable FACTORY_ADDRESS;
address private immutable STADIUM_ADDRESS;

Sponsor mentioned that stadium address is the same as ProxyFactory's owner:

auditor: so STADIUM_ADDRESS will be set by the protocol. correct me if I'm wrong
sponsor: Nope the STADIUM_ADDRESS is the owner of the protocol.

Now here comes the vulnerability:

Expected tokens to use in contract is mentioned in nat-spec as:

@notice General ERC20 stable coin tokens, e.g. JPYC, USDC, USDT, DAI, etc, are suppsoed to be used in SPARKN.

The problem is JPYC,USDC and USDT are tokens with blocklist feature!

Let's look at JPYC'S transfer function:

function transfer(address to, uint256 value)
external
override
whenNotPaused
notBlocklisted(msg.sender)
notBlocklisted(to)
checkAllowlist(msg.sender, value)
returns (bool)
{
_transfer(msg.sender, to, value);
return true;
}

So in order for transfers to occur in JPYC, both the "msg.sender" and "to" address must not be blacklisted.

Coming back to our SPARKN, we know that after contest end, prices will send via _distribute() function called inside ProxyFactory and distribute() that is inside Distributor.sol have two transfers:

erc20.safeTransfer(winners[i], amount);
...
_commissionTransfer(erc20);
...
function _commissionTransfer(IERC20 token) internal {
token.safeTransfer(STADIUM_ADDRESS, token.balanceOf(address(this)));
}

So what are the risks in here?

1 - if implementation contract is blocklisted, distribute will fail. Since msg.sender for transfer function is Distributor.sol's deployed address (implementation), and there is no way to change this address after deployment(it is immutable in proxy and it is used in salt while creating proxy) in the case of blocklisted Distributor.sol, funds will stuck at proxy contract without a way to withdraw.

2 - if one of the winners is blocklisted, distribute will fail. This is not a problem since winners are passed as a data to function, in case this happens it can be solved off-chain (Winner can provide another not blocklisted address or winner gets deleted from winners).

3 - if STADIUM_ADDRESS is blocklisted, distribute will fail. This address is protocol's owner as mentioned by sponsor, and if that address get blocklisted by token, since there is no way to change this address (it is immutable), funds will stuck at proxy contract without a way to withdraw.

Impact

Funds get stuck in a proxy contract without a way to withdraw. Hence this issue describes a high severity vulnerability.

Tools Used

Manual Review

Recommendations

If protocol wants to use tokens with blocklists, they have to reconsider implementations of immutables.

To be specific:

1- STADIUM_ADDRESS in Distributor.sol should be changeable in case of blocklist.

2- _implementation in Proxy.sol should be changeable (which actually effects proxy's salt calculation, hence salt calculation should also be changed to a one that don't have implementation address inside).

Support

FAQs

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