From ac0217de32f97aa076e5d28f44ca2bedbd8c1c7b Mon Sep 17 00:00:00 2001 From: Samyak Jain <34437877+KaymasJain@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:52:28 +0530 Subject: [PATCH] receiver logics update --- .vscode/settings.json | 3 + .../receivers2/aave-v2-receiver/helpers.sol | 113 +++++++++++++++++- .../aave-v2-receiver/interfaces.sol | 57 +++++++++ .../receivers2/aave-v2-receiver/main.sol | 99 +++++---------- .../senders2/aave-v2-migrator/helpers.sol | 17 ++- contracts/senders2/aave-v2-migrator/main.sol | 2 +- 6 files changed, 209 insertions(+), 82 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bef44e0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.compileUsingRemoteVersion": "v0.7.0+commit.9e61f92b" +} \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/helpers.sol b/contracts/receivers2/aave-v2-receiver/helpers.sol index 5d6c5c8..1547730 100644 --- a/contracts/receivers2/aave-v2-receiver/helpers.sol +++ b/contracts/receivers2/aave-v2-receiver/helpers.sol @@ -1,13 +1,30 @@ pragma solidity >=0.7.0; import { DSMath } from "../../common/math.sol"; -import { TokenMappingInterface, AaveData } from "./interfaces.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { + TokenMappingInterface, + AaveData, + AaveLendingPoolProviderInterface, + AaveDataProviderInterface, + AaveInterface +} from "./interfaces.sol"; abstract contract Helpers is DSMath { - // Replace this + using SafeERC20 for IERC20; + + // TODO: Add function for flash deposits and withdraw + mapping(address => mapping(address => uint)) flashDeposits; // Flash deposits of particular token + mapping(address => uint) flashAmts; // token amount for flashloan usage (these token will always stay raw in this contract) + + // TODO: Replace this TokenMappingInterface tokenMapping = TokenMappingInterface(address(2)); - function remapTokens(AaveData memory data) internal returns (AaveData memory) { + AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xd05e3E715d945B59290df0ae8eF85c1BdB684744); + + function remapTokens(AaveData memory data) internal view returns (AaveData memory) { for (uint i = 0; i < data.supplyTokens.length; i++) { data.supplyTokens[i] = tokenMapping.getMapping(data.supplyTokens[i]); } @@ -18,4 +35,94 @@ abstract contract Helpers is DSMath { return data; } + + function transferAtokens(address dsa, address[] memory supplyTokens, uint[] memory supplyAmts) internal { + AaveInterface aave = AaveInterface(aaveProvider.getLendingPool()); + for (uint i = 0; i < supplyTokens.length; i++) { + address _token = supplyTokens[i]; + IERC20 _atokenContract = IERC20(_token); // TODO: Fetch atoken from Aave mapping (change _token to atoken address) + uint _atokenBal = _atokenContract.balanceOf(address(this)); + uint _supplyAmt = supplyAmts[i]; + bool isFlash; + uint _flashAmt; + + // get Aave liquidity of token + uint tokenLiq = uint(0); + + if (_atokenBal < _supplyAmt) { + uint _reqAmt = _supplyAmt - _atokenBal; + if (tokenLiq < _reqAmt) { + _flashAmt = flashAmts[_token]; + if (_flashAmt > 0) { + // TODO: deposit _flashAmt to Aave + aave.deposit(_token, _flashAmt, address(this), 3288); + tokenLiq += _flashAmt; + isFlash = true; + } + } + + uint num = _reqAmt/tokenLiq + 1; // TODO: Is this right + uint splitAmt = _reqAmt/num; // TODO: Check decimal + uint finalSplit = _reqAmt - (splitAmt * (num - 1)); // TODO: to resolve upper decimal error + + for (uint j = 0; j < num; j++) { + if (i < num - 1) { + aave.borrow(_token, splitAmt, 2, 3288, address(this)); // TODO: is "2" for interest rate mode. Right? + aave.deposit(_token, splitAmt, address(this), 3288); + } else { + aave.borrow(_token, finalSplit, 2, 3288, address(this)); // TODO: is "2" for interest rate mode. Right? + aave.deposit(_token, finalSplit, address(this), 3288); + } + } + } + + if (isFlash) { + aave.withdraw(_token, _flashAmt, address(this)); + } + + _atokenContract.safeTransfer(dsa, _supplyAmt); + } + } + + function borrowAndTransferSpells(address dsa, address[] memory borrowTokens, uint[] memory borrowAmts) internal { + for (uint i = 0; i < borrowTokens.length; i++) { + address _token = borrowTokens[i]; + address _atoken = address(0); // TODO: Fetch atoken address + // get Aave liquidity of token + uint tokenLiq = uint(0); + uint _borrowAmt = borrowAmts[i]; + if (tokenLiq < _borrowAmt) { + uint _flashAmt = flashAmts[_token]; + // TODO: deposit _flashAmt in Aave + tokenLiq += _flashAmt; + } + // TODO: Check number of loops needed. Borrow and supply on user's account. + uint num = _borrowAmt/tokenLiq + 1; // TODO: Is this right + uint splitAmt = _borrowAmt/num; // TODO: Check decimal + uint finalSplit = _borrowAmt - (splitAmt * (num - 1)); // TODO: to resolve upper decimal error + + uint spellsAmt = (2 * num) + 1; + string[] memory targets = new string[](spellsAmt); + bytes[] memory castData = new bytes[](spellsAmt); + for (uint j = 0; j < num; j++) { + targets[j] = "AAVE-A"; + uint k = j * 2; + if (i < num - 1) { + // borrow spell + castData[k] = abi.encode("6abcd3de", _token, splitAmt, 2, 0, 0); // TODO: verify this & is rate mode right? + // deposit spell + castData[k+1] = abi.encode("ce88b439", _token, splitAmt, 2, 0, 0); // TODO: verify this & is rate mode right? + } else { + // borrow spell + castData[k] = abi.encode("6abcd3de", _token, finalSplit, 2, 0, 0); // TODO: verify this & is rate mode right? + // deposit spell + castData[k+1] = abi.encode("ce88b439", _token, finalSplit, 2, 0, 0); // TODO: verify this & is rate mode right? + } + } + targets[spellsAmt] = "BASIC-A"; // TODO: right spell? + castData[spellsAmt] = abi.encode("4bd3ab82", _atoken, _borrowAmt, address(this), 0, 0); // encode the data of atoken withdrawal + // TODO: Call DSAs cast and borrow (maybe create a new implementation which only this contract can run?) + } + } + } \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/interfaces.sol b/contracts/receivers2/aave-v2-receiver/interfaces.sol index 1cccf5d..50aa0bc 100644 --- a/contracts/receivers2/aave-v2-receiver/interfaces.sol +++ b/contracts/receivers2/aave-v2-receiver/interfaces.sol @@ -27,4 +27,61 @@ struct AaveData { interface IndexInterface { function master() external view returns (address); +} + +interface AaveLendingPoolProviderInterface { + function getLendingPool() external view returns (address); +} + +interface AaveDataProviderInterface { + function getReserveTokensAddresses(address _asset) external view returns ( + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress + ); + function getUserReserveData(address _asset, address _user) external view returns ( + uint256 currentATokenBalance, + uint256 currentStableDebt, + uint256 currentVariableDebt, + uint256 principalStableDebt, + uint256 scaledVariableDebt, + uint256 stableBorrowRate, + uint256 liquidityRate, + uint40 stableRateLastUpdated, + bool usageAsCollateralEnabled + ); + function getReserveConfigurationData(address asset) external view returns ( + uint256 decimals, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus, + uint256 reserveFactor, + bool usageAsCollateralEnabled, + bool borrowingEnabled, + bool stableBorrowRateEnabled, + bool isActive, + bool isFrozen + ); +} + +interface AaveInterface { + function deposit(address _asset, uint256 _amount, address _onBehalfOf, uint16 _referralCode) external; + function withdraw(address _asset, uint256 _amount, address _to) external; + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + function repay(address _asset, uint256 _amount, uint256 _rateMode, address _onBehalfOf) external; + function setUserUseReserveAsCollateral(address _asset, bool _useAsCollateral) external; + function getUserAccountData(address user) external view returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); } \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/main.sol b/contracts/receivers2/aave-v2-receiver/main.sol index 61f5bd7..45f16a4 100644 --- a/contracts/receivers2/aave-v2-receiver/main.sol +++ b/contracts/receivers2/aave-v2-receiver/main.sol @@ -1,10 +1,10 @@ -pragma solidity >=0.7.0; +pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { AccountInterface, AaveData } from "./interfaces.sol"; +import { AccountInterface, AaveData, IndexInterface } from "./interfaces.sol"; import { Events } from "./events.sol"; import { Helpers } from "./helpers.sol"; @@ -16,13 +16,8 @@ contract MigrateResolver is Helpers, Events { uint public safeRatioGap = 200000000000000000; // 20%? 2e17 // dsa => position - mapping(uint => AaveData) public positions; + mapping(uint => bytes) public positions; mapping(address => mapping(address => uint)) deposits; - // TODO: Add function for flash deposits and withdraw - mapping(address => mapping(address => uint)) flashDeposits; // Flash deposits of particular token - mapping(address => uint) flashAmts; // token amount for flashloan usage (these token will always stay raw in this contract) - // TODO: need to add function to add this mapping - mapping(address => address) tokensL1L2; // L1 tokens mapping to L2 tokens. Eg:- L1 DAI to L2 DAI // InstaIndex Address. IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); @@ -97,94 +92,54 @@ contract AaveV2Migrator is MigrateResolver { uint private lastStateId; function _migratePosition(AaveData memory _data) internal { - AaveData storage data = _data; + AaveData memory data = remapTokens(_data); // converting L1 token addresses to L2 addresses address dsa = _data.targetDsa; - address[] memory supplyAmts = _data.supplyAmts; - address[] memory borrowAmts = _data.borrowAmts; + uint[] memory supplyAmts = _data.supplyAmts; + uint[] memory borrowAmts = _data.borrowAmts; address[] memory supplyTokens = _data.supplyTokens; address[] memory borrowTokens = _data.borrowTokens; - for (uint i = 0; i < supplyTokens.length; i++) { - address _token = tokensL1L2[supplyTokens[i]]; - IERC20 _atokenContract = IERC20(_token); // TODO: Fetch atoken from Aave mapping (change _token to atoken address) - uint _atokenBal = _atokenContract.balanceOf(address(this)); - uint _supplyAmt = supplyAmts[i]; - if (_atokenBal < _supplyAmt) { - // TODO: Loop in the token. by borrow & supply to desirable amount - // Things to take into consideration: - // number of loops (have to check from liquidity available in Aave & accounts borrowing limit with every loop it'll decrease) - } - _atokenContract.safeTransfer(dsa, _supplyAmt); - } + transferAtokens(dsa, supplyTokens, supplyAmts); // Have to borrow from user's account - for (uint i = 0; i < borrowTokens.length; i++) { - address _token = tokensL1L2[borrowTokens[i]]; - // get Aave liquidity of token - uint tokenLiq = uint(0); - uint _borrowAmt = borrowAmts[i]; - if (tokenLiq < _borrowAmt) { - // deposit flash amt in Aave - uint _flashAmt = flashAmts[_token]; - // TODO: deposit in Aave - tokenLiq += _flashAmt; - } - // TODO: Check number of loops needed. Borrow and supply on user's account. - uint num = _borrowAmt/tokenLiq + 1; // TODO: Is this right - uint splitAmt = _borrowAmt/num; // TODO: Check decimal - uint finalSplit = _borrowAmt - (splitAmt * (num - 1)); // TODO: to resolve upper decimal error - - uint spellsAmt = num + 1; - string[] memory targets = string[](spellsAmt); - bytes[] memory castData = bytes[](spellsAmt); - for (uint j = 0; j < num; j++) { - targets[j] = "AAVE-A"; - if (i < num - 1) { - castData[j] = bytes(0); // encode the cast data & use splitAmt - } else { - castData[j] = bytes(0); // encode the cast data & use finalSplit - } - } - targets[spellsAmt] = "BASIC-A"; // TODO: right spell? - castData[spellsAmt] = bytes(0); // encode the data of atoken withdrawal - // TODO: Call DSAs cast and borrow (maybe create a new implementation which only this contract can run?) - } + borrowAndTransferSpells(dsa, borrowTokens, borrowAmts); // TODO: Final position should be 20% less than liquidation (use 'safeRatioGap', Also should we check this at start?) } - function getPosition(address owner) public view returns (AaveData memory data) { - data = positions[owner]; - } + // function getPosition(address owner) public view returns (AaveData memory data) { + // data = positions[owner]; + // } - // have to add more conditions - function canMigrate(AaveData calldata data) public view returns (bool can) { - can = true; + // TODO: have to add more conditions + function canMigrate(AaveData memory data) public view returns (bool can) { + // can = true; - AaveData memory data = getPosition(owner); + // AaveData memory data = getPosition(owner); - for (uint i = 0; i < data.supplyTokens.length; i++) { - IERC20 token = IERC20(data.supplyTokens[i]); - if (token.balanceOf(address(this)) < data.supplyAmts[i]) { - can = false; - } - } + // for (uint i = 0; i < data.supplyTokens.length; i++) { + // IERC20 token = IERC20(data.supplyTokens[i]); + // if (token.balanceOf(address(this)) < data.supplyAmts[i]) { + // can = false; + // } + // } } function onStateReceive(uint256 stateId, bytes calldata receivedData) external { require(stateId > lastStateId, "wrong-data"); lastStateId = stateId; - (AaveData memory data) = abi.decode(receivedData, (AaveData)); - positions[stateId] = data; // TODO: what's the best way to store user's data to create position later + // (AaveData memory data) = abi.decode(receivedData, (AaveData)); + positions[stateId] = receivedData; // TODO: what's the best way to store user's data to create position later } function migrate(uint _id) external { - AaveData memory data = positions[_id]; - - require(data != AaveData(0), "already-migrated"); + bytes memory _data = positions[_id]; + require(_data != bytes(0), "already-migrated"); // TODO: How to resolve this + + AaveData memory data = abi.decode(_data, (AaveData)); require(canMigrate(data), "not-enough-liquidity"); // TODO: do we need this? as we can see if transaction will go through or not from our bot diff --git a/contracts/senders2/aave-v2-migrator/helpers.sol b/contracts/senders2/aave-v2-migrator/helpers.sol index 406e2ee..d382d83 100644 --- a/contracts/senders2/aave-v2-migrator/helpers.sol +++ b/contracts/senders2/aave-v2-migrator/helpers.sol @@ -3,6 +3,9 @@ pragma solidity >=0.7.0; import { DSMath } from "../../common/math.sol"; import { Stores } from "../../common/stores.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + import { AaveLendingPoolProviderInterface, AaveDataProviderInterface, @@ -12,6 +15,8 @@ import { abstract contract Helpers is DSMath, Stores { + using SafeERC20 for IERC20; + struct AaveDataRaw { address targetDsa; uint[] supplyAmts; @@ -87,9 +92,9 @@ abstract contract Helpers is DSMath, Stores { } function _PaybackCalculate(AaveInterface aave, AaveDataRaw memory _data, address sourceDsa) internal returns (uint[] stableBorrow, uint[] variableBorrow, uint[] totalBorrow) { - for (uint i = 0; i < data.borrowTokens.length; i++) { - address _token = data.borrowTokens[i] == ethAddr ? wethAddr : data.borrowTokens[i]; - data.borrowTokens[i] = _token; + for (uint i = 0; i < _data.borrowTokens.length; i++) { + address _token = _data.borrowTokens[i] == ethAddr ? wethAddr : _data.borrowTokens[i]; + _data.borrowTokens[i] = _token; ( , @@ -98,11 +103,11 @@ abstract contract Helpers is DSMath, Stores { ,,,,, ) = aaveData.getUserReserveData(_token, sourceDsa); - stableBorrow[i] = data.stableBorrowAmts[i] == uint(-1) ? stableDebt : data.stableBorrowAmts[i]; - variableBorrow[i] = data.variableBorrowAmts[i] == uint(-1) ? variableDebt : data.variableBorrowAmts[i]; + stableBorrow[i] = _data.stableBorrowAmts[i] == uint(-1) ? stableDebt : _data.stableBorrowAmts[i]; + variableBorrow[i] = _data.variableBorrowAmts[i] == uint(-1) ? variableDebt : _data.variableBorrowAmts[i]; totalBorrow[i] = add(stableBorrow[i], variableBorrow[i]); - if (totalBorrowAmt > 0) { + if (totalBorrow[i] > 0) { IERC20(_token).safeApprove(address(aave), totalBorrow[i]); // TODO: Approval is to Aave address of atokens address? } aave.borrow(_token, totalBorrow[i], 2, 3088, address(this)); // TODO: Borrowing debt to payback diff --git a/contracts/senders2/aave-v2-migrator/main.sol b/contracts/senders2/aave-v2-migrator/main.sol index 494f3ac..2b1a432 100644 --- a/contracts/senders2/aave-v2-migrator/main.sol +++ b/contracts/senders2/aave-v2-migrator/main.sol @@ -1,4 +1,4 @@ -pragma solidity >=0.7.0; +pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";