Skip to content

Commit

Permalink
upgrade to aave v3, uniswap v3
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanaichi committed Mar 29, 2024
1 parent 46a4c49 commit 646cd2c
Show file tree
Hide file tree
Showing 51 changed files with 4,121 additions and 1,730 deletions.
304 changes: 140 additions & 164 deletions basic/21-aave-uni-loan/contracts/AaveApe.sol
Original file line number Diff line number Diff line change
@@ -1,236 +1,212 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity >=0.6.0 <0.9.0;
pragma experimental ABIEncoderV2;
pragma solidity ^0.8.0;

import "./AaveUniswapBase.sol";
import './AaveUniswapBase.sol';

contract AaveApe is AaveUniswapBase {
using SafeMath for uint256;
event Ape(address ape, string action, address apeAsset, address borrowAsset, uint256 borrowAmount, uint256 apeAmount, uint256 interestRateMode);

event Ape(address ape, string action, address apeAsset, address borrowAsset, uint256 borrowAmount, uint256 apeAmount, uint256 interestRateMode);
constructor(
address lendingPoolAddressesProviderAddress,
address uniswapRouterAddress
) public AaveUniswapBase(lendingPoolAddressesProviderAddress, uniswapRouterAddress) {}

constructor(address lendingPoolAddressesProviderAddress, address uniswapRouterAddress) AaveUniswapBase(lendingPoolAddressesProviderAddress, uniswapRouterAddress) public {}


// Gets the amount available to borrow for a given address for a given asset
function getAvailableBorrowInAsset(address borrowAsset, address ape) public view returns (uint256) {
( ,,uint256 availableBorrowsETH,,,) = LENDING_POOL().getUserAccountData(ape);
return getAssetAmount(borrowAsset, availableBorrowsETH);
// availableBorrowsBase V3 USD based
(, , uint256 availableBorrowsBase, , , ) = LENDING_POOL().getUserAccountData(ape);
return getAssetAmount(borrowAsset, availableBorrowsBase);
}

// Converts an amount denominated in ETH into an asset based on the Aave oracle
function getAssetAmount(address asset, uint256 amountInEth) public view returns (uint256) {
uint256 assetPrice = getPriceOracle().getAssetPrice(asset);
(uint256 decimals ,,,,,,,,,) = getProtocolDataProvider().getReserveConfigurationData(asset);
uint256 assetAmount = amountInEth.mul(10**decimals).div(assetPrice);
return assetAmount;
// return asset amount with its decimals
function getAssetAmount(address asset, uint256 amountIn) public view returns (uint256) {
//All V3 markets use USD based oracles which return values with 8 decimals.
uint256 assetPrice = getPriceOracle().getAssetPrice(asset);
(uint256 decimals, , , , , , , , , ) = getProtocolDataProvider().getReserveConfigurationData(asset);
uint256 assetAmount = amountIn.mul(10**decimals).div(assetPrice);
return assetAmount;
}

// 1. Borrows the maximum amount available of a borrowAsset (in the designated interest rate mode)
// Note: requires the user to have delegated credit to the Aave Ape Contract
// 2. Converts it into apeAsset via Uniswap
// 3. Deposits that apeAsset into Aave on behalf of the borrower
function ape(address apeAsset, address borrowAsset, uint256 interestRateMode) public returns (bool) {

// Get the maximum amount available to borrow in the borrowAsset
uint256 borrowAmount = getAvailableBorrowInAsset(borrowAsset, msg.sender);

require(borrowAmount > 0, "Requires credit on Aave!");
require(borrowAmount > 0, 'Requires credit on Aave!');

ILendingPool _lendingPool = LENDING_POOL();
IPool _lendingPool = LENDING_POOL();

// Borrow from Aave
_lendingPool.borrow(
borrowAsset,
borrowAmount,
interestRateMode,
0,
msg.sender
);
_lendingPool.borrow(borrowAsset, borrowAmount, interestRateMode, 0, msg.sender);

// Approve the Uniswap Router on the borrowed asset
IERC20(borrowAsset).approve(UNISWAP_ROUTER_ADDRESS, borrowAmount);

//we will set the uniswap pool fee to 0.3%.
uint24 poolFee = 3000;

// Execute trade on Uniswap
address[] memory path = new address[](2);
path[0] = borrowAsset;
path[1] = apeAsset;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: borrowAsset,
tokenOut: apeAsset,
fee: poolFee,
recipient: address(this),
deadline: block.timestamp + 50,
amountIn: borrowAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});

uint[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(borrowAmount, 0, path, address(this), block.timestamp + 50);
uint256 outputAmount = UNISWAP_ROUTER.exactInputSingle(params);

// get the output amount, approve the Lending Pool to move that amount of erc20 & deposit
uint outputAmount = amounts[amounts.length - 1];
IERC20(apeAsset).approve(ADDRESSES_PROVIDER.getLendingPool(), outputAmount);
IERC20(apeAsset).approve(ADDRESSES_PROVIDER.getPool(), outputAmount);

_lendingPool.deposit(
apeAsset,
outputAmount,
msg.sender,
0
);
_lendingPool.supply(apeAsset, outputAmount, msg.sender, 0);

emit Ape(msg.sender, 'open', apeAsset, borrowAsset, borrowAmount, outputAmount, interestRateMode);

return true;
}

function superApe(address apeAsset, address borrowAsset, uint256 interestRateMode, uint levers) public returns (bool) {
// Call "ape" for the number of levers specified
for (uint i = 0; i < levers; i++) {
ape(apeAsset, borrowAsset, interestRateMode);
}

// Call "ape" for the number of levers specified
for (uint i = 0; i < levers; i++) {
ape(apeAsset, borrowAsset, interestRateMode);
}

return true;
}

function uniswapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address fromAsset,
address toAsset
) internal returns (uint[] memory amounts) {

// Approve the transfer
IERC20(fromAsset).approve(UNISWAP_ROUTER_ADDRESS, amountInMax);

// Prepare and execute the swap
address[] memory path = new address[](2);
path[0] = fromAsset;
path[1] = toAsset;
// todo
return UNISWAP_ROUTER.swapTokensForExactTokens(amountOut, amountInMax, path, address(this), block.timestamp + 5);
return true;
}

