-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
51 changed files
with
4,121 additions
and
1,730 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
Oops, something went wrong.