Sparkn

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

The distribution of the tokens to the winners will reach a gas limit if the winners array is large enough

Summary

The distribute() function will reach a gas limit if the winners array is bigger than ~1000 addresses. This is a limitation on the design of the contract, and should be noted. Changes could be made so that the contract has no limit on number of winners.

Vulnerability Details

In the way it was designed, the distribute() function will have a maximum of winners possible to execute. As the codebase stands it seems that if there are ~1000 addresses the loop of transaction PUSH will fail by reaching the block gas limit.

On polygon and ethereum the current gas limit per block, and per transaction, is 30 million.

The following POC can be added to ProxyTest.t.sol

    function testGasLimit()
        public
        setUpContestForNameAndSentAmountToken("James", jpycv2Address, 10000 ether)
    {
        bytes32 randomId_ = keccak256(abi.encode("James", "001"));
        bytes memory data = createDataToDistributeJpycv2();

        // prepare data
        uint160 numberOfWinners = 950;
        address[] memory winners = new address[](numberOfWinners);
        uint256[] memory percentages_ = new uint256[](numberOfWinners);
        for (uint160 i = 1; i < numberOfWinners+1; i++) {
            winners[i-1] = address(i);
            percentages_[i-1] = 10;
        }

        data = abi.encodeWithSelector(Distributor.distribute.selector, jpycv2Address, winners, percentages_, "");

        vm.startPrank(organizer);
        deployedProxy = proxyFactory.deployProxyAndDistribute(randomId_, address(distributor), data);
        vm.stopPrank();

    }

Run the gas report with forge test --match-test testGasLimit --gas-report

The gas report will show that the transaction with 950 winners will cost 27 million gas, which is close to the block gas limit.

Impact

This is a limitation on the current design of the contract, and the creators have one of two options, redesign the protocol for unlimited number of winners, or document the limitation.

Tools Used

Forge gas-report

Recommendations

Divide the distribution of the tokens in multiple steps.

  1. Organizer adds the winners to a mapping on storage with their percentages. This could be done in multiple transactions, until the full list is achieved. Add the percentages of each winner to a total percentage.
    A struct could be used to store the winner and percentage in one slot.

     struct Winner {
         address winner; // 20 bytes
         uint96 percentage; // 12 bytes
     }
    
  2. Organizer calls a function to validate the total percentage is 100%. At this point the state of the contract changes and the winners would be able to withdraw their funds with a Pull pattern.

  3. Users withdraw their funds with a pull pattern.

Or document the limitation.

Support

FAQs

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