diff --git a/contracts/mainnet/connectors/euler-import/helpers.sol b/contracts/mainnet/connectors/euler-import/helpers.sol new file mode 100644 index 00000000..42824fdd --- /dev/null +++ b/contracts/mainnet/connectors/euler-import/helpers.sol @@ -0,0 +1,160 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +import { TokenInterface, AccountInterface } from "../../common/interfaces.sol"; +import { Basic } from "../../common/basic.sol"; +import "./interface.sol"; + +contract EulerHelpers is Basic { + + IEulerMarkets internal constant markets = + IEulerMarkets(0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3); + + IEulerExecute internal constant eulerExec = IEulerExecute(0x59828FdF7ee634AaaD3f58B19fDBa3b03E2D9d80); + + function getSubAccountAddress(address primary, uint256 subAccountId) + public + pure + returns (address) + { + require(subAccountId < 256, "sub-account-id-too-big"); + return address(uint160(primary) ^ uint160(subAccountId)); + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + EulerTokenInterface[] eTokens; + EulerTokenInterface[] dTokens; + uint256[] supplyAmts; + uint256[] borrowAmts; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.eTokens = new EulerTokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == ethAddr + ? wethAddr + : inputData.supplyTokens[i]; + data._supplyTokens[i] = _token; + data.eTokens[i] = EulerTokenInterface(markets.underlyingToEToken(_token)); + data.supplyAmts[i] = data.eTokens[i].balanceOf(userAccount); + } + + return data; + } + + function getBorrowAmounts( + address userAccount,//user's EOA sub-account address + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.borrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == ethAddr + ? wethAddr + : inputData.borrowTokens[i]; + + data._borrowTokens[i] = _token; + data.dTokens[i] = EulerTokenInterface(markets.underlyingToDToken(_token)); + data.borrowAmts[i] = data.dTokens[i].balanceOf(userAccount); + } + } + return data; + } + +//transfer and enter market + function _TransferEtokens( + uint256 _length, + EulerTokenInterface[] memory etokenContracts, + uint256[] memory amts, + address[] memory tokens, + bool[] memory enterMarket, + address userAccountFrom, + address userAccountTo, + uint targetId + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + etokenContracts[i].transferFrom( + userAccountFrom, + userAccountTo, + _amt + ), + "allowance?"//change + ); + + if (enterMarket[i]) { + markets.enterMarket(targetId, tokens[i]); + } else { + markets.exitMarket(targetId, tokens[i]); + } + } + } + } + + function _TransferDtokens( + uint256 _length, + EulerTokenInterface[] memory dtokenContracts, + uint256[] memory amts, + address[] memory tokens, + address eoaIdFrom, + address dsaIdTo + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + dtokenContracts[i].transferFrom( + eoaIdFrom, + dsaIdTo, + _amt + ), + "debt-transfer-failed?" + ); + } + } + } + + + +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/euler-import/interface.sol b/contracts/mainnet/connectors/euler-import/interface.sol new file mode 100644 index 00000000..a62a860b --- /dev/null +++ b/contracts/mainnet/connectors/euler-import/interface.sol @@ -0,0 +1,55 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +interface EulerTokenInterface { + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface IEulerMarkets { + function enterMarket(uint256 subAccountId, address newMarket) external; + + function getEnteredMarkets(address account) + external + view + returns (address[] memory); + + function exitMarket(uint256 subAccountId, address oldMarket) external; + + function underlyingToEToken(address underlying) + external + view + returns (address); + + function underlyingToDToken(address underlying) + external + view + returns (address); +} + +interface IEulerExecute { + + struct EulerBatchItem { + bool allowError; + address proxyAddr; + bytes data; + } + + struct EulerBatchItemResponse { + bool success; + bytes result; + } + + function batchDispatch(EulerBatchItem[] calldata items, address[] calldata deferLiquidityChecks) external returns (EulerBatchItemResponse[] memory); + + function deferLiquidityCheck(address account, bytes memory data) external; +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/euler-import/main.sol b/contracts/mainnet/connectors/euler-import/main.sol new file mode 100644 index 00000000..b9dfe0db --- /dev/null +++ b/contracts/mainnet/connectors/euler-import/main.sol @@ -0,0 +1,64 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +import "./helpers.sol"; + +contract EulerImport is EulerHelpers { + + function importEuler( + address userAccount,//EOA address + uint256 sourceId, + uint256 targetId, + bool[] memory enterMarket, + ImportInputData memory inputData + ) + external + payable + { + _importEuler(userAccount, sourceId, targetId, inputData, enterMarket); + } + + function _importEuler( + address userAccount,//EOA address + uint256 sourceId, + uint256 targetId, + ImportInputData memory inputData, + bool[] memory enterMarket + ) + internal + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + require(enterMarket.length == inputData.supplyTokens.length, "lengths-not-same"); + + address _sourceAccount = getSubAccountAddress(userAccount, sourceId); //User's EOA sub-account address + address _targetAccount = getSubAccountAddress(address(this), targetId); + + ImportData memory data; + + data = getBorrowAmounts(_sourceAccount, inputData, data); + data = getSupplyAmounts(_targetAccount, inputData, data); + + _TransferEtokens( + data._supplyTokens.length, + data.eTokens, + data.supplyAmts, + data._supplyTokens, + enterMarket, + _sourceAccount, + _targetAccount + ); + + _TransferDtokens( + data._borrowTokens.length, + data.dTokens, + data.borrowAmts, + data._borrowTokens, + _sourceAccount, + _targetAccount + ); + } +}