Introduction
Flash loans in Balancer V3 allow users to borrow assets without collateral, as long as the borrowed amount is repaid within the same transaction. This page explains the logic behind executing a flash loan and settling it correctly using the Balancer V3 Vault.
Prerequisites
- Basic understanding of Solidity and smart contract development.
- Familiarity with ERC20 token approvals and transfers.
- Knowledge of Balancer V3's Vault architecture.
Flash Loan Process
- Unlocking the Vault: Flash loans in Balancer V3 operate within a transient unlocked state, where the Vault grants temporary access to funds.
- Receiving Funds: The requested asset is transferred to the borrower contract.
- Executing Transactions: The borrower contract can use the funds for any logic, such as liquidations, lending, or arbitrage.
- Repaying the Loan: The borrowed amount must be returned before the transaction ends.
- Settling with the Vault: The contract ensures that the Vault registers the repayment and completes the process.
Example Contract Implementation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVaultMain } from "./IVaultMain.sol";
contract BalancerFlashLoan {
IVaultMain public immutable balancerVault;
address public immutable loanToken;
address public owner;
constructor(address _balancerVault, address _loanToken) {
balancerVault = IVaultMain(_balancerVault);
loanToken = _loanToken;
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function executeFlashLoan(uint256 amount) external onlyOwner {
// Prepare calldata for the vault callback
bytes memory userData = abi.encode(amount);
balancerVault.unlock(abi.encodeWithSelector(this.receiveFlashLoan.selector, userData));
}
function receiveFlashLoan(bytes memory userData) external {
require(msg.sender == address(balancerVault), "Unauthorized callback");
// Decode flash loan amount
uint256 amount = abi.decode(userData, (uint256));
// Send some tokens from the vault to this contract (taking a flash loan)
balancerVault.sendTo(IERC20(loanToken), address(this), amount);
// Execute any logic with the borrowed funds (e.g., arbitrage, liquidation, etc.)
// Repay the loan
IERC20(loanToken).transfer(address(balancerVault), amount);
// Settle the repayment
balancerVault.settle(IERC20(loanToken), amount);
}
}
Info
Key Considerations when using Flash Loans:
- Ensure Approval: If interacting with other contracts, ensure the Balancer Vault has sufficient token allowance.
- Transaction Atomicity: The entire flash loan execution and repayment must occur within the same transaction.
- Settlement Accuracy: The
settle()
function is required to inform the Vault that the borrowed funds have been repaid and to settle the balances.