In the first part of this article series, we looked at flash loans and how they work. In this part, we will be exploring the yDAI exploit. However, in order to understand the exploit itself, we need to understand what liquidity pools are and how they work.
This is a 3-part article series - read the first part on flash loans.
Liquidity pools and Automated Market Makers (AMMs) are both crucial in understanding the exploit we will be covering. At a high level, the exploiter makes use of certain characteristics of a liquidity pool to force a DeFi platform to take bad trades, with the exploiter positioning themselves on the opposite side of the bad trades. This allows them to profit from the DeFi platform’s loss.
A liquidity pool on a blockchain is simply a basket which traders interact with to trade between the assets. Typically a pool only consists of two assets, and these can be anything. If traders want to trade between ETH and USDC, they will interact with an ETH-USDC liquidity pool. If a trader wants to buy ETH, they will deposit USDC and remove ETH from the pool (and the reverse operation if they want to sell ETH).
However, for any trade to be possible, there needs to be some ETH and USDC in the pool. This is taken care of by liquidity providers. In this example, liquidity providers will supply both ETH and USDC in specific amounts. In return, they passively earn trading fees from traders.
Trading in a liquidity pool changes the supply of its two assets. Intuitively, we can already see that to reflect the new amounts of the two assets, prices must change. If a trader deposits USDC and removes ETH (so trader buys ETH), the latter is now more scarce and so commands a higher price. The pricing is determined by the type of AMM model used.
One type of AMM model determines the price from the ratio of the two assets: if the pool contains 3 ETH and 6,000 USDC, the mid-market price will be 6,000/3 = 2,000 USDC per ETH. However, trades will never occur at this exact price, as this does not account for the trade size.
Instead, a trader will incur slippage. If they want to buy 1 ETH, they will pay 3,000 instead of 2,000 USDC. This is a slippage of 50%, as they are paying 50% more than what the quoted mid-market price is. This happens because the product of the two token quantities must remain constant:
We just described a constant product AMM, and see why all trades on DEXs using this model will incur slippage.
Suppose now that the pool consisted of two stablecoins, DAI and USDC. We know that the constant product model always incurs slippage, so it wouldn’t make sense to always be penalised with slippage for assets that are both supposed to represent $1. A better model would be a constant sum AMM, where instead of multiplying the two token quantities, we simply add:
The net result is a simple swap of 2,000 USDC to 2,000 DAI. This makes sense as both are stablecoins with equal value. However, there is a problem with this approach. Suppose another trader wants to do the same, and takes the remaining 1,000 DAI. The pool now only has 7,000 USDC with zero DAI, rendering it only half as useful as now only DAI to USDC trades are possible. This model is never used in practice, but helps us see how the StableSwap AMM functions.
Without going into the math, curve.fi (the largest stablecoin liquidity pool platform on the Ethereum network) uses the StableSwap model, which is essentially a combination of the two. For instances where there are enough tokens for both of the assets in the pool, it follows a constant sum model. When the quantities for each stablecoin have significantly deviated with respect to each other, it follows a constant product model. The StableSwap AMM penalises the trader who further depletes the stablecoin that’s low in supply with large slippage, or rewards the trader who provides it.
We can also apply the bolded statement to liquidity providers. With the StableSwap AMM, it is possible to provide single assets into the liquidity pool. Providing assets that are already in high supply will result in a penalty, whereas providing assets that are in short supply will result in a reward.
One final point to note is that this AMM model can be extended to any number of assets, not just two. In Curve’s case, the largest liquidity pool is the ‘3pool’, which consists of three assets: USDC, USDT, and DAI. This is also the pool involved in the exploit.
From the above bolded statements, we can already start to think of possible attack patterns. Suppose we add a large amount of liquidity to a StableSwap-based liquidity pool. The volume is large enough to push the AMM to start functioning as a constant product AMM, where any additional liquidity provided for the same asset will be heavily penalised. If we can somehow force someone else to do this (provide liquidity after us for the same asset), then we can take advantage of the second part of the bolded statement: we remove the excess and are rewarded for doing so. A high-level overview of an attack will look something like this:
Note that in the first step, we require a large deposit. This is made possible with a flash loan and provided we repay the loan by the end of the transaction, the exploit will be validated. The liquidity pool used in the example is Curve.fi’s 3pool, and the platform being manipulated is typically a vault. The following section describes the vault which was exploited.
Vaults in DeFi are automated strategies to maximise the yield of the deposited assets. Some lending-focused vaults will actively switch between lending stablecoins on Aave and Compound, depending on whichever is providing a higher interest rate. Other vaults will provide liquidity to several decentralised exchanges to earn trading fees, switching to whichever provides higher fees. There are other types of strategies, and many vaults will combine these to achieve higher returns. By depositing to these vaults, users benefit from active management of their funds in an automated fashion.
In the yDAI vault’s case, the strategy is as follows:
These three steps are what the exploited vault would automate. It allowed depositors to follow a compounding strategy without having to do the manual work of regularly carrying out the several transactions and paying the associated gas fees.
Now that we have an understanding of liquidity pools, AMMs and the yDAI vault, we now have all the necessary pieces to understand this exploit. The sequence of steps are as follows:
This summary has simplified some elements and omits token amounts for simplicity, but the idea remains. As reported in the disclosure by Yearn Finance, part of the reason why this exploit was profitable was due to temporarily disabling withdrawal fees from the vault to encourage migration to v2 vaults.
The exploit boils down to identifying a flaw in how deposits to the vault are accounted for, and how this can be manipulated for profit. We can see that identifying the exploit requires someone with substantial knowledge of smart contracts. However, with the availability of flash loans, executing the exploit does not require large capital (200,000+ ETH in this case). As we saw in the first part of the flash loans article, flash loans are simply a tool for enabling an efficient market in DeFi. In this case, flash loans also enable a larger group of people to carry out exploits. Whether the attacker returns the funds and acts altruistically (whitehats) or keeps the money (blackhats) is a separate issue, though both push DeFi through its teething period.
The final part of this series will propose an initial framework to quantify a platform's susceptibility to flash loan attacks.