10

What do developers need to be aware of to make their contracts and systems safe against flash loans?

What do they have to think about and protect against?

1
  • 2
    Thank you for the 2 swift answers so far, both making good and different points. Will give a bit more time in case there are other answers, before having to choose a "best" answer.
    – eth
    Commented Oct 25, 2020 at 9:39

3 Answers 3

8

As we work on flash loan attack debriefs, we can gather a lot of information about what could have been taken as preventative measures. It's important to understand how these flash loans work before diving into root causes and analytics.

Flash loans are loans given out without collateral and returned in the same transaction that they were lent out. This is possible in the world of smart contracts, because the code can verify whether or not it will be able to return the loan at the end of the block. If yes, it's able to lend as much as the user needs. This is where the attack vectors come in.

We can draw from a study done by academics at the Imperial College London that looks at two notorious attacks in particular. Their main finding is as follows:

Flash loan attacks are typically Price Oracle Attacks / Oracle Manipulation

This seems to be the #1 cause of attacks at the moment, by far. What is important to note, is that decentralized exchanges are not decentralized oracles. Using Uniswap, Sushiswap, or Curve to get pricing information to execute trades is pulling data from protocols whose price depends solely on liquidity. Looking at the infamous ground zero bZx attack that sparked this wave of attacks, we can see exactly what happens. These flash loans are used to crash and manipulate the price of these decentralized exchanges, which most projects deemed safe to use. The issue here relies in the fact that these protocols prices depend entirely on liquidity. Here's basically how it works:

  1. User takes a massive loan out
  2. User uses that loan, to buy out all the liquidity on one side of a liquidity pool, causing a massive drop in price
  3. This price is being used by another protocol to determine or execute some code, ie, they peg the price of their asset to the protocols asset. So the user swaps for next to nothing for a massive profit.
  4. Then pays back the original loan, and leaves with massive gains

What auditors and software engineers need to do, is make sure they don't get pricing or data that rely on DEXes. Uniswap is a decentralized exchange, NOT a decentralized pricing oracle, they are each a centralized data point, and using them as such is dangerous to a point where in the last 2 months, ~5 protocols have been hacked for over $30M combined.

Prevention

This all being said, to prevent these attacks from happening, you need to make sure that when you're getting pricing information, or any data at all, it needs to come from decentralized oracles and get the data from decentralized Chainlink Price Feeds, if it's price data. For any other data, you need to get your data from a decentralized network of Chainlink Oracles. And as an engineer, anyone can customize their oracle network to make it as wide or narrow as they like.

This all being said, at this point, there is enough information out there that if a protocol gets hacked and that protocol paid an auditor, that auditor needs to be held accountable as well, as missing centralized price oracles in audit reviews is going to make this keep happening. Many projects who have been hacked have integrated Chainlink price feeds as there backbone for data reliability and have since stabilized.

The second most popular attack is reentrancy attacks

To quote:

A reentrancy attack can occur when you create a function that makes an external call to another untrusted contract before it resolves any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the effects were resolved.

Luckily, we know that fallback functions have been patched to use only 2300 gas, so this can be helpful here. Consensys recommends using call instead of transfer or send to protect yourself against these.

I think this is a mistake, I think transfer is actually safer, and just as long as you do all work before you make an external call, ie take something like:

func withdraw() public {
  token.transfer(to_address, amount);
  balance = 0;
}

and change it to:

func withdraw() public {
  balance = 0;
  token.transfer(to_address, amount);
}

Other notes

It should be mentioned that there are some "hacks" that will also fix this issue. Making all your important transactions happen over the course of 2 or more blocks can be a hacky way to get around some of this. Also you will always want to use safeMath when working with massive integers, and need to make sure your overflows don't break your protocol. Note: I am Alpha Chain CEO & Chainlink DevRel

3
  • 1
    "Flash loans are loans given out without collateral and returned in the same block" - Is this correct? Do you not mean to say that they are loans which are returned in the same TRANSACTION not block? Commented Nov 30, 2020 at 19:43
  • You are correct. Updating. Commented Nov 30, 2020 at 21:26
  • 1
    I am not exactly sure, but I checked Uniswap V2 code, either I failed to find it or they haven't implemented decentralized oracle network based or linked with chainlink feed. Commented Mar 19, 2021 at 19:48
3

Flash loans violate two common intuitive assumptions.

1. "Nobody has that much ETH"

Problems can arise when for example:

  • Your contract uses a non-linear formula with an ETH amount as parameter
  • Your contract relies on nobody having more than a certain amount of ETH to prevent integer overflow

2. The balance of an address (and ETH received from an address) is owned by that address

Problems can arise when for example:

  • Your contract verifies whether an address has some minimum balance by simply checking its current balance
  • Your contract verifies whether an address has some minimum balance by receiving that minimum balance and sending it back in the same transaction
  • Your contract has control flow based on ETH amount (e.g. offering a discount for large purchases of tokens, and offering partial refunds such that a flash loan could be paid back)
3

Generally speaking, if your system gives the users different terms under different scenarios, then you need to remember that a user can issue a flash-load attack on it.

For example, if your pool gives the users a better conversion-rate when the pool is deeper (higher reserve balances), then a user can attack it by executing atomically:

  • Add liquidity and increase the pool's depth
  • Make a conversion from one reserve to another
  • Remove liquidity and decrease the pool's depth

This sort of attack can gradually (or even abruptly) drain out your pool.

UPDATE:

The example above is feasible only if the user is guaranteed to receive in the last step the same liquidity (reserve amounts) invested in the first step, regardless of the state (reserve balances) of the pool.

This restriction is not guaranteed in most pools currently deployed, making the illustrated flash-loan attack useless under these circumstances.

5
  • 1
    Why does the problem in your exemple only arise with flash loans ? Can't it be done by someone just having a lot of money ? What prevents it ?
    – Xavier59
    Commented Oct 25, 2020 at 22:13
  • 1
    @Xavier59: You are correct, it's the same thing. But more specifically (given the example in my question), you typically need a huge amount in order to make a small gain. And that is easier (risk-free) to conduct via flash-load, rather than by temporarily acquiring that huge amount of tokens. Commented Oct 26, 2020 at 5:35
  • @goodvibrations Thanks. So the 2 points of flash loan are 1) the ability to make the attack without having any money and 2) the atomicity of the transactions
    – Xavier59
    Commented Oct 26, 2020 at 18:05
  • @Xavier59: Yes, but like you said, the 'without having any money' requirement can be replaced with 'having a lot of money'. In either case, the amount of money only determines your ultimate profit pre transaction. Of course, it has to be larger than the gas fee, otherwise it's not worth it. But the important part is to issue those actions atomically, on a pool which gives a better conversion rate when its balances are higher. Commented Oct 26, 2020 at 18:11
  • @Xavier59: Please see my update. Commented Oct 29, 2020 at 12:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.