31488 - [SC - Critical] Merging tokens allows multiple Flux accruals wi...
Last updated
Was this helpful?
Last updated
Was this helpful?
Submitted on May 20th 2024 at 10:37:06 UTC by @Holterhus for
Report ID: #31488
Report type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol
Impacts:
Theft of unclaimed yield
A token that has claimed Flux in the current epoch can be merged with a token that has not yet claimed Flux. This allows the already claimed token to transfer its Flux and reuse its voting power through the non-voted token. An attacker can exploit this by repeatedly merging voted tokens with fresh, non-claimed tokens to perpetually claim Flux using the same initial voting power. This allows an attacker to accrue unlimited Flux instantly.
Every token is allowed to accrue Flux once per epoch. The amount of claimable Flux is calculated in the claimableFlux()
function, which multiplies the current voting power of the token by fluxPerVeALCX
.
In the VotingEscrow
contract, the merge()
function allows a user to combine the voting power of token from
and token to
, with the from
token ultimately burned while the to
token continues with the combined voting power. Nothing prevents someone from claiming Flux with the from
token, transferring its voting power to a fresh to
token, and claiming Flux again. This will double count the underlying voting power and allow claiming multiple times in an epoch.
An attacker can repeat the accrual of their Flux multiple times in an epoch using the same underlying voting power. This allows for an infinite mint of Flux.
See the PoC below.
I have created the following test file and added it to the tests/
directory:
Running the command forge test -vvv --match-test testUnlimitedAccrueFluxBug --rpc-url $ETH_RPC_URL
gives the following result:
Since 39432419229826226642800 / 98583512563800063892800
is approximately 3.999
, this specific example shows that the Flux from tokenIds[0]
can be quadruple counted by using 4 fresh token ids.