code-423n4 / 2024-01-salty-findings

11 stars 6 forks source link

Exchange Launch Constraint During DAO Bootstrapping #800

Closed c4-bot-2 closed 9 months ago

c4-bot-2 commented 9 months ago

Lines of code

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/BootstrapBallot.sol#L48-L86

Vulnerability details

Exchange Launch Constraint During DAO Bootstrapping

Observation

The initiation of the exchange during the bootstrap phase holds critical significance, as once the process concludes without activation, subsequent attempts by the DAO to commence the exchange become impracticable, even if the DAO expresses a desire to do so.

Impact

MEDIUM

The DAO faces the inability to commence the exchange if it decides not to initiate it initially. Furthermore, there is a risk of being front-run by a malicious actor with a negative vote in the eleventh hour, potentially locking the exchange in a perpetual state of startExchangeApproved == false.

Consider the following perspective: even though voters are selected from specific staker groups (AAVE, Compounds, ...), they have the option to collaborate and, strategically, wait until the last moment to cast negative votes and consequently close the exchange.

Proof of concept

The finalizeBallot() function is used to determining whether the exchange should start or not. In the event of a negative decision, the exchange becomes unlaunchable in subsequent attempts.

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/launch/BootstrapBallot.sol#L48C2-L86C3

function finalizeBallot() external nonReentrant
        {
        require( ! ballotFinalized, "Ballot has already been finalized" );
        require( block.timestamp >= completionTimestamp, "Ballot is not yet complete" );

        if ( startExchangeYes > startExchangeNo )
            {
            exchangeConfig.initialDistribution().distributionApproved();
            exchangeConfig.dao().pools().startExchangeApproved();

            startExchangeApproved = true;
            }

        emit BallotFinalized(startExchangeApproved);

        ballotFinalized = true;
        }
    }

The function is callable only once due to the following requirement:

require( ! ballotFinalized, "Ballot has already been finalized" );

Recommended Mitigation Steps

Consider allowing the DAO the flexibility to initiate the exchange at a later time if desired.

Assessed type

Other

c4-judge commented 9 months ago

Picodes marked the issue as duplicate of #606

c4-judge commented 8 months ago

Picodes changed the severity to QA (Quality Assurance)

c4-judge commented 8 months ago

Picodes marked the issue as grade-b

c4-judge commented 8 months ago

Picodes marked the issue as grade-c