diff --git a/contracts/senders/aave-v2/events.sol b/contracts/senders/aave-v2-connector/events.sol similarity index 52% rename from contracts/senders/aave-v2/events.sol rename to contracts/senders/aave-v2-connector/events.sol index 0429bac..902bd55 100644 --- a/contracts/senders/aave-v2/events.sol +++ b/contracts/senders/aave-v2-connector/events.sol @@ -3,10 +3,8 @@ pragma solidity ^0.7.0; contract Events { event LogAaveV2Migrate( address indexed user, + address indexed targetDsa, address[] supplyTokens, - address[] borrowTokens, - uint[] supplyAmts, - uint[] stableBorrowAmts, - uint[] variableBorrowAmts + address[] borrowTokens ); } \ No newline at end of file diff --git a/contracts/senders/aave-v2-connector/helpers.sol b/contracts/senders/aave-v2-connector/helpers.sol new file mode 100644 index 0000000..92c5c35 --- /dev/null +++ b/contracts/senders/aave-v2-connector/helpers.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../common/math.sol"; +import { Stores } from "../../common/stores.sol"; +import { AaveLendingPoolProviderInterface, AaveDataProviderInterface, AaveMigratorInterface } from "./interfaces.sol"; + +abstract contract Helpers is DSMath, Stores { + + AaveMigratorInterface constant internal migrator = AaveMigratorInterface(address(2)); // Replace this (Migrator contract) + + /** + * @dev Aave Data Provider + */ + AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d); +} diff --git a/contracts/senders/aave-v2-connector/interfaces.sol b/contracts/senders/aave-v2-connector/interfaces.sol new file mode 100644 index 0000000..0c1a495 --- /dev/null +++ b/contracts/senders/aave-v2-connector/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 AaveMigratorInterface { + function migrate(address, address[] calldata, address[] calldata) external; +} + +// interface StateSenderInterface { +// function syncState(address receiver, bytes calldata data) external; +// function register(address sender, address receiver) external; +// } diff --git a/contracts/senders/aave-v2-connector/main.sol b/contracts/senders/aave-v2-connector/main.sol new file mode 100644 index 0000000..7950cc2 --- /dev/null +++ b/contracts/senders/aave-v2-connector/main.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { TokenInterface, AccountInterface } from "../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +contract AaveMigrateResolver is Helpers, Events { + + function migrate( + address targetDsa, + address[] calldata supplyTokens, + address[] calldata borrowTokens + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + require(supplyTokens.length > 0, "0-length-not-allowed"); + require(targetDsa != address(0), "invalid-address"); + + for (uint i = 0; i < supplyTokens.length; i++) { + address _token = supplyTokens[i] == ethAddr ? wethAddr : supplyTokens[i]; + (address _aToken, ,) = aaveData.getReserveTokensAddresses(_token); + ATokenInterface _aTokenContract = ATokenInterface(_aToken); + _aTokenContract.approve(address(migrator), _aTokenContract.balanceOf(address(this))); + } + + migrator.migrate(targetDsa, supplyTokens, borrowTokens); + + _eventName = "LogAaveV2Migrate(address,address,address[],address[])"; + _eventParam = abi.encode(msg.sender, targetDsa, supplyTokens, borrowTokens); + } +} + +contract AaveV2Migrator is AaveMigrateResolver { + string constant public name = "AaveV2PolygonMigrator-v1"; +} \ No newline at end of file diff --git a/contracts/senders/aave-v2-migrator/helpers.sol b/contracts/senders/aave-v2-migrator/helpers.sol new file mode 100644 index 0000000..2337aff --- /dev/null +++ b/contracts/senders/aave-v2-migrator/helpers.sol @@ -0,0 +1,80 @@ +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 { + /** + * @dev Aave referal code + */ + uint16 constant internal referalCode = 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 _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 _Withdraw( + uint _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint i = 0; i < _length; i++) { + if (amts[i] > 0) { + aave.withdraw(tokens[i], amts[i], address(this)); + } + } + } +} \ No newline at end of file diff --git a/contracts/senders/aave-v2/interfaces.sol b/contracts/senders/aave-v2-migrator/interfaces.sol similarity index 98% rename from contracts/senders/aave-v2/interfaces.sol rename to contracts/senders/aave-v2-migrator/interfaces.sol index d6af589..bb612fa 100644 --- a/contracts/senders/aave-v2/interfaces.sol +++ b/contracts/senders/aave-v2-migrator/interfaces.sol @@ -68,9 +68,10 @@ interface ATokenInterface { 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; -} +} \ No newline at end of file diff --git a/contracts/senders/aave-v2-migrator/main.sol b/contracts/senders/aave-v2-migrator/main.sol new file mode 100644 index 0000000..c96a20b --- /dev/null +++ b/contracts/senders/aave-v2-migrator/main.sol @@ -0,0 +1,111 @@ +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"; + +contract LiquidityResolver is Helpers { + using SafeERC20 for IERC20; + + mapping(address => mapping(address => uint)) deposits; + + function deposit(address[] calldata tokens, uint[] calldata amts) external payable { + uint _length = tokens.length; + require(_length == amts.length, "invalid-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; + } else { + IERC20 tokenContract = IERC20(_token); + _amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i]; + tokenContract.safeTransferFrom(msg.sender, address(this), _amt); + } + + deposits[_token][msg.sender] = _amt; + } + } +} + +contract MigrateResolver is LiquidityResolver { + struct AaveData { + bool isFinal; + address targetDsa; + uint[] supplyAmts; + uint[] variableBorrowAmts; + uint[] stableBorrowAmts; + address[] supplyTokens; + address[] borrowTokens; + } + + mapping (address => AaveData) public positions; + + function migrate( + address targetDsa, + address[] calldata supplyTokens, + address[] calldata borrowTokens + ) external { + require(supplyTokens.length > 0, "0-length-not-allowed"); + require(targetDsa != address(0), "invalid-address"); + + address sourceDsa = msg.sender; + + AaveInterface aave = AaveInterface(aaveProvider.getLendingPool()); + + AaveData memory data; + + (,,,,,uint healthFactor) = aave.getUserAccountData(sourceDsa); + require(healthFactor > 1e18, "position-not-safe"); + + data.supplyAmts = new uint[](supplyTokens.length); + data.supplyTokens = new address[](supplyTokens.length); + data.targetDsa = targetDsa; + + for (uint i = 0; i < supplyTokens.length; i++) { + address _token = supplyTokens[i] == ethAddr ? wethAddr : supplyTokens[i]; + (address _aToken, ,) = aaveData.getReserveTokensAddresses(_token); + + data.supplyTokens[i] = _token; + data.supplyAmts[i] = ATokenInterface(_aToken).balanceOf(sourceDsa); + } + + if (borrowTokens.length > 0) { + data.variableBorrowAmts = new uint[](borrowTokens.length); + data.stableBorrowAmts = new uint[](borrowTokens.length); + + for (uint i = 0; i < borrowTokens.length; i++) { + address _token = borrowTokens[i] == ethAddr ? wethAddr : borrowTokens[i]; + data.borrowTokens[i] = _token; + + ( + , + data.stableBorrowAmts[i], + data.variableBorrowAmts[i], + ,,,,, + ) = aaveData.getUserReserveData(_token, sourceDsa); + + uint totalBorrowAmt = add(data.stableBorrowAmts[i], data.variableBorrowAmts[i]); + + if (totalBorrowAmt > 0) { + TokenInterface(_token).approve(address(aave), totalBorrowAmt); + } + } + + _PaybackStable(borrowTokens.length, aave, data.borrowTokens, data.stableBorrowAmts, sourceDsa); + _PaybackVariable(borrowTokens.length, aave, data.borrowTokens, data.variableBorrowAmts, sourceDsa); + } + + _Withdraw(supplyTokens.length, aave, data.supplyTokens, data.supplyAmts); + + positions[sourceDsa] = data; + bytes memory positionData = abi.encode(sourceDsa, data); + stateSender.syncState(polygonReceiver, positionData); + } +} \ No newline at end of file diff --git a/contracts/senders/aave-v2/helpers.sol b/contracts/senders/aave-v2/helpers.sol deleted file mode 100644 index 220fe68..0000000 --- a/contracts/senders/aave-v2/helpers.sol +++ /dev/null @@ -1,35 +0,0 @@ -pragma solidity ^0.7.0; - -import { DSMath } from "../../common/math.sol"; -import { Stores } from "../../common/stores.sol"; -import { AaveLendingPoolProviderInterface, AaveDataProviderInterface, StateSenderInterface } from "./interfaces.sol"; - -abstract contract Helpers is DSMath, Stores { - /** - * @dev Aave referal code - */ - uint16 constant internal referalCode = 3228; - - address constant internal fundLocker = address(1); // Replace this - - 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 Sender - */ - StateSenderInterface constant internal stateSender = StateSenderInterface(0x28e4F3a7f651294B9564800b2D01f35189A5bFbE); - - function getIsColl(address token, address user) internal view returns (bool isCol) { - (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); - } -} diff --git a/contracts/senders/aave-v2/main.sol b/contracts/senders/aave-v2/main.sol deleted file mode 100644 index 6785c02..0000000 --- a/contracts/senders/aave-v2/main.sol +++ /dev/null @@ -1,142 +0,0 @@ -pragma solidity ^0.7.0; -pragma experimental ABIEncoderV2; - -import { TokenInterface, AccountInterface } from "../../common/interfaces.sol"; -import { AaveInterface, ATokenInterface } from "./interfaces.sol"; -import { Helpers } from "./helpers.sol"; -import { Events } from "./events.sol"; - -abstract contract AaveResolver is Helpers, Events { - 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 _Withdraw( - uint _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts - ) internal { - for (uint i = 0; i < _length; i++) { - if (amts[i] > 0) { - aave.withdraw(tokens[i], amts[i], fundLocker); - } - } - } -} - -contract AaveImportResolver is AaveResolver { - struct AaveData { - bool isFinal; - address targetDsa; - uint[] supplyAmts; - uint[] variableBorrowAmts; - uint[] stableBorrowAmts; - address[] supplyTokens; - address[] borrowTokens; - } - - // TODO - Move state syncing to Migrator - // mapping (address => AaveData) public positions; - - function migrate( - address targetDsa, - address[] calldata supplyTokens, - address[] calldata borrowTokens - ) external payable returns (string memory _eventName, bytes memory _eventParam) { - require(AccountInterface(address(this)).isAuth(msg.sender), "user-account-not-auth"); - require(supplyTokens.length > 0, "0-length-not-allowed"); - require(targetDsa != address(0), "invalid-address"); - - AaveData memory data; - - AaveInterface aave = AaveInterface(aaveProvider.getLendingPool()); - - (,,,,,uint healthFactor) = aave.getUserAccountData(address(this)); - require(healthFactor > 1e18, "position-not-safe"); - - data.supplyAmts = new uint[](supplyTokens.length); - data.supplyTokens = new address[](supplyTokens.length); - data.targetDsa = targetDsa; - - for (uint i = 0; i < supplyTokens.length; i++) { - address _token = supplyTokens[i] == ethAddr ? wethAddr : supplyTokens[i]; - (address _aToken, ,) = aaveData.getReserveTokensAddresses(_token); - data.supplyTokens[i] = _token; - data.supplyAmts[i] = ATokenInterface(_aToken).balanceOf(address(this)); - } - - if (borrowTokens.length > 0) { - data.variableBorrowAmts = new uint[](borrowTokens.length); - data.stableBorrowAmts = new uint[](borrowTokens.length); - - for (uint i = 0; i < borrowTokens.length; i++) { - address _token = borrowTokens[i] == ethAddr ? wethAddr : borrowTokens[i]; - data.borrowTokens[i] = _token; - - ( - , - data.stableBorrowAmts[i], - data.variableBorrowAmts[i], - ,,,,, - ) = aaveData.getUserReserveData(_token, address(this)); - - uint totalBorrowAmt = add(data.stableBorrowAmts[i], data.variableBorrowAmts[i]); - - if (totalBorrowAmt > 0) { - TokenInterface(_token).approve(address(aave), totalBorrowAmt); - } - } - - // TODO - Request liquidity from Migrator - - _PaybackStable(borrowTokens.length, aave, data.borrowTokens, data.stableBorrowAmts, address(this)); - _PaybackVariable(borrowTokens.length, aave, data.borrowTokens, data.variableBorrowAmts, address(this)); - } - - _Withdraw(supplyTokens.length, aave, data.supplyTokens, data.supplyAmts); - - // TODO - Move state syncing to Migrator - // positions[msg.sender] = data; - // bytes memory positionData = abi.encode(msg.sender, data); - // stateSender.syncState(polygonReceiver, positionData); - - _eventName = "LogAaveV2Migrate(address,address[],address[],uint256[],uint256[],uint256[])"; - _eventParam = abi.encode( - msg.sender, - supplyTokens, - borrowTokens, - data.supplyAmts, - data.stableBorrowAmts, - data.variableBorrowAmts - ); - } -} \ No newline at end of file