Harvest Finance (FARM) hack explained simply
The Harvest Finance hack is an excellent show of DeFi's problems, solutions and entertainment value.
Harvest Finance is a DeFi yield aggregator. The idea is that users put money into it, and it automatically pools and invests those funds in various strategies that utilise other elements of the DeFi ecosystem. It's basically a semi-automated hedge fund that uses the power of DeFi shenanigans to deliver big returns, equivalent to around 20-50% APY.
On 26 October 2020, someone took advantage of a vulnerability in one of Harvest's programmed investment strategies to steal US$24 million in stablecoins. They could have conceivably taken a lot more, but apparently chose to stop at $24 million.
Prior to the attack Harvest had more than US$1 billion under management. At the time of writing that's down to about $400 million, with many users having fled the platform.
So, in simple terms, how exactly does someone yank a fortune off the Internet like this, especially from what was supposedly one of the safer DeFi applications?
Where it all began
This attack focused on USDT and USDC. Both are stablecoins that used the same strategy when deposited into Harvest. Specifically, USDC and USDT depositors automatically earned returns from farming on Curve Finance.
Curve is a liquidity pool with an emphasis on stablecoins. People can use it to swap between stablecoins with relatively low fees and slippage, or to earn returns on their stablecoins by being a liquidity provider, which is what Harvest depositors were doing. The returns for these liquidity providers come from a combination of transaction fees paid by other users making swaps on Curve, plus a bonus reward in the form of the CRV governance token.
The Harvest strategy was to simultaneously pick up the revenue from Curve swap fees, to sell the bonus CRV tokens and to top it all off with some of Harvest's own FARM token. This three-ingredient sandwich was fed to Harvest depositors, which is where the attractive returns came from.
Basically Harvest was functioning as an extra-profitable, but also extra-risky, way of putting funds into the Curve pools.
So far so good.
And then it all went wrong
As is typical for yield-bearing DeFi applications, Curve pays yields based on proportional share of the liquidity pool. If you have 10% of all the funds in the pool, you get 10% of the yields. However, everyone's proportional share of the pool is constantly changing as prices shift, and as new deposits and withdrawals are made.
This means both Curve and Harvest alike need to keep a running real-time price feed of the Curve markets, to ensure yields keep being allocated correctly.
However, Harvest also needs to check prices whenever someone makes a deposit or withdrawal, to make sure it correctly calculates how much of a share someone gets when they enter a pool (deposit), and how much of a share they give up when they leave the pool (withdrawal).
That's where things went wrong.
Harvest's big mistake was that it also used Curve pricing information to calculate the value of assets being deposited and withdrawn in its Curve farming pools. As such, if someone could find a way to manipulate prices on Curve they could also claim a larger share of the pool on deposit, and give up a smaller share of the pool on withdrawal, with the functional result of stealing funds from anyone else in the same Harvest pool.
The basic idea behind this exploit is to drive up the prices of the deposited asset when making a deposit, then drive down the prices before making a withdrawal. Because one's proportional ownership of the pool remains consistent even as the prices change, this ends up being like stealing money from the pool.
In theory this is relatively straightforward. To give a basic example:
- 1 USDT = 1 pool share.
- Drive up USDT prices until 1 USDT = 1.1 pool shares.
- Deposit 100 USDT to get 110 pool shares.
- Drive USDT prices back down until 1 USDT = 1 pool share again.
- Withdraw 110 USDT.
Congratulations, you have successfully withdrawn 10 USDT of other people's money from the pool.
That's basically what happened to Harvest, to the tune of $24 million.
How it went down
First things first, the hacker needed two piles of money: one pile for Curve and one for Harvest.
The Curve pile was about $18 million. Its purpose was to be swapped back and forth between USDT and USDC on Curve, to move the prices of the USDT and USDC there and in turn change the prices Harvest was using to valuate deposits and withdrawals. It needed to be big enough to meaningfully impact prices.
The second pile was $50 million. Its purpose was to be deposited to and withdrawn from Harvest in time with the price swings. The bigger this pile, the bigger the profits from the stunt.
Fortunately for both the attacker and the spirit of egalitarianism, they didn't actually need to have this money to pull off the attack. Thanks to "flash loans", this attacker was able to go in with only 10 Ether (~US$4,000) to pay for gas, before walking away with $24 million at the end.
Flash loans in brief
Flash loans are a fascinating and frequently terrifying example of how blockchain expands the possibilities of finance. They let anyone borrow practically any amount, roughly $70 million in this case, at relatively low cost, with no collateral and with theoretically zero risk to lenders.
The catch is that these funds can only be borrowed extremely briefly. The loan needs to be borrowed and repaid, along with a small fee, all within the same Ethereum transaction. If the loan and fees aren't repaid prior to the conclusion of the transaction it essentially undoes itself, and doesn't execute.
For clarity, it should be mentioned that when we talk about an "Ethereum transaction" in this context, we're talking about a script with multiple steps rather than a simple token transfer. That's what the attacker used here. They scripted the entire attack as an Ethereum transaction beforehand and then executed it all pretty much simultaneously.
Flash loans open up a slew of interesting possibilities. Most notably, it lets anyone (with the right know-how) carry out arbitrage trading without any capital. It also lets people carry out these kinds of hacks and manipulate markets dramatically with minimal upfront cost and even pass through governance votes. Earlier this week, for example, someone used a flash loan to borrow enough governance tokens to force a vote their way.
In essence, flash loans basically mean anyone can access an almost unlimited amount of money to do whatever they want with, as long as it can be done fast, on-chain and without losing the money.
Step by step, the first time the hacker ran their flash loan-powered attack script it executed like so, according to Harvest's postmortem:
- Take out a flash loan of 18.3 million USDT and 50 million USDC.
- Convert 17.2 million USDT into USDC on Curve, pushing up Curve's USDC prices.
- Deposit 50 million USDC into Harvest, and thanks to elevated USDC prices receive 51.5 million shares (known as fUSDC).
- Convert the USDC back into USDT on Curve, pushing prices back down.
- Withdraw 50.6 million USDC. In other words, convert 51.5 million fUSDC into 50.6 million USDC at prevailing rates.
The attacker repeated this sequence 17 times over 4 minutes for the USDC pool, then mirrored it 13 times within 3 minutes for the USDT pool.
In the end, they withdrew exactly 13 million USDC and 11 million USDT to another wallet, tossed the remainder (some $2.5 million) back to the Harvest deployer contract like spare change and then set about laundering their freshly-acquired gains.
The semi-wild west
Harvest pulled all its vulnerable pools out of action as soon as it became clear what was going on. It then put out a very polite and complimentary bounty on the attacker, while strongly implying it knew the identity of the attacker.
This appears to have been a bluff.
Firstly, because the attacker's identity is apparently still unknown. Harvest later upped the bounty to $1 million and is still working on tracking the missing money as it's laundered.
Secondly, because the attacker clearly knows what they're doing. Based on how they carried out the attack and are laundering the funds, it's reasonable to assume they didn't actually leave behind any personally identifying information. That said, the list of people who could have done this is very short.
On the way in, they used a fresh wallet funded with Ether from the Ethereum-based Tornado Cash mixing service. On the way out, they swiftly converted funds into Bitcoin via renBTC.
According to Harvest's latest update, this Bitcoin is now being laundered through a diverse selection of several dark web marketplaces and exchanges, specifically: Flugsvamp 3.0, Wm_cash, Coins.ph, Treddr, Kraken, Binance and Huobi.
While centralised exchanges may appear an unusual choice for this kind of laundering, it's worth noting that it's not clear whether there was ever any crime committed, whether exchanges should feel obligated to do something about this laundering or whether this particular incident is technically more of an exploit, hack, heist, trade, arbitrage opportunity or example of someone simply using a decentralised application as intended.
A "victimless" "crime"
One could argue that Harvest is not a legal entity of any sort. It's just a computer program, built and maintained primarily by persons unknown, with a community of users who chose to collectively put about a billion dollars into it.
Presumably they did so with the understanding that by making deposits they were exposing themselves to many smart contract risks on different applications, as well as functionally just buying fTokens. And since no fTokens were ever stolen, how could anyone have been robbed? The amount of underlying collateral decreased so the value of the fTokens dropped, but that's arguably just market forces, not robbery. Someone saw an arbitrage opportunity, which they created, and took it.
On the other hand, one could also argue that this kind of pedantic nitpicking on technicalities is stupid because obviously no one meant to lose their money this way and obviously this was not the intended behaviour of the program, technicalities be damned. This is a real problem and all that posturing on "technical correctness" doesn't do anything to help solve it. What are you, some kind of jerk?
To which one might counter that the hacker in this case willingly chose to stop at $24 million when they could have taken hundreds of millions instead, which presumably earns them some "niceness points" since that's what we're apparently measuring now, and it's not like putting bounties on people is especially nice either, to which one might reply...
And so on.
While this kind of debate, which always picks up after these sorts of incidents, probably won't end soon, the vulnerability at least can be fixed.
Here's the headscratcher though:
Harvest can't use a Curve price feed to calculate the value of its pool shares, because accurately capturing Curve prices leaves it vulnerable to arbitrage attacks as seen here.
But Harvest also can't not use a Curve price feed to calculate the value of its pool shares, because its pool is going into Curve and by not accurately capturing Curve prices Harvest would also be vulnerable to arbitrage attacks.
A perfect solution might be tricky.
The nature of this problem is such that it can't be immediately solved with oracles such as Chainlink's, which is usually the go-to suggestion whenever something price feed-related breaks down in DeFi.
Harvest's own existing anti-arbitrage security measures probably can't handle it either. Harvest was trying to prevent exactly this type of thing from happening by checking for arbitrage of up to 3% and blocking deposits and withdrawals if the price difference exceeded that. Unfortunately, 3% was much too high in this case where the attacker only induced price swings of about 1%.
The reason the attacker made so much was because they had a $50 million flash loan wrecking ball which they swung 30 times in the space of 7 minutes. The profit from each swing was relatively small, an average of $800,000 or so per swing, but it added up extremely fast, all while sneaking under that 3% threshold.
So while there could be a potential fix in bumping that 3% threshold downwards, it would have to be so low that it would probably start barring normal user deposits and withdrawals at an annoyingly high rate, without necessarily being able to stop a sophisticated attacker.
Another potential fix Harvest floated in its post-mortem was to add an intermission of sorts to its deposit and withdrawal scripts, to stop people making a Harvest deposit and withdrawal in the same transaction. This would prevent flash loan attacks, at the (quite reasonable-sounding) cost of a UX change and maybe slightly higher gas fees for users.
Harvest also noted that it could do away with the whole "exchange tokens for fTokens" mechanism and just have user deposits and withdrawals made directly in the underlying asset. This would prevent such attacks because then the attacker would just be manipulating the value of their own deposits along with everyone else's.
The downside is Harvest then wouldn't be able to perform such lucrative farming strategies with such ease, and users wouldn't necessarily earn yields in the same currency as their deposit which starts to undermine Harvest's value proposition.
It's a tricky problem.
However, Curve offered up its own answer: virtual price feeds. These are intended to deliver simulated, non-manipulable Curve market prices to prevent this kind of thing from happening. The solution was there all along, and had Harvest approached Curve to ask about risks before pouring hundreds of millions of dollars into the pool this crisis might have been averted.
Yup, it's just another day in DeFi.
Disclosure: The author owns a range of cryptocurrencies at the time of writing