Skip to content

Commit

Permalink
feat - add exit with flashloan function
Browse files Browse the repository at this point in the history
  • Loading branch information
Dvisacker committed Sep 30, 2024
1 parent e278a22 commit 7f6162f
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 84 deletions.
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ leverage-mainnet:
@echo "Leveraging on Mainnet..."
@cd $(CONTRACTS_DIR) && forge script script/Leverage.s.sol:Leverage --rpc-url $(MAINNET_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv

exit-with-flashloan-local-fork:
@echo "Exiting with flashloan on Local..."
@cd $(CONTRACTS_DIR) && forge script script/ExitWithFlashloan.s.sol:ExitWithFlashloan --rpc-url http://localhost:8545 --private-key $(PRIVATE_KEY) --broadcast -vv

exit-with-flashloan-arbitrum:
@echo "Exiting with flashloan on Arbitrum..."
@cd $(CONTRACTS_DIR) && forge script script/ExitWithFlashloan.s.sol:ExitWithFlashloan --rpc-url $(ARBITRUM_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast -vvvv

exit-with-flashloan-mainnet:
@echo "Exiting with flashloan on Mainnet..."
@cd $(CONTRACTS_DIR) && forge script script/ExitWithFlashloan.s.sol:ExitWithFlashloan --rpc-url $(MAINNET_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv

get-position-local-fork:
@echo "Getting position on Local..."
@cd $(CONTRACTS_DIR) && forge script script/GetPosition.s.sol:GetPosition --rpc-url http://localhost:8545 --private-key $(PRIVATE_KEY) --broadcast -vv
Expand Down
35 changes: 35 additions & 0 deletions contracts/script/ExitWithFlashloan.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {Script} from "forge-std/Script.sol";
import {AaveLooper} from "../src/AaveLooper.sol";
import {ERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {console2} from "forge-std/console2.sol";
import {DevOpsTools} from "../lib/foundry-devops/src/DevOpsTools.sol";
import {HelperConfig} from "./HelperConfig.s.sol";

contract ExitWithFlashloan is Script {
HelperConfig helperConfig = new HelperConfig();
address aaveLooper = DevOpsTools.get_most_recent_deployment("AaveLooper", block.chainid);
address supplyAsset;
address borrowAsset;
uint256 initialAmount;
uint256 iterations;

function run() external {
HelperConfig.NetworkConfig memory networkConfig = helperConfig.getActiveNetworkConfig();

supplyAsset = vm.envOr("SUPPLY_ASSET", networkConfig.usdc);
borrowAsset = vm.envOr("BORROW_ASSET", networkConfig.weth);
initialAmount = vm.envOr("INITIAL_AMOUNT", uint256(1000000));

AaveLooper looper = AaveLooper(aaveLooper);

console2.log("Supply asset:", supplyAsset);
console2.log("Borrow asset:", borrowAsset);

vm.startBroadcast();
looper.exitPositionWithFlashLoan(supplyAsset, borrowAsset);
vm.stopBroadcast();
}
}
1 change: 1 addition & 0 deletions contracts/script/GetPosition.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ contract GetPosition is Script {
uint256 healthFactor
) = looper.getPositionData();

console2.log("Contract address:", aaveLooper);
console2.log("Total collateral:", totalCollateral);
console2.log("Total debt:", totalDebt);
console2.log("Available borrows:", availableBorrows);
Expand Down
29 changes: 0 additions & 29 deletions contracts/script/Leverage.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,7 @@ contract Leverage is Script {
uint256 initialAmount;
uint256 iterations;

function getCodeAt(address _addr) public view returns (bytes memory) {
uint256 size;
bytes memory code;

assembly {
// retrieve the size of the code, this needs assembly
size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using code = new bytes(size)
code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(code, 0x20), 0, size)
}

return code;
}

function run() external {
console2.log("Chainid:", block.chainid);
console2.log("Block timestamp:", block.timestamp);
console2.log("Block number:", block.number);
console2.log("AaveLooper address:", aaveLooper);
HelperConfig.NetworkConfig memory networkConfig = helperConfig.getActiveNetworkConfig();

supplyAsset = vm.envOr("SUPPLY_ASSET", networkConfig.usdc);
Expand All @@ -53,10 +28,6 @@ contract Leverage is Script {

AaveLooper looper = AaveLooper(aaveLooper);

console2.log("Supply asset:", supplyAsset);
console2.log("Borrow asset:", borrowAsset);
console2.log("Iterations:", iterations);

// (uint24 bestFeeTier, uint256 amountOut) =
// looper.getBestFeeTier(uniswapV3Factory, supplyAsset, borrowAsset, initialAmount);
// console2.log("Best fee tier:", bestFeeTier);
Expand Down
144 changes: 89 additions & 55 deletions contracts/src/AaveLooper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,40 @@ contract AaveLooper is ImmutableOwnable {
return getLiquidity(supplyAsset);
}

function exitPositionWithFlashLoan(address supplyAsset, address borrowAsset) external onlyOwner returns (uint256) {
uint256 borrowBalance = getBorrowBalance(borrowAsset);

// Request a flash loan for the borrow balance
LENDING_POOL.flashLoanSimple(address(this), borrowAsset, borrowBalance, abi.encode(supplyAsset, borrowAsset), 0);

// After the flash loan, withdraw any remaining supply and send to owner
_redeemSupply(supplyAsset, type(uint256).max);
return _withdrawToOwner(supplyAsset);
}

// flash loan callback
function executeOperation(address asset, uint256 amount, uint256 premium, address initiator, bytes calldata params)
external
returns (bool)
{
require(msg.sender == address(LENDING_POOL), "Caller must be lending pool");
require(initiator == address(this), "Initiator must be this contract");

(address supplyAsset, address borrowAsset) = abi.decode(params, (address, address));

// Repay the borrowed amount
_repayBorrow(borrowAsset, amount);

// Withdraw the supply
_redeemSupply(supplyAsset, type(uint256).max);

// Approve the flash loan repayment
uint256 amountOwed = amount + premium;
ERC20(asset).approve(address(LENDING_POOL), amountOwed);

return true;
}

/**
* @param iterations - MAX loop count
* @return Withdrawn amount of ASSET to OWNER
Expand Down Expand Up @@ -326,59 +360,59 @@ contract AaveLooper is ImmutableOwnable {

// -- swap --

function _swap(SwapParams memory params) public onlyOwner {
// IERC20(params.tokenIn).approve(AGGREGATION_ROUTER_V5, params.amountIn);
// IERC20(params.tokenIn).transferFrom(msg.sender, address(this), params.amountIn);

// IAggregationRouterV5.SwapDescription memory desc = IAggregationRouterV5.SwapDescription({
// srcToken: IERC20(params.tokenIn),
// dstToken: IERC20(params.tokenOut),
// srcReceiver: payable(address(this)),
// dstReceiver: params.recipient,
// amount: params.amountIn,
// minReturnAmount: params.minReturnAmount,
// flags: params.flags
// });

// console2.log("Swapping", params.tokenIn, "to", params.tokenOut);

// IAggregationRouterV5(AGGREGATION_ROUTER_V5).swap(
// address(0), // executor (0 for default)
// desc,
// params.permit,
// params.data
// );

// // If there are any leftover tokens, send them back to the user
// uint256 leftover = IERC20(params.tokenIn).balanceOf(address(this));
// if (leftover > 0) {
// IERC20(params.tokenIn).transfer(msg.sender, leftover);
// }
}

function swapExactTokensForTokensV3(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint256 amountOutMin,
address to,
uint256 deadline
) public onlyOwner returns (uint256) {
// IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
IERC20(tokenIn).approve(UNISWAP_V3_ROUTER, amountIn);

ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: fee,
recipient: to,
deadline: deadline,
amountIn: amountIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
});

return ISwapRouter(UNISWAP_V3_ROUTER).exactInputSingle(params);
}
// function _swap(SwapParams memory params) public onlyOwner {
// // IERC20(params.tokenIn).approve(AGGREGATION_ROUTER_V5, params.amountIn);
// // IERC20(params.tokenIn).transferFrom(msg.sender, address(this), params.amountIn);

// // IAggregationRouterV5.SwapDescription memory desc = IAggregationRouterV5.SwapDescription({
// // srcToken: IERC20(params.tokenIn),
// // dstToken: IERC20(params.tokenOut),
// // srcReceiver: payable(address(this)),
// // dstReceiver: params.recipient,
// // amount: params.amountIn,
// // minReturnAmount: params.minReturnAmount,
// // flags: params.flags
// // });

// // console2.log("Swapping", params.tokenIn, "to", params.tokenOut);

// // IAggregationRouterV5(AGGREGATION_ROUTER_V5).swap(
// // address(0), // executor (0 for default)
// // desc,
// // params.permit,
// // params.data
// // );

// // // If there are any leftover tokens, send them back to the user
// // uint256 leftover = IERC20(params.tokenIn).balanceOf(address(this));
// // if (leftover > 0) {
// // IERC20(params.tokenIn).transfer(msg.sender, leftover);
// // }
// }

// function swapExactTokensForTokensV3(
// address tokenIn,
// address tokenOut,
// uint24 fee,
// uint256 amountIn,
// uint256 amountOutMin,
// address to,
// uint256 deadline
// ) public onlyOwner returns (uint256) {
// // IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
// IERC20(tokenIn).approve(UNISWAP_V3_ROUTER, amountIn);

// ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
// tokenIn: tokenIn,
// tokenOut: tokenOut,
// fee: fee,
// recipient: to,
// deadline: deadline,
// amountIn: amountIn,
// amountOutMinimum: amountOutMin,
// sqrtPriceLimitX96: 0
// });

// return ISwapRouter(UNISWAP_V3_ROUTER).exactInputSingle(params);
// }
}

0 comments on commit 7f6162f

Please sign in to comment.