The March 13 flash loan attack against Euler Finance resulted in over $195 million in losses. It caused a contagion to spread through multiple decentralized finance (DeFi) protocols, and at least 11 protocols other than Euler suffered losses due to the attack.
Over the next 23 days, and to the great relief of many Euler users, the attacker returned all of the exploited funds.
But while the crypto community can celebrate the return of the funds, the question remains whether similar attacks may cause massive losses in the future.
An analysis of how the attack happened and whether developers and users can do anything to help prevent these kinds of attacks in the future may be helpful.
Luckily, Euler’s developer docs clearly explain how the protocol works, and the blockchain itself has preserved a complete record of the attack.
How Euler Finance works
According to the protocol’s official docs, Euler is a lending platform similar to Compound or Aave. Users can deposit crypto and allow the protocol to lend it to others, or they can use a deposit as collateral to borrow crypto.
The value of a user’s collateral must always be more than what they borrow. Suppose a user’s collateral falls below a specific ratio of collateral value to debt value. In that case, the platform will allow them to be “liquidated,” meaning their collateral will be sold off to pay back their debts. The exact amount of collateral a user needs depends upon the asset being deposited vs. the asset being borrowed.
eTokens are assets, while dTokens are debts
Whenever users deposit to Euler, they receive eTokens representing the deposited coins. For example, if a user deposits 1,000 USD Coin (USDC), they will receive the same amount of eUSDC in exchange.
Since they become worth more than the underlying coins as the deposit earns interest, eTokens don’t have a 1:1 correspondence with the underlying asset in terms of value.
Euler also allows users to gain leverage by minting eTokens. But if they do this, the protocol will send them debt tokens (dTokens) to balance out the assets created.
For example, the docs say that if a user deposits 1,000 USDC, they can mint 5,000 eUSDC. However, if they do this, the protocol will also send them 5,000 of a debt token called “dUSDC.”
The transfer function for a dToken is written differently than a standard ERC-20 token. If you own a debt token, you can’t transfer it to another person, but anyone can take a dToken from you if they want to.
Related: Liquidity protocol Sentiment exploited for over $500K
According to the Euler docs, a user can only mint as many eTokens as they would have been able to by depositing and borrowing over and over again, as it states, “The Mint function mimics what would happen if a user deposited $1,000 USDC, then borrowed $900 USDC, then redeposited that $900 USDC, to borrow $810 more USDC, and so on.”
Users liquidated if health scores drop to 1 or below
According to a blog post from Euler, each user has a “health score” based on the value of the eTokens held in their wallets vs. the value of the dTokens held. A user needs to have a greater dollar value of eTokens than dTokens, but how much more depends on the particular coins they are borrowing or depositing. Regardless, a user with enough eTokens will have a health score greater than 1.
If the user barely falls below the required number of eTokens, they will have a health score of precisely 1. This will subject them to “soft liquidation.” Liquidator bots can call a function to transfer some of the user’s eTokens and dTokens to themselves until the borrower’s health score returns to 1.25. Since a user who is barely below the collateral requirements will still have more collateral than debt, the liquidator should profit from this transaction.
If a user’s health score falls below 1, then an increasing discount is given out to the liquidator based on how bad the health score is. The worse the health score, the greater the discount to the liquidator. This is intended to make sure that someone will always liquidate an account before it accumulates too much bad debt.
Euler’s post claims that other protocols offer a “fixed discount” for liquidation and argues why it thinks variable discounts are superior.
How the Euler attack happened
Blockchain data reveals that the attacker engaged in a series of attacks that drained various tokens from the protocol. The first attack drained around $8.9 million worth of Dai (DAI) from the Dai deposit pool. It was then repeated over and over again for other deposit pools until the total amount was drained.
The attacker used three different Ethereum addresses to perform the attack. The first was a smart contract, which Etherscan has labeled “Euler Exploit Contract 1,” used to borrow from Aave. The second address was used to deposit and borrow from Euler, and the third was used to perform a liquidation.
To avoid having to repeatedly state the addresses that Etherscan has not labeled, the second account will be referred to as “Borrower” and the third account “Liquidator,” as shown below:
The first attack consisted of 20 transactions in the same block.
First, Euler Exploit Contract 1 borrowed 30 million DAI from Aave in a flash loan. It then sent this loan to the borrower account.
After receiving the 30 million DAI, borrower deposited 20 million of it to Euler. Euler then responded by minting approximately 19.6 million eDAI and sending it to borrower.
These eDAI coins were a receipt for the deposit, so a corresponding amount of dDai was not minted in the process. And since each eDAI can be redeemed for slightly more than one DAI, the borrower only received 19.6 million instead of the full 20 million.
After performing this initial deposit, borrower minted approximately 195.7 million eDAI. In response, Euler minted 200 million dDAI and sent it to borrower.
At this point, borrower was near their eDAI mint limit, as they had now borrowed about 10 times the amount of DAI they had deposited. So their next step was to pay off some of the debts. They deposited the other 10 million DAI they had held onto, effectively paying back $10 million of the loan. In response, Euler took 10 million dDAI out of borrower’s wallet and burned it, reducing borrower’s debt by $10 million.
Related: Allbridge offers bounty to exploiter who stole $573K in flash loan attack
The attacker was then free to mint more eDAI. Borrower minted another 195.7 million eDAI, bringing their eDAI total minted to around 391.4 million. The 19.6 million eDAI in deposit receipts brought borrower’s eDAI total to about 411 million.
In response, Euler minted another 200 million dDai and sent it to borrower, bringing borrower’s total debt to $400 million.
Once borrower had maximized their eDAI minting capacity, they sent 100 million eDai to the null address, effectively destroying it.
This pushed their health score well below 1, as they now had $400 million in debt vs. approximately $320 million in assets.
This is where the liquidator account comes in. It called the liquidate function, entering borrower’s address as the account to be liquidated.
In response, Euler initiated the liquidation process. It first took around 254 million dDAI from borrower and destroyed it, then minted 254 million new dDai and transferred it to liquidator. These two steps transferred $254 million worth of debt from borrower to liquidator.
Next, Euler minted an additional 5.08 million dDAI and sent it to liquidator. This brought liquidator’s debt to $260 million. Finally, Euler transferred approximately 310.9 million eDAI from borrower to liquidator, completing the liquidation process.
In the end, borrower was left with no eDAI, no DAI, and 146 million dDAI. This meant that the account had no assets and $146 million worth of debt.
On the other hand, liquidator had approximately 310.9 million eDAI and only 260 million dDAI.
Once the liquidation had been completed, liquidator redeemed 38 million eDAI ($38.9 million), receiving 38.9 million DAI in return. They then returned 30 million DAI plus interest to Euler Exploiter Contract 1, which the contract used to pay back the loan from Aave.
In the end, liquidator was left with approx. $8.9 million in profit that had been exploited from other users of the protocol.
This attack was repeated for multiple other tokens, including Wrapped Bitcoin (WBTC), Staked Ether (stETH) and USDC, amounting to $197 million in exploited cryptocurrencies.
What went wrong in the Euler attack
Blockchain security firms Omniscia and SlowMist have analyzed the attack to try and determine what could have prevented it.
According to a March 13 report from Omniscia, the primary problem with Euler was its “donateToReserves” function. This function allowed the attacker to donate their eDAI to Euler reserves, removing assets from their wallet without removing a corresponding amount of debt. Omnisica says that this function was not in the original version of Euler but was introduced in Euler Improvement Proposal 14 (eIP-14).
The code for eIP-14 reveals that it created a function called donateToReserves, which allows the user to transfer tokens from their own balance to a protocol variable called “assetStorage.reserveBalance.” Whenever this function is called, the contract emits a “RequestDonate” event that provides information about the transaction.
Blockchain data shows that this RequestDonate event was emitted for a value of 100 million tokens. This is the exact amount that Etherscan shows were burned, pushing the account into insolvency.
In their March 15 analysis, SlowMist agreed with Omniscia about the importance of the donateToReserve function, stating:
“Failure to check whether the user was in a state of liquidation after donating funds to the reserve address resulted in the direct triggering of the soft liquidation mechanism.”
Cointelegraph reached out to the Euler team to see if the attacker could have transferred their eTokens to another user or to the null address instead of donating. In response, a representative from the company told us that this would not have been possible because “transferring ETokens always performs a health check.”
The representative pointed out that line 345 of the EToken.sol contract calls the checkLiquidity function. “Omniscia and co are correct, this check should also have been inside the donateToReserves function,” the representative said.
Related: Euler team denies on-chain sleuth was a suspect in hack case
The two firms agreed that another major vulnerability in Euler was the steep discounts offered to liquidators. According to SlowMist, when a lending protocol has a “liquidation mechanism that dynamically updates discounts,” it “creates lucrative arbitrage opportunities for attackers to siphon off a large amount of collateral without the need for collateral or debt repayment.” Omniscia made similar observations, stating:
“When the violator liquidates themselves, a percentage-based discount is applied [...] guaranteeing that they will be ‘above-water’ and incur only the debt that matches the collateral they will acquire.”
How to prevent a future Euler attack
In its analysis, SlowMist advised developers on how to prevent another Euler-style attack in the future. It argued that lending protocols should not allow users to burn assets if this will cause them to create bad debt, and it claimed that developers should be careful when using multiple modules that may interact with each other in unexpected ways:
“The SlowMist Security Team recommends that lending protocols incorporate necessary health checks in functions that involve user funds, while also considering the security risks that can arise from combining different modules. This will allow for the design of secure economic and viable models that effectively mitigate such attacks in the future.”
A representative from DeFi developer Spool told Cointelegraph that technological risk is an intrinsic feature of the DeFi ecosystem. Although it can’t be eliminated, it can be mitigated through models that properly rate the risks of protocols.
According to Spool’s risk management white paper, it uses a “risk matrix” to determine the riskiness of protocols. This matrix considers factors such as the protocol’s annual percentage yield (APY), audits performed on its contracts, time since its deployment, total value locked (TVL) and others to create a risk rating. Users of Spool can employ this matrix to diversify DeFi investments and limit risks.
The representative told Cointelegraph that Spool’s matrix significantly reduced investor losses from the Euler incident.
“In this incident, the worst affected Smart Vaults, those designed by users to seek higher (and riskier) yields, were only affected for up to 35%. The lowest affected vault with exposure to Euler strategies (via Harvest or Idle), in comparison, was only affected by 6%. Some vaults had zero exposure and were thus not impacted,” they stated.
Spool continued, “While this is not ideal, it clearly demonstrates the ability of the Smart Vaults to provide tailored risk models and to distribute users’ funds among multiple yield sources.”
Cointelegraph got a similar answer from SwissBorg, another DeFi protocol that aims to help users limit risk through diversification. SwissBorg CEO Cyrus Fazel stated that the SwissBorg app has “different yield strategies based on risk/timeAPY.”
Some strategies are listed as “1: core = low,” while others are listed as “2: adventurous = risky.” Because Euler was given a “2” rating, losses from the protocol were limited to only a small portion of SwissBorg’s total value locked, Fazel stated.
SwissBorg head of engineering Nicolas Rémond clarified further that the team employs sophisticated criteria to determine what protocols can be listed in the SwissBorg app.
“We have a due-diligence process for all DeFi platforms before entering any position. And then, once we’re there, we have operation procedures,“ he said, adding, ”The due diligence is all about TVL, team, audits, open-source code, TVL, oracle manipulation attack, etc. […] The operation procedure is about platform monitoring, social media monitoring and some emergency measures. Some are still manual, but we’re investing to automatize everything based so that we can be extremely reactive.”
In a March 13 Twitter thread, the SwissBorg team stated that although the protocol had lost 2.2% of the funds from one pool and 29.52% from another, all users would be compensated by SwissBorg should the funds not be recoverable from Euler.
The Euler attack was the worst DeFi exploit of Q1 2023. Thankfully, the attacker returned most of the funds, and most users should end up with no losses when all is said and done. But the attack raises questions about how developers and users can limit risk as the DeFi ecosystem continues to expand.
Some combination of developer diligence and investor diversification may be the solution to the problem. But regardless, the Euler hack may continue to be discussed well into the future, if for no other reason than its sheer size and illustration of the risks of DeFi exploits.
This story was edited on April 7 to add comments from the Euler team clarifying how the EToken transfer function works.