The winner payout mechanism, executed in withdraw(), incorrectly calculates the assets to be paid out, resulting in a large portion of the prize pool being permanently locked within the vault contract.
The contract attempts to distribute the entire prize pool (finalizedVaultAsset) based on the winners' proportional shares (totalWinnerShares). Because of either precision loss in Math.mulDiv or, more likely, a failure to dynamically track the remaining pool, the total assets paid out are significantly less than the total prize pool.
In the provided scenario, only 13.13 \text{ ETH} of the total 19.7 \text{ ETH} pool was distributed, leaving 6.56 \text{ ETH} (34% of the pool) permanently locked in the vault, which is irrecoverable by any participant
Likelihood:
Reason 1: The flaw is guaranteed to occur on any market resolution and subsequent withdrawal, as the financial model is broken.
Reason 2: The failure is consistent and predictable, resulting in an immediate asset lock after the first winner successfully withdraws.
Impact:
Permanent Asset Lock: A large percentage of the total prize pool is permanently inaccessible by participants, representing a total loss of locked value. In the PoC, 6.56 \text{ ETH} remains locked.
• Failure of Core Financial Promise: This bug renders the platform unusable and violates the fundamental principle of a vault using the EIP 4626 standard.
The PoC demonstrates the discrepancy: the total assets in the vault before withdrawals (19.7 \text{ ETH}) do not equal the sum of assets paid out, leaving 6.56 \text{ ETH} behind.
Explanation
Initial Pool: 19.7 \text{ ETH}.
Expected Payout: All three winners should receive their net stake back, totaling 19.7 { ETH}, leaving 0 { ETH} in the vault.
Actual Payout: The logs confirm that the three winners combined received only 13.14 { ETH} in total.
Result: The final vault balance is 6.56 { ETH}, confirming that 34% of the pool is permanently locked and inaccessible.
The contract must dynamically track the pool of remaining assets and remaining shares. After each withdrawal, the pool size must be reduced by the amount paid out and the shares burned. This prevents the proportional calculation from using the original static pool size.
Mitigation Code (Conceptual)
This fix requires introducing two new state variables (remainingAssets and remainingShares) that are set equal to finalizedVaultAsset and totalWinnerShares in setWinner() and then dynamically reduced in withdraw().
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.