In this tutorial on flash loans we will be creating a Solidity smart contract which takes a flash loan from Uniswap v3. This allows you to borrow huge amounts of capital with the catch that you have to pay it back in the same block or the whole transaction is reverted.
How Do Flash Loans Work
Flash loans allow users to borrow a significant amount of funds from a liquidity pool and pay it back within a single transaction, without requiring any collateral. These loans are made possible by the composability and programmability of smart contracts on blockchain platforms like Ethereum.
Flash loans rely on the atomicity of blockchain transactions. If any part of the loan transaction fails, the entire transaction is reverted, ensuring that the lending pool remains secure.
A typical flash loan follows these steps:
- User calls a smart contract function which requests the loan from a DeFi platform like Uniswap or Aave
- The funds come out of the liquidity pool and are sent to the contract
- The users smart contract has a call back function which is called from the liquidity pool after funds have been sent
- This callback function includes the code for whatever the user wants to do with the funds and then at the end of the function it pays the loan back plus a fee
- If the user doesn’t pay back the loan + fee the whole transaction is reverted and original state is restored
Uniswap Flash Loan Code
The full source code for this demonstration is available in the Solidity Snippets repo: https://github.com/jamesbachini/Solidity-Snippets/blob/main/contracts/UniswapFlashLoan.sol
The code starts by setting up interfaces for ERC20 tokens, the Uniswap v3 liquidity pool and the Uniswap factory. In the constructor argument we take in two tokens and the fee and sort out which is token0 and which is token1. Note the ordering of these is switched around because Uniswap always has the lower value token address as token0.
Then we get on to the interesting bit
function flashLoan(uint _token0Amount, uint _token1Amount) external {
bytes memory data = abi.encode(_token0Amount, _token1Amount, msg.sender);
IPool(pool).flash(address(this), _token0Amount, _token1Amount, data);
}
function uniswapV3FlashCallback(uint _fee0, uint _fee1, bytes calldata _data) external {
require(msg.sender == address(pool), "Only Callback");
(uint token0Amount, uint token1Amount, address msgSender) = abi.decode(_data, (uint, uint, address));
// do something to generate enough to payback the loan and fee
balanceCheck0 = IERC20(token0).balanceOf(address(this));
balanceCheck1 = IERC20(token1).balanceOf(address(this));
if (_fee0 > 0) IERC20(token0).transfer(address(pool), token0Amount + _fee0);
if (_fee1 > 0) IERC20(token1).transfer(address(pool), token1Amount + _fee1);
}
The flashLoan function takes two amounts for how much you want to borrow from either side of the pool. We can then encode some data to send with the request for funds. This data will be sent back in the callback function.
We execute Uniswap’s flash() function and wait to receive the callback.
The liquidity pool contract will then call back uniswapV3FlashCallback which is a pre-defined function name that must remain constant. It provides the fees for the loans and the data we passed through previously. We decode the data and then check the fee requests to pay back the loan.
Note that you’ll need excess tokens in the contract because you need to pay a fee of 0.3009027% on the loan amount. So if you borrow 1 WETH token you’ll need to pay back 1.003009027 WETH so you need to send the contract some tokens first.
Note that the code is not production ready and lacks any user permissions or security/sanity checks.
Flash Loan Use Cases
Flash loans can be used for a multitude of reasons. Here are some potential use cases.
Arbitrage
Flash loans can be used to take advantage of price discrepancies across different decentralized exchanges or liquidity pools. Borrowers can borrow funds, perform quick trades to exploit these price differences and then repay the loan, keeping the profits.
In my experience this never works as well as you would hope because the fees and additional slippage eat in to the trade profitability.
Exploits
Flash loans have been used a lot to unbalance liquidity pools. An attacker will borrow a huge amount of one asset, use this to create a massive price discrepancy in a vulnerable market and then trade at the manipulated price for a profit.
Voting
Many governance tokens that trade on Uniswap have voting rights and if a protocol isn’t aware of the risks then flash loans can be used to influence the outcome of DAO votes.
Liquidations
Flash loans can be utilized for performing liquidations within decentralized lending platforms. Borrowers can borrow funds, use them to repay a large outstanding loan that is in default and then repay the flash loan, earning liquidation rewards.
At time of writing Uniswap has $3.19 billion dollars worth of collateral available to be borrowed. Would you like to be a billionaire for one block?
These are just some examples of how flash loans can be used for good and bad purposes. Understanding how they work and how it influences the DeFi ecosystem can help prevent the downsides and utilise them as a tool in the blockchain developers tool kit.