code-423n4 / 2023-05-ajna-findings

2 stars 0 forks source link

ExtraordinaryFunding may fail due to a decrease in the treasury funds due to a `StandardFunding` new distribution round #485

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L105 https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L176

Vulnerability details

Impact

Users could spend gas and possibly other off chain resources voting on an ExtraordinaryFunding proposal which would later revert when executing.

Proof of Concept

If an ExtraordinaryFunding proposal requests an amount of tokens between 48.5% and 50% of the treasury (the limits change as ExtraordinaryFunding proposals are funded). 48.5% is 0.5*0.97*100, due to the 3% allocated to the StandardFunding distribution round.

Add the following test to GrantFund.t.sol:

function test_POC_ExtraordinaryFundingFails_DueToNewDistributionRound() external {
    // initial minter distributes tokens to test addresses
    _transferAjnaTokens(_token, _votersArr, 500_000_000 * 1e18, _tokenDeployer);

    for (uint256 i_ = 0; i_ < _votersArr.length; ++i_) {
        changePrank(_votersArr[i_]);
        _token.delegate(_votersArr[i_]);
    }

    vm.roll(_startBlock + 33);

    // fund treasury
    changePrank(_tokenDeployer);
    _token.approve(address(_grantFund), 50_000_000 * 1e18);
    vm.expectEmit(true, true, false, true);
    emit FundTreasury(50_000_000 * 1e18, 50_000_000 * 1e18);
    _grantFund.fundTreasury(50_000_000 * 1e18);

    // create proposal
    address[] memory targets_ = new address[](1);
    targets_[0] = address(_token);
    uint256[] memory values_ = new uint256[](1);
    values_[0] = 0;
    bytes[] memory calldatas_ = new bytes[](1);
    // The first extraordinary proposal can give up to treasury/2 tokens
    calldatas_[0] = abi.encodeWithSignature("transfer(address,uint256)", _tokenHolder1, 50_000_000/2*1e18);

    uint256 proposalId_ = _grantFund.proposeExtraordinary(
        block.number + 216_000 - 1,
        targets_,
        values_,
        calldatas_,
        ""
    );

    // vote on it
    _extraordinaryVote(_grantFund, _tokenHolder1, proposalId_, 1);
    _extraordinaryVote(_grantFund, _tokenHolder2, proposalId_, 1);
    _extraordinaryVote(_grantFund, _tokenHolder3, proposalId_, 1);

    // StandardFunding new round
    _startDistributionPeriod(_grantFund);

    // execute proposal should revert
    vm.expectRevert(IExtraordinaryFunding.ExecuteExtraordinaryProposalInvalid.selector);
    _grantFund.executeExtraordinary(targets_, values_, calldatas_, keccak256(""));
}

Tools Used

Vscode, Foundry

Recommended Mitigation Steps

If a StandardFunding distribution is coming in the next fund, only allow proposing up to _getMinimumThresholdPercentage()*0.97*treasury. Another option is, when executing, don't revert if tokensRequested_ are bigger than _getMinimumThresholdPercentage()*treasury, but limit it to it.

Assessed type

Math

c4-judge commented 1 year ago

Picodes marked the issue as duplicate of #489

c4-judge commented 1 year ago

Picodes marked the issue as unsatisfactory: Invalid