Let's start with Compound — this case is simpler because the inflation attack here occurs in a more classic way.
A
cToken conceptually resembles a vault.
- cToken.totalSupply() represents the total number of shares.
- exchange_rate is the amount of underlying_token that can be exchanged for one unit of cToken, meaning it represents the share price.
The protocol uses a precision multiplier of
1e18 for exchange rate calculations. However, this does not prevent rounding errors, which are key to this hack. For simplicity, we will omit the precision multiplier everywhere except in
step 5.
The empty pool used for the attack will be referred to as
The Pool (this corresponds to the
cToken contract).
At the beginning of the transaction, the attacker takes a large flash loan from an external protocol. Then, they perform the following steps:
1. Deposit a relatively small
initial_deposit into The Pool.
The number of minted shares of
cToken for the attacker is determined by the initial exchange rate
initial_rate. It is defined in the code as
CToken.initialExchangeRateMantissa and set during The Pool's initialization.
As a result, the hacker receives
initial_rate * initial_deposit cToken shares.
2. Withdraw
initial_rate * initial_deposit - 2 shares.
After this, only 2 shares remain in the attacker's balance.
This sequence of steps (1–2) is necessary because
initial_rate is quite high, and even a 1 wei deposit of
underlying_token results in a balance of more than 2 shares.
3. Transfer the remaining part of the flash loan funds to the
cToken contract balance.
The exchange rate of
cToken is determined by the contract's balance of
underlying_token:
CToken.exchangeRateStoredInternal():