// Unwind a position (long apeAsset, short borrowAsset)
function unwindApe(address apeAsset, address borrowAsset, uint256 interestRateMode) public {
// Get the user's outstanding debt
(, uint256 stableDebt, uint256 variableDebt, , , , , , ) = getProtocolDataProvider().getUserReserveData(borrowAsset, msg.sender);

uint256 repayAmount;
if (interestRateMode == 1) {
repayAmount = stableDebt;
} else if (interestRateMode == 2) {
repayAmount = variableDebt;
}

// Get the user's outstanding debt
(,uint256 stableDebt, uint256 variableDebt,,,,,,) = getProtocolDataProvider().getUserReserveData(borrowAsset, msg.sender);

uint256 repayAmount;
if(interestRateMode == 1) {
repayAmount = stableDebt;
} else if (interestRateMode == 2) {
repayAmount = variableDebt;
}

require(repayAmount > 0, "Requires debt on Aave!");

// Prepare the flashLoan parameters
address receiverAddress = address(this);
require(repayAmount > 0, 'Requires debt on Aave!');

address[] memory assets = new address[](1);
assets[0] = borrowAsset;
// Prepare the flashLoan parameters
address receiverAddress = address(this);

uint256[] memory amounts = new uint256[](1);
amounts[0] = repayAmount;
address[] memory assets = new address[](1);
assets[0] = borrowAsset;

// 0 = no debt, 1 = stable, 2 = variable
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
uint256[] memory amounts = new uint256[](1);
amounts[0] = repayAmount;

address onBehalfOf = address(this);
bytes memory params = abi.encode(msg.sender, apeAsset, interestRateMode);
uint16 referralCode = 0;
// 0 = no debt, 1 = stable, 2 = variable
uint256[] memory modes = new uint256[](1);
modes[0] = 0;

LENDING_POOL().flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
address onBehalfOf = address(this);
bytes memory params = abi.encode(msg.sender, apeAsset, interestRateMode);
uint16 referralCode = 0;

LENDING_POOL().flashLoan(receiverAddress, assets, amounts, modes, onBehalfOf, params, referralCode);
}

// This is the function that the Lending pool calls when flashLoan has been called and the funds have been flash transferred
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
)
external
returns (bool)
{
require(msg.sender == ADDRESSES_PROVIDER.getLendingPool(), 'only the lending pool can call this function');
require(initiator == address(this), 'the ape did not initiate this flashloan');

// Calculate the amount owed back to the lendingPool
address borrowAsset = assets[0];
uint256 repayAmount = amounts[0];
uint256 amountOwing = repayAmount.add(premiums[0]);

// Decode the parameters
(address ape, address apeAsset, uint256 rateMode) = abi.decode(params, (address, address, uint256));

// Close position & repay the flashLoan
return closePosition(ape, apeAsset, borrowAsset, repayAmount, amountOwing, rateMode);

}

function closePosition(address ape, address apeAsset, address borrowAsset, uint256 repayAmount, uint256 amountOwing, uint256 rateMode) internal returns (bool) {

// Approve the lendingPool to transfer the repay amount
IERC20(borrowAsset).approve(ADDRESSES_PROVIDER.getLendingPool(), repayAmount);
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
require(msg.sender == ADDRESSES_PROVIDER.getPool(), 'only the lending pool can call this function');
require(initiator == address(this), 'the ape did not initiate this flashloan');

// Calculate the amount owed back to the lendingPool
address borrowAsset = assets[0];
uint256 repayAmount = amounts[0];
uint256 amountOwing = repayAmount.add(premiums[0]);

// Decode the parameters
(address ape, address apeAsset, uint256 rateMode) = abi.decode(params, (address, address, uint256));

// Close position & repay the flashLoan
return closePosition(ape, apeAsset, borrowAsset, repayAmount, amountOwing, rateMode);
}

// Repay the amount owed
LENDING_POOL().repay(
borrowAsset,
repayAmount,
rateMode,
ape
);
function closePosition(
address ape,
address apeAsset,
address borrowAsset,
uint256 repayAmount,
uint256 amountOwing,
uint256 rateMode
) internal returns (bool) {

// Calculate the amount available to withdraw (the smaller of the borrow allowance and the aToken balance)
uint256 maxCollateralAmount = getAvailableBorrowInAsset(apeAsset, ape);
IPool _lendingPool = LENDING_POOL();

DataTypes.ReserveData memory reserve = getAaveAssetReserveData(apeAsset);
address _lendingPoolAdress = ADDRESSES_PROVIDER.getPool();
// Approve the lendingPool to transfer the repay amount
IERC20(borrowAsset).approve(_lendingPoolAdress, repayAmount);

IERC20 _aToken = IERC20(reserve.aTokenAddress);
// Repay the amount owed
_lendingPool.repay(borrowAsset, repayAmount, rateMode, ape);

if(_aToken.balanceOf(ape) < maxCollateralAmount) {
maxCollateralAmount = _aToken.balanceOf(ape);
}
// Calculate the amount available to withdraw (the smaller of the borrow allowance and the aToken balance)
uint256 maxCollateralAmount = getAvailableBorrowInAsset(apeAsset, ape);

// transfer the aTokens to this address, then withdraw the Tokens from Aave
_aToken.transferFrom(ape, address(this), maxCollateralAmount);
DataTypes.ReserveData memory reserve = getAaveAssetReserveData(apeAsset);

LENDING_POOL().withdraw(
apeAsset,
maxCollateralAmount,
address(this)
);
IERC20 _aToken = IERC20(reserve.aTokenAddress);

// Make the swap on Uniswap
IERC20(apeAsset).approve(UNISWAP_ROUTER_ADDRESS, maxCollateralAmount);
if (_aToken.balanceOf(ape) < maxCollateralAmount) {
maxCollateralAmount = _aToken.balanceOf(ape);
}

uint[] memory amounts = uniswapTokensForExactTokens(amountOwing, maxCollateralAmount, apeAsset, borrowAsset);
// transfer the aTokens to this address, then withdraw the Tokens from Aave
_aToken.transferFrom(ape, address(this), maxCollateralAmount);

// Deposit any leftover back into Aave on behalf of the user
uint256 leftoverAmount = maxCollateralAmount.sub(amounts[0]);
_lendingPool.withdraw(apeAsset, maxCollateralAmount, address(this));

if(leftoverAmount > 0) {
// Make the swap on Uniswap
IERC20(apeAsset).approve(UNISWAP_ROUTER_ADDRESS, maxCollateralAmount);

IERC20(apeAsset).approve(ADDRESSES_PROVIDER.getLendingPool(), leftoverAmount);
// unsiwap v3 swap
//we will set the uniswap pool fee to 0.3%.
uint24 poolFee = 3000;

LENDING_POOL().deposit(
apeAsset,
leftoverAmount,
ape,
0
);
}
// Execute trade on Uniswap
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
tokenIn: apeAsset,
tokenOut: borrowAsset,
fee: poolFee,
recipient: address(this),
deadline: block.timestamp + 5,
amountOut: amountOwing,
amountInMaximum: maxCollateralAmount,
sqrtPriceLimitX96: 0
});

uint256 amountIn = UNISWAP_ROUTER.exactOutputSingle(params);

// Deposit any leftover back into Aave on behalf of the user
uint256 leftoverAmount = maxCollateralAmount.sub(amountIn);

if (leftoverAmount > 0) {
IERC20(apeAsset).approve(_lendingPoolAdress, leftoverAmount);

_lendingPool.supply(apeAsset, leftoverAmount, ape, 0);
}

// Approve the Aave Lending Pool to recover the flashloaned amount
IERC20(borrowAsset).approve(ADDRESSES_PROVIDER.getLendingPool(), amountOwing);
// Approve the Aave Lending Pool to recover the flashloaned amount
IERC20(borrowAsset).approve(_lendingPoolAdress, amountOwing);

emit Ape(ape, 'close', apeAsset, borrowAsset, amountOwing, amounts[0], rateMode);
emit Ape(ape, 'close', apeAsset, borrowAsset, amountOwing, amountIn, rateMode);

return true;
return true;
}

}
Loading

0 comments on commit 646cd2c

Please sign in to comment.