From 6994796c295fce2ec790ac82f1817aff50b62070 Mon Sep 17 00:00:00 2001 From: Samyak Jain <34437877+KaymasJain@users.noreply.github.com> Date: Mon, 5 Apr 2021 05:30:16 +0530 Subject: [PATCH] major updates in L1 migration contracts --- .../senders2/aave-v2-migrator/events.sol | 26 +++ .../senders2/aave-v2-migrator/helpers.sol | 131 ++++++++++++ .../senders2/aave-v2-migrator/interfaces.sol | 81 ++++++++ contracts/senders2/aave-v2-migrator/main.sol | 191 ++++++++++++++++++ 4 files changed, 429 insertions(+) create mode 100644 contracts/senders2/aave-v2-migrator/events.sol create mode 100644 contracts/senders2/aave-v2-migrator/helpers.sol create mode 100644 contracts/senders2/aave-v2-migrator/interfaces.sol create mode 100644 contracts/senders2/aave-v2-migrator/main.sol diff --git a/contracts/senders2/aave-v2-migrator/events.sol b/contracts/senders2/aave-v2-migrator/events.sol new file mode 100644 index 0000000..336ec9d --- /dev/null +++ b/contracts/senders2/aave-v2-migrator/events.sol @@ -0,0 +1,26 @@ +pragma solidity >=0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogDeposit( + address owner, + address[] tokens, + uint[] amts + ); + + event LogWithdraw( + address owner, + address[] tokens, + uint[] amts + ); + + event LogAaveV2Migrate( + address indexed user, + address indexed targetDsa, + address[] supplyTokens, + address[] borrowTokens, + uint[] supplyAmts, + uint[] variableBorrowAmts, + uint[] stableBorrowAmts + ); +} \ No newline at end of file diff --git a/contracts/senders2/aave-v2-migrator/helpers.sol b/contracts/senders2/aave-v2-migrator/helpers.sol new file mode 100644 index 0000000..4255030 --- /dev/null +++ b/contracts/senders2/aave-v2-migrator/helpers.sol @@ -0,0 +1,131 @@ +pragma solidity >=0.7.0; + +import { DSMath } from "../../common/math.sol"; +import { Stores } from "../../common/stores.sol"; + +import { + AaveLendingPoolProviderInterface, + AaveDataProviderInterface, + AaveInterface, + StateSenderInterface +} from "./interfaces.sol"; + +abstract contract Helpers is DSMath, Stores { + + struct AaveDataRaw { + address targetDsa; + uint[] supplyAmts; + uint[] variableBorrowAmts; + uint[] stableBorrowAmts; + address[] supplyTokens; + address[] borrowTokens; + } + + struct AaveData { + address targetDsa; + uint[] supplyAmts; + uint[] borrowAmts; + address[] supplyTokens; + address[] borrowTokens; + } + + /** + * @dev Aave referal code + */ + uint16 constant internal referralCode = 3228; + + address constant internal polygonReceiver = address(2); // Replace this + + /** + * @dev Aave Provider + */ + AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5); + + /** + * @dev Aave Data Provider + */ + AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d); + + /** + * @dev Polygon State Sync Contract + */ + StateSenderInterface constant internal stateSender = StateSenderInterface(0x28e4F3a7f651294B9564800b2D01f35189A5bFbE); + + function _borrow(address _token, uint _amt) internal { + } + + function _paybackBehalfOne(AaveInterface aave, address token, uint amt, uint rateMode, address user) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + 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; + + ( + , + uint stableDebt, + uint variableDebt, + ,,,,, + ) = 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]; + + totalBorrow[i] = add(stableBorrow[i], variableBorrow[i]); + if (totalBorrowAmt > 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 + } + } + + function _getAtokens(AaveInterface aave, address[] memory supplyTokens, uint[] memory supplyAmts) internal returns (uint[] finalAmts) { + for (uint i = 0; i < supplyTokens.length; i++) { + (address _aToken, ,) = aaveData.getReserveTokensAddresses(supplyTokens[i]); + ATokenInterface aTokenContract = ATokenInterface(_aToken); + + if (supplyAmts[i] == uint(-1)) { + // TODO: get maximum balance and set the return variable + } else { + finalAmts[i] = supplyAmts[i]; + } + + aTokenContract.transferFrom(sourceDsa, address(this), finalAmts[i]); + } + } + + function _checkRatio(AaveData data, uint _safeRatioGap) returns (bool isOk) { + // TODO: Check the debt/collateral ratio should be less than "_safeRatioGap" from Liquidation of that particular user assets + } + +} \ No newline at end of file diff --git a/contracts/senders2/aave-v2-migrator/interfaces.sol b/contracts/senders2/aave-v2-migrator/interfaces.sol new file mode 100644 index 0000000..f7b4a12 --- /dev/null +++ b/contracts/senders2/aave-v2-migrator/interfaces.sol @@ -0,0 +1,81 @@ +pragma solidity >=0.7.0; +pragma experimental ABIEncoderV2; + +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 + ); +} + +interface AaveLendingPoolProviderInterface { + function getLendingPool() external view returns (address); +} + +// Aave Protocol Data Provider +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 AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() external view returns (address[] memory); +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + function isTransferAllowed(address _user, uint256 _amount) external view returns (bool); + function balanceOf(address _user) external view returns(uint256); + function transferFrom(address, address, uint) external returns (bool); + function approve(address, uint256) external; +} + +interface StateSenderInterface { + function syncState(address receiver, bytes calldata data) external; + function register(address sender, address receiver) external; +} + +interface IndexInterface { + function master() external view returns (address); +} \ No newline at end of file diff --git a/contracts/senders2/aave-v2-migrator/main.sol b/contracts/senders2/aave-v2-migrator/main.sol new file mode 100644 index 0000000..97f3d56 --- /dev/null +++ b/contracts/senders2/aave-v2-migrator/main.sol @@ -0,0 +1,191 @@ +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 { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { AaveInterface, ATokenInterface } from "./interfaces.sol"; +import { Events } from "./events.sol"; + +contract LiquidityResolver is Helpers, Events { + using SafeERC20 for IERC20; + + mapping(address => mapping(address => uint)) public deposits; + + // This will be used to have debt/collateral ratio always 20% less than liquidation + // TODO: Is this number correct for it? + uint public safeRatioGap = 20000000000000000; // 20%? + + uint public fee = 200000000000000; // 0.2% on collateral? + + // InstaIndex Address. + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); + + function spell(address _target, bytes memory _data) external { + require(msg.sender == instaIndex.master(), "not-master"); + require(_target != address(0), "target-invalid"); + assembly { + let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0) + + switch iszero(succeeded) + case 1 { + // throw if delegatecall failed + let size := returndatasize() + returndatacopy(0x00, 0x00, size) + revert(0x00, size) + } + } + } + + // TODO: Deposited assets will get deposited into Aave as collateral + function deposit(address[] calldata tokens, uint[] calldata amts) external payable { + uint _length = tokens.length; + require(_length == amts.length, "invalid-length"); + + uint[] memory _amts = new uint[](_length); + + for (uint256 i = 0; i < _length; i++) { + uint _amt; + address _token = tokens[i]; + if (_token == ethAddr) { + require(msg.value == amts[i]); + _amt = msg.value; + + TokenInterface(wethAddr).deposit{value: msg.value}(); + } else { + IERC20 tokenContract = IERC20(_token); + _amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i]; + tokenContract.safeTransferFrom(msg.sender, address(this), _amt); + } + + _amts[i] = _amt; + + deposits[msg.sender][_token] += _amt; + } + + emit LogDeposit(msg.sender, tokens, _amts); + } + + // TODO: If not enough ideal token then withdraw the required amount from Aave. Also, check that it should not make position risky + function withdraw(address[] calldata tokens, uint[] calldata amts) external { + uint _length = tokens.length; + require(_length == amts.length, "invalid-length"); + + uint[] memory _amts = new uint[](_length); + + for (uint256 i = 0; i < _length; i++) { + uint _amt = amts[i]; + address _token = tokens[i]; + uint maxAmt = deposits[msg.sender][_token]; + + if (_amt > maxAmt) { + _amt = maxAmt; + } + + if (_token == ethAddr) { + TokenInterface(wethAddr).withdraw(_amt); + msg.sender.call{value: _amt}(""); + } else { + IERC20(_token).safeTransfer(msg.sender, _amt); + } + + _amts[i] = _amt; + + deposits[msg.sender][_token] = sub(maxAmt, _amt); + } + + emit LogWithdraw(msg.sender, tokens, _amts); + } + + // TODO: payback if debt else deposit + // TODO: if ratio is safe then transfer excess collateral to L2 migration contract + // Always, keep 1000 wei WETH ideal for flashloan + function settle(address[] calldata tokens) external { + + } + +} + +contract MigrateResolver is LiquidityResolver { + using SafeERC20 for IERC20; + + function _migrate(address l2DSA, AaveDataRaw memory _data, address sourceDsa) internal { + require(_data.supplyTokens.length > 0, "0-length-not-allowed"); + require(_data.targetDsa != address(0), "invalid-address"); + require(_data.supplyTokens.length == _data.supplyAmts.length, "invalid-length"); + require( + _data.borrowTokens.length == _data.variableBorrowAmts.length && + _data.borrowTokens.length == _data.stableBorrowAmts.length, + "invalid-length" + ); + + AaveInterface aave = AaveInterface(aaveProvider.getLendingPool()); + + (,,,,,uint healthFactor) = aave.getUserAccountData(sourceDsa); + require(healthFactor > 1e18, "position-not-safe"); + + (uint[] stableBorrows, uint[] variableBorrows, uint[] totalBorrows) = _PaybackCalculate(aave, _data, sourceDsa); + + _PaybackStable(_data.borrowTokens.length, aave, _data.borrowTokens, stableBorrows, sourceDsa); + _PaybackVariable(_data.borrowTokens.length, aave, _data.borrowTokens, variableBorrows, sourceDsa); + + (uint[] totalSupplies) = _getAtokens(aave, _data.supplyTokens, _data.supplyAmts); + + // Aave on Polygon doesn't have stable borrowing so we'll borrow all the debt in variable + AaveData memory data; + + data.borrowTokens = _data.borrowTokens; + data.stableBorrowAmts = _data.stableBorrowAmts; + data.supplyAmts = totalSupplies; + data.supplyTokens = _data.supplyTokens; + data.targetDsa = _data.targetDsa; + data.borrowAmts = totalBorrows; + + // TODO: Check the amount that user is trying to migrate is 20% below the Liquidation + bool isOk = _checkRatio(data, safeRatioGap); + require(isOk, "position-risky-to-migrate"); + + bytes memory positionData = abi.encode(l2DSA, data); // TODO: Can we do anything else to make the data more secure? (It's secure already) + stateSender.syncState(polygonReceiver, positionData); + + emit LogAaveV2Migrate( + sourceDsa, + data.targetDsa, + data.supplyTokens, + data.borrowTokens, + data.supplyAmts, + data.variableBorrowAmts, + data.stableBorrowAmts + ); + } + + function migrate(address l2DSA, AaveDataRaw calldata _data) external { + _migrate(l2DSA, _data, msg.sender); + } + + function migrateWithFlash(address l2DSA, AaveDataRaw calldata _data, uint ethAmt) external { + bytes data = abi.encode(l2DSA, _data, msg.sender, ethAmt); + + // TODO: integrate dydx flashloan and borrow "ethAmt" and transfer ETH to this address + + } + + function callFunction( + address sender, + Account.Info memory account, + bytes memory data + ) public override { + require(sender == address(this), "wrong-sender"); + (address l2DSA, AaveDataRaw memory _data, address sourceDsa, uint ethAmt) = abi.decode( + data, + (address, AaveDataRaw, address, uint) + ); + // TODO: deposit WETH "ethAmt" in Aave + _migrate(l2DSA, _data, sourceDsa); + // TODO: withdraw WETH "ethAmt" from Aave + // TODO: approve WETH "ethAmt + 2" to dydx + } + +} \ No newline at end of file