Welcome to the definitive guide on instant flash loans! Whether you’re a DeFi veteran looking to optimize your strategies or a newcomer eager to understand this revolutionary financial tool, this comprehensive walkthrough will provide you with everything you need to know to successfully navigate the world of flash loans.
## Table of Contents
1. [Understanding Flash Loans](#understanding-flash-loans)
2. [The Technology Behind Instant Flash Loans](#technology-behind-flash-loans)
3. [Benefits and Risks of Flash Loans](#benefits-and-risks)
4. [Popular Flash Loan Platforms](#popular-platforms)
5. [Setting Up Your Environment](#setting-up-environment)
6. [Creating Your First Flash Loan](#creating-first-loan)
7. [Advanced Flash Loan Strategies](#advanced-strategies)
8. [Real-World Use Cases](#real-world-use-cases)
9. [Security Considerations](#security-considerations)
10. [Troubleshooting Common Issues](#troubleshooting)
11. [Future of Flash Loans](#future-of-flash-loans)
12. [Regulatory Landscape](#regulatory-landscape)
13. [Glossary of Terms](#glossary)
14. [Resources and Further Learning](#resources)
Flash loans represent one of the most innovative financial instruments to emerge from the decentralized finance (DeFi) ecosystem. Unlike traditional loans that require collateral, credit checks, and extended repayment periods, flash loans operate on a fundamentally different principle: they must be borrowed and repaid within a single blockchain transaction.
An instant flash loan is a type of uncollateralized loan where the borrower can access substantial liquidity without providing any upfront collateral, as long as the borrowed amount plus fees are returned within the same transaction block. If the repayment condition isn’t met, the entire transaction is reversed as if it never happened, thanks to the atomic nature of blockchain transactions.
This innovative lending mechanism leverages the unique properties of blockchain technology to create a financial instrument that would be impossible in traditional finance. By eliminating the need for collateral verification, credit checks, and lengthy approval processes, flash loans democratize access to large-scale capital, albeit for very brief periods.
The concept of flash loans was first introduced by Aave in 2020, revolutionizing how liquidity could be accessed in DeFi. Since then, several platforms have implemented their own versions of flash loans, each with slight variations in fees, maximum loan amounts, and technical implementation.
The evolution of flash loans represents a significant milestone in DeFi’s maturation, showcasing how blockchain technology can reimagine fundamental financial concepts. What started as an experimental feature has quickly become a cornerstone of DeFi infrastructure, enabling complex financial strategies that were previously accessible only to institutional players.
To truly appreciate the innovation of flash loans, let’s compare them to traditional lending mechanisms:
This radical departure from traditional lending principles has opened up new possibilities for capital efficiency, arbitrage, collateral swapping, and self-liquidation – use cases we’ll explore in detail later in this guide.
To effectively utilize flash loans, it’s crucial to understand the technological foundations that make them possible. At their core, flash loans leverage several key blockchain concepts and DeFi infrastructure components.
Flash loans are powered by smart contracts – self-executing code that runs on blockchain networks. The defining feature that enables flash loans is the atomic nature of blockchain transactions. “Atomic” means that a transaction either completes entirely or doesn’t happen at all – there’s no in-between state.
In the context of flash loans, this means that a series of operations (borrowing funds, using them for some purpose, and returning them plus fees) must all succeed, or the entire transaction is rolled back. This atomicity is what eliminates the risk for lenders, as failed repayments simply cause the transaction to revert, returning funds to the lending pool as if they were never borrowed.
Flash loans are primarily available on Ethereum and compatible networks due to their robust smart contract capabilities. Here’s an overview of the main networks supporting flash loans:
Each network offers different trade-offs in terms of liquidity depth, transaction costs, and execution speed, which may influence your choice depending on your specific flash loan use case.
Successfully executing a flash loan requires several technical components working together:
For developers new to flash loans, the learning curve can be steep. However, various development frameworks and templates have emerged to simplify the process, which we’ll explore in the setup section of this guide.
Flash loans draw their capital from liquidity pools – smart contract-controlled reserves of assets supplied by users seeking yield on their deposits. These pools serve multiple purposes within DeFi ecosystems:
The size of these liquidity pools directly impacts the maximum amount you can borrow in a flash loan. Larger pools enable larger flash loans, which is why established platforms like Aave and Maker can support flash loans worth tens or even hundreds of millions of dollars.
Flash loans offer unique advantages but also come with significant challenges and risks. Understanding both sides is crucial before diving into this sophisticated DeFi primitive.
Despite their advantages, flash loans come with several significant risks:
Flash loans have gained notoriety for their role in several high-profile DeFi exploits. While not inherently malicious, they can amplify the impact of exploits by providing attackers with temporary access to vast liquidity. Some security implications include:
These security concerns have prompted many DeFi protocols to implement safeguards against flash loan attacks, such as time-weighted average price (TWAP) oracles and governance protections requiring tokens to be held for minimum periods before voting.
Several DeFi platforms offer flash loan capabilities, each with unique features, fee structures, and supported assets. Understanding the landscape will help you choose the right platform for your specific needs.
Aave pioneered the concept of flash loans and remains one of the most popular platforms for this service.
Aave’s flash loans are accessed through their LendingPool contracts and have been battle-tested through billions of dollars in transaction volume.
dYdX offers flash loans as part of its margin trading and derivatives platform.
dYdX’s implementation requires repaying the flash loan before the end of the function call, making it slightly different from Aave’s model.
While not marketed explicitly as flash loans, Uniswap’s flash swaps function similarly:
Uniswap’s flash swaps are particularly useful for arbitrage between different DeFi platforms.
MakerDAO offers flash loans through its DSS Flash module:
MakerDAO’s flash loans are particularly useful for DAI-centric strategies and interactions with the Maker protocol.
| Platform | Fee | Asset Variety | Liquidity Depth | Network Availability | Ease of Use |
|---|---|---|---|---|---|
| Aave | 0.09% | High | Very High | Multiple | Moderate |
| dYdX | Free | Medium | High | Limited | Moderate |
| Uniswap | 0.05-1% | Very High | Varies by pair | Multiple | Moderate |
| MakerDAO | 0.05% | Low (DAI-focused) | Very High for DAI | Ethereum only | Complex |
When choosing a platform, consider not only the fees but also the specific assets you need to borrow, the networks you want to operate on, and how the platform’s implementation aligns with your intended use case.
Before executing your first flash loan, you need to set up a proper development environment. This section will guide you through the necessary tools, frameworks, and configurations.
To work with flash loans, you’ll need several key components:
Let’s walk through setting up a basic environment for flash loan development:
Download and install from nodejs.org. Verify installation with:
node -v npm -v
mkdir flash-loan-project cd flash-loan-project
npm init -y
npm install --save-dev hardhat
npx hardhat
Select “Create a basic sample project” and follow the prompts.
npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai dotenv @openzeppelin/contracts
Create a .env file in your project root:
ALCHEMY_API_KEY=your_alchemy_api_key PRIVATE_KEY=your_wallet_private_key ETHERSCAN_API_KEY=your_etherscan_api_key
Edit hardhat.config.js to include network configurations and environment variables:
require('@nomiclabs/hardhat-waffle');
require('dotenv').config();
module.exports = {
solidity: {
compilers: [
{
version: "0.8.10",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
{
version: "0.6.12",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
]
},
networks: {
hardhat: {
forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`,
blockNumber: 14000000
}
},
mainnet: {
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`,
accounts: [process.env.PRIVATE_KEY]
},
polygon: {
url: `https://polygon-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
accounts: [process.env.PRIVATE_KEY]
}
}
};
For safe development and testing of flash loans, it’s essential to use a local environment that can simulate mainnet conditions:
Hardhat’s network forking feature allows you to create a local copy of the Ethereum mainnet for testing:
npx hardhat node --fork https://eth-mainnet.alchemyapi.io/v2/YOUR_ALCHEMY_KEY
Create a simple script to verify your local environment is working:
// scripts/test-connection.js
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Testing with account:", deployer.address);
const balance = await ethers.provider.getBalance(deployer.address);
console.log("Account balance:", ethers.utils.formatEther(balance));
const blockNumber = await ethers.provider.getBlockNumber();
console.log("Current block number:", blockNumber);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Run with:
npx hardhat run scripts/test-connection.js --network localhost
Create interface files for the protocols you’ll interact with. For example, for Aave:
// contracts/interfaces/IAave.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
interface ILendingPool {
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint256);
function getAddressesProvider() external view returns (address);
}
interface ILendingPoolAddressesProvider {
function getLendingPool() external view returns (address);
}
Several frameworks can simplify flash loan development:
For beginners, Hardhat offers the best balance of features, documentation, and community support, making it the recommended starting point.
Now that your environment is set up, let’s build a simple flash loan contract and execute it. We’ll use Aave as our flash loan provider for this example.
First, let’s create a simple flash loan receiver contract that follows Aave’s interface requirements:
// contracts/FlashLoanExample.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IAave.sol";
contract FlashLoanExample {
using SafeERC20 for IERC20;
address public owner;
address public aaveLendingPoolAddressesProvider;
event FlashLoanExecuted(address indexed token, uint256 amount);
constructor(address _aaveLendingPoolAddressesProvider) {
owner = msg.sender;
aaveLendingPoolAddressesProvider = _aaveLendingPoolAddressesProvider;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
// Function to request a flash loan
function executeFlashLoan(address _token, uint256 _amount) external onlyOwner {
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
address[] memory assets = new address[](1);
uint256[] memory amounts = new uint256[](1);
uint256[] memory modes = new uint256[](1);
assets[0] = _token;
amounts[0] = _amount;
modes[0] = 0; // 0 = no debt, 1 = stable, 2 = variable
// Execute flash loan
lendingPool.flashLoan(
address(this),
assets,
amounts,
modes,
address(this),
bytes(""),
0
);
}
// This function is called by Aave after the flash loan is provided
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// Make sure this is being called by the lending pool
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
require(msg.sender == address(lendingPool), "Caller must be lending pool");
// Custom logic goes here - what to do with the flash loaned funds
// For this example, we just emit an event
emit FlashLoanExecuted(assets[0], amounts[0]);
// Approve the LendingPool to pull the amount + premium
for (uint i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i] + premiums[i];
IERC20(assets[i]).safeApprove(address(lendingPool), amountOwing);
}
return true;
}
// Function to withdraw tokens sent to this contract
function rescueTokens(address _token, address _to, uint256 _amount) external onlyOwner {
IERC20(_token).safeTransfer(_to, _amount);
}
}
Now, let's create a deployment script:
// scripts/deploy-flash-loan.js
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with the account:", deployer.address);
// Aave LendingPoolAddressesProvider address for Mainnet
const aaveLendingPoolAddressesProvider = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5";
const FlashLoanExample = await ethers.getContractFactory("FlashLoanExample");
const flashLoan = await FlashLoanExample.deploy(aaveLendingPoolAddressesProvider);
await flashLoan.deployed();
console.log("FlashLoanExample deployed to:", flashLoan.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Deploy your contract to the local forked network:
npx hardhat run scripts/deploy-flash-loan.js --network localhost
Now, let's create a script to execute the flash loan:
// scripts/execute-flash-loan.js
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Executing flash loan with the account:", deployer.address);
// Replace with your deployed contract address
const flashLoanAddress = "YOUR_DEPLOYED_CONTRACT_ADDRESS";
const flashLoan = await ethers.getContractAt("FlashLoanExample", flashLoanAddress);
// DAI address on mainnet
const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
// Flash loan 10 DAI
const amount = ethers.utils.parseEther("10");
console.log("Executing flash loan for 10 DAI...");
const tx = await flashLoan.executeFlashLoan(daiAddress, amount);
await tx.wait();
console.log("Flash loan executed successfully!");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Run the script to execute your flash loan:
npx hardhat run scripts/execute-flash-loan.js --network localhost
Let's break down what happens during flash loan execution:
executeFlashLoan function on your contract.flashLoan function, specifying the assets and amounts to borrow.executeOperation function, where your custom logic runs.This lifecycle is completed within a single transaction, ensuring the atomic nature of flash loans.
| Error | Possible Causes | Solution |
|---|---|---|
| "Insufficient funds for gas * price + value" | Your account doesn't have enough ETH to cover gas costs | Ensure your account has sufficient ETH for transaction fees |
| "Not enough liquidity" | The lending pool doesn't have enough of the requested token | Reduce the loan amount or use a different token with more liquidity |
| "Contract call reverted" | Issues in your contract logic, often related to repayment | Check your math to ensure you're approving the correct repayment amount |
| "Caller must be lending pool" | Incorrect lending pool address or unauthorized call | Verify you're using the correct lending pool address for your network |
| "Gas estimation failed" | Complex transaction that fails during execution | Add more detailed error handling and logging to identify the failure point |
Now that you've mastered the basics, let's explore more sophisticated flash loan strategies that can unlock significant value in DeFi ecosystems.
Arbitrage is perhaps the most common use case for flash loans. By borrowing large amounts of capital without upfront collateral, you can exploit price discrepancies across different exchanges.
Here's a simplified example of a DEX arbitrage contract:
// contracts/ArbitrageFlashLoan.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IAave.sol";
import "./interfaces/IUniswap.sol";
import "./interfaces/ISushiswap.sol";
contract ArbitrageFlashLoan {
using SafeERC20 for IERC20;
address public owner;
address public aaveLendingPoolAddressesProvider;
address public uniswapRouter;
address public sushiswapRouter;
event ArbitrageExecuted(uint256 profit, address token);
constructor(
address _aaveLendingPoolAddressesProvider,
address _uniswapRouter,
address _sushiswapRouter
) {
owner = msg.sender;
aaveLendingPoolAddressesProvider = _aaveLendingPoolAddressesProvider;
uniswapRouter = _uniswapRouter;
sushiswapRouter = _sushiswapRouter;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
function executeArbitrage(
address _baseToken,
address _quoteToken,
uint256 _amount
) external onlyOwner {
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
address[] memory assets = new address[](1);
uint256[] memory amounts = new uint256[](1);
uint256[] memory modes = new uint256[](1);
assets[0] = _baseToken;
amounts[0] = _amount;
modes[0] = 0; // 0 = no debt
// Execute flash loan
lendingPool.flashLoan(
address(this),
assets,
amounts,
modes,
address(this),
abi.encode(_baseToken, _quoteToken),
0
);
}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// Verify caller is lending pool
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
require(msg.sender == address(lendingPool), "Caller must be lending pool");
// Decode parameters
(address baseToken, address quoteToken) = abi.decode(params, (address, address));
// Get initial balance
uint256 flashLoanAmount = amounts[0];
uint256 repayAmount = flashLoanAmount + premiums[0];
// Execute the arbitrage
IERC20(baseToken).safeApprove(uniswapRouter, flashLoanAmount);
// Step 1: Swap baseToken for quoteToken on Uniswap
address[] memory path = new address[](2);
path[0] = baseToken;
path[1] = quoteToken;
uint[] memory amountsOut = IUniswapRouter(uniswapRouter).swapExactTokensForTokens(
flashLoanAmount,
0, // accept any amount of quoteToken
path,
address(this),
block.timestamp + 300
);
uint256 quoteTokenAmount = amountsOut[1];
// Step 2: Swap quoteToken back to baseToken on Sushiswap
IERC20(quoteToken).safeApprove(sushiswapRouter, quoteTokenAmount);
path[0] = quoteToken;
path[1] = baseToken;
amountsOut = ISushiswapRouter(sushiswapRouter).swapExactTokensForTokens(
quoteTokenAmount,
0, // accept any amount of baseToken
path,
address(this),
block.timestamp + 300
);
uint256 baseTokenAmountOut = amountsOut[1];
// Check if arbitrage was profitable
require(baseTokenAmountOut > repayAmount, "Arbitrage not profitable");
// Calculate profit
uint256 profit = baseTokenAmountOut - repayAmount;
// Repay the flash loan
IERC20(baseToken).safeApprove(address(lendingPool), repayAmount);
// Transfer profit to owner
IERC20(baseToken).safeTransfer(owner, profit);
emit ArbitrageExecuted(profit, baseToken);
return true;
}
// Function to rescue tokens sent to this contract
function rescueTokens(address _token, address _to, uint256 _amount) external onlyOwner {
IERC20(_token).safeTransfer(_to, _amount);
}
}
Flash loans can be used to swap collateral in lending platforms without having to repay loans first. This is particularly useful when you want to change your exposure to different assets without closing positions.
Here's a simplified collateral swap contract:
// contracts/CollateralSwapFlashLoan.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IAave.sol";
import "./interfaces/ICompound.sol";
import "./interfaces/IUniswap.sol";
contract CollateralSwapFlashLoan {
using SafeERC20 for IERC20;
address public owner;
address public aaveLendingPoolAddressesProvider;
address public uniswapRouter;
address public comptroller;
event CollateralSwapped(address oldCollateral, address newCollateral, uint256 amount);
constructor(
address _aaveLendingPoolAddressesProvider,
address _uniswapRouter,
address _comptroller
) {
owner = msg.sender;
aaveLendingPoolAddressesProvider = _aaveLendingPoolAddressesProvider;
uniswapRouter = _uniswapRouter;
comptroller = _comptroller;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
function executeCollateralSwap(
address _oldCollateral,
address _newCollateral,
address _cOldCollateral,
address _cNewCollateral,
uint256 _amount
) external onlyOwner {
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
address[] memory assets = new address[](1);
uint256[] memory amounts = new uint256[](1);
uint256[] memory modes = new uint256[](1);
assets[0] = _oldCollateral;
amounts[0] = _amount;
modes[0] = 0; // 0 = no debt
// Execute flash loan
lendingPool.flashLoan(
address(this),
assets,
amounts,
modes,
address(this),
abi.encode(_oldCollateral, _newCollateral, _cOldCollateral, _cNewCollateral),
0
);
}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// Verify caller is lending pool
ILendingPoolAddressesProvider provider = ILendingPoolAddressesProvider(aaveLendingPoolAddressesProvider);
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
require(msg.sender == address(lendingPool), "Caller must be lending pool");
// Decode parameters
(
address oldCollateral,
address newCollateral,
address cOldCollateral,
address cNewCollateral
) = abi.decode(params, (address, address, address, address));
uint256 flashLoanAmount = amounts[0];
uint256 repayAmount = flashLoanAmount + premiums[0];
// Step 1: Repay the old collateral from Compound
// First, approve the flash loaned tokens to be used by Compound
IERC20(oldCollateral).safeApprove(cOldCollateral, flashLoanAmount);
// Repay the borrowed amount
ICErc20(cOldCollateral).repayBorrow(flashLoanAmount);
// Step 2: Redeem the underlying collateral from Compound
uint256 cTokenBalance = IERC20(cOldCollateral).balanceOf(address(this));
ICErc20(cOldCollateral).redeem(cTokenBalance);
// Step 3: Swap the old collateral for the new collateral
uint256 oldCollateralBalance = IERC20(oldCollateral).balanceOf(address(this));
IERC20(oldCollateral).safeApprove(uniswapRouter, oldCollateralBalance);
address[] memory path = new address[](2);
path[0] = oldCollateral;
path[1] = newCollateral;
uint[] memory amountsOut = IUniswapRouter(uniswapRouter).swapExactTokensForTokens(
oldCollateralBalance - repayAmount, // Keep enough for flash loan repayment
0,
path,
address(this),
block.timestamp + 300
);
uint256 newCollateralAmount = amountsOut[1];
// Step 4: Supply the new collateral to Compound
IERC20(newCollateral).safeApprove(cNewCollateral, newCollateralAmount);
ICErc20(cNewCollateral).mint(newCollateralAmount);
// Step 5: Borrow the old collateral again to repay the flash loan
ICErc20(cOldCollateral).borrow(repayAmount);
// Approve and repay the flash loan
IERC20(oldCollateral).safeApprove(address(lendingPool), repayAmount);
emit CollateralSwapped(oldCollateral, newCollateral, newCollateralAmount);
return true;
}
// Function to rescue tokens sent to this contract
function rescueTokens(address _token, address _to, uint256 _amount) external onlyOwner {
IERC20(_token).safeTransfer(_to, _amount);
}
}
Flash loans can be used to protect against liquidation in lending platforms. When your position is close to liquidation, you can use a flash loan to repay part of your debt or add more collateral.
// contracts/LiquidationProtectionFlashLoan.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IAave.sol";
import "./interfaces/IUniswap.sol";contract LiquidationProtectionFlashLoan {
using SafeERC20 for IERC20;address public owner;
address public aaveLendingPoolAddressesProvider;
address public uniswapRouter;event PositionProtected(address collateralToken, address debtToken