57587 sc critical earmark reduction of transmuterdifference does not always account for the full transmuter balance diff which can cause permanent earmark to accrue in alchemist
The _earmark() function subtracts any increases in Transmuter coverage from the current amount to be earmarked. This is done so that redemption amounts that can be covered by the transmuter are not earmarked unnecessarily. The Transmuter collateral increase is reduced from the currently earmarked amount (up to the full amount) and the lastTransmuterTokenBalance state is updated to the current balance. See code below:
The problem occurs because, while the amount of transmuterDifference accounted for is capped by the new earmarked amount in the current call, the lastTransmuterTokenBalance is updated to the full current balance. This means any transmuterDifference amount not accounted for in the current call will never be accounted for.
This may result in an amount of earmark reserved in Alchemist that is redundant and never cleared, causing part of CDP owners collateral to remain permanently and unnecessarily locked.
Consider for following scenario:
Starting state: Alchemist has 100 debt, 0 earmark, no redemptions created or vested.
An amount of 50 collateral tokens (worth 50 debt tokens) is sent to the transmuter directly.
At the next _earmark() call (following a poke() for example) transmuterDifference is X, but since no new earmark is added in the call, it is not reduced from earmark. The lastTransmuterTokenBalance is updated to X.
A redemption is created for 50 debt tokens
As timeToTransmute elapses, 50 debt tokens are earmarked for the redemption
When the redemption matures the redeemer calls claim redemption, and transmuter pays the entire redemption from its collateral coverage.
The 50 debt tokens earmarked for the redemption are therefore not cleared and remain in the system indefinitely, blocking CDP owners from fully withdrawing their collateral.
Impact Details
Permanent freezing of part of CDP owners collateral due to the redundant earmark
Copy the code below into the IntegrationTest contract is tes/IntergrationTest.t.sol
Run with FOUNDRY_PROFILE=default forge test --fork-url https://mainnet.gateway.tenderly.co --match-test testEarmarkTransmuterIncreasePOC -vvv --evm-version cancun