From 81c2e5edda688cf54e4003e969c4f62be4749c01 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Fri, 1 Apr 2022 10:13:17 +0530 Subject: [PATCH 01/25] added permit function for aave-v3 --- .../aave/v3-import-permit/events.sol | 15 + .../aave/v3-import-permit/helpers.sol | 309 ++++++++++++++++++ .../aave/v3-import-permit/interface.sol | 105 ++++++ .../connectors/aave/v3-import-permit/main.sol | 125 +++++++ 4 files changed, 554 insertions(+) create mode 100644 contracts/polygon/connectors/aave/v3-import-permit/events.sol create mode 100644 contracts/polygon/connectors/aave/v3-import-permit/helpers.sol create mode 100644 contracts/polygon/connectors/aave/v3-import-permit/interface.sol create mode 100644 contracts/polygon/connectors/aave/v3-import-permit/main.sol diff --git a/contracts/polygon/connectors/aave/v3-import-permit/events.sol b/contracts/polygon/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..03aafca4 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..7350486f --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,309 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } + + struct signedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = 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] == maticAddr + ? wmaticAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + 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.aTokens = new ATokenInterface[](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] == maticAddr + ? wmaticAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint256[] memory amts, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for(uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit(userAccount, address(this), amts[i], expiry[i], v[i], r[i], s[i]); + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/polygon/connectors/aave/v3-import-permit/interface.sol b/contracts/polygon/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..ab9bbd09 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,105 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +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 permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..3589123e --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,125 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ + +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + signedPermits memory permitData + ) internal returns (string memory _eventName, bytes memory _eventParam) { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + data.supplyAmts, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData, signedPermits memory permitData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData, permitData); + } +} + +contract ConnectV2AaveV3ImportPolygon is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} From a2a635196c2437310a4d25a208a55985d286c781 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Fri, 1 Apr 2022 10:37:50 +0530 Subject: [PATCH 02/25] updated param --- contracts/polygon/connectors/aave/v3-import-permit/main.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 3589123e..98e468fd 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -110,6 +110,7 @@ contract AaveV3ImportResolver is AaveHelpers { * @notice Import EOA's aave V3 position to DSA's aave v3 position * @param userAccount The address of the EOA from which aave position will be imported * @param inputData The struct containing all the neccessary input data + * @param permitData The struct containing signed permit data like v,r,s,expiry */ function importAave(address userAccount, ImportInputData memory inputData, signedPermits memory permitData) external From df450aa6f1f5791f12c7894fb1397f0ae6c45051 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Sat, 2 Apr 2022 15:23:19 +0530 Subject: [PATCH 03/25] added tests --- .../connectors/aave/v3-import-permit/main.sol | 6 +- test/polygon/aave/aaveV3-import-test.ts | 360 ++++++++++++++++++ 2 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 test/polygon/aave/aaveV3-import-test.ts diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 98e468fd..bbd3af95 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -11,7 +11,7 @@ import { AaveInterface, ATokenInterface } from "./interface.sol"; import "./helpers.sol"; import "./events.sol"; -contract AaveV3ImportResolver is AaveHelpers { +contract AaveV3ImportPermitResolver is AaveHelpers { function _importAave( address userAccount, ImportInputData memory inputData, @@ -121,6 +121,6 @@ contract AaveV3ImportResolver is AaveHelpers { } } -contract ConnectV2AaveV3ImportPolygon is AaveV3ImportResolver { - string public constant name = "Aave-v3-import-v1"; +contract ConnectV2AaveV3ImportPermitPolygon is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-permit-v1"; } diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts new file mode 100644 index 00000000..b12510f9 --- /dev/null +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -0,0 +1,360 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { toUtf8Bytes } from "@ethersproject/strings"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { tokens } from "../../../scripts/tests/polygon/tokens"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitPolygon__factory } from "../../../typechain"; +import { parse } from "path/posix"; +import { aave } from "dsa-connect/dist/abi/connectors/v1"; +const { provider } = waffle; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aEthAddress = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +const daiAddress = tokens.dai.address; +const mnemonic = "test test test test test test test test test test test junk"; + +describe("Import Aave", async function () { + const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; + + const aEthAbi = [ + { + inputs: [{ internalType: "address", name: "user", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "caller", type: "address" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "index", type: "uint256" } + ], + name: "mint", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "owner", type: "address" }], + name: "nonces", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const aDaiAbi = [ + { + inputs: [{ internalType: "address", name: "user", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "caller", type: "address" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "index", type: "uint256" } + ], + name: "mint", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "owner", type: "address" }], + name: "nonces", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const aaveAbi = [ + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "uint256", + name: "interestRateMode", + type: "uint256" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ]; + + const daiAbi = abis.basic.erc20; + + let aEth: Contract, aDai: Contract, Dai: any; + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + console.log(wallet.address); + + before(async () => { + // await hre.network.provider.request({ + // method: "hardhat_reset", + // params: [ + // { + // forking: { + // //@ts-ignore + // jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: await ethers.provider.getBlockNumber() + // } + // } + // ] + // }); + masterSigner = await getMasterSigner(); + console.log(await masterSigner.getAddress()); + + await hre.network.provider.send("hardhat_setBalance", [ + wallet.address, + ethers.utils.parseEther("10").toHexString() + ]); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + + //check + // const signerAddr = "0xDA9dfA130Df4dE4673b89022EE50ff26f6EA73Cf"; + // await hre.network.provider.request({ + // method: "hardhat_impersonateAccount", + // params: [signerAddr] + // }); + // let sig = await ethers.getSigner(signerAddr); + + console.log("Connector address", connector.address); + aEth = new ethers.Contract(aEthAddress, aEthAbi); + aDai = new ethers.Contract(aDaiAddress, aDaiAbi); + Dai = new ethers.Contract(daiAddress, daiAbi); + const aave = new ethers.Contract(aaveAddress, aaveAbi); + + //deposit ether to aave: ETH-A + await aave.connect(wallet).deposit(aEthAddress, parseEther("9"), wallet.address, 3228); + + //borrow + await aave.connect(wallet).borrow(aDaiAddress, parseUnits("100"), 1, 3228, wallet.address); + + // //building dsaWallet + dsaWallet0 = await buildDSAv2(wallet.address); + console.log(dsaWallet0.address); + await hre.network.provider.send("hardhat_setBalance", [ + wallet.address, + ethers.utils.parseEther("10").toHexString() + ]); + console.log("bro"); + + // deposit ETH to dsa wallet + await wallet.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + console.log(await ethers.provider.getBalance(dsaWallet0.address)); //should be 10 + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("check user AAVE position", async () => { + it("Should check position of user", async () => { + const exchangeRate = 0.9289; + expect(new BigNumber(await aEth.connect(wallet).balanceOf(wallet.address)).dividedBy(1e8).toFixed(0)).to.eq( + new BigNumber(9).dividedBy(exchangeRate).toFixed(0) + ); + expect(await Dai.connect(wallet).balanceOf(wallet.address)).to.eq("100000000000000000000"); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const name = "Aave ETH"; + const chainId = 137; + const DOMAIN_SEPARATOR = keccak256( + defaultAbiCoder.encode( + ["bytes32", "bytes32", "bytes32", "uint256", "address"], + [ + keccak256( + toUtf8Bytes("EIP712Domain(string name, string version, uint256 chainId, address verifyingContract)") + ), + keccak256(toUtf8Bytes(name)), + keccak256(toUtf8Bytes("1")), + chainId, + aEth.address + ] + ) + ); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + let nonces = await aEth.connect(wallet).nonces(wallet.address); + let nonce = nonces.toNumber(); + const amount = await aEth.connect(wallet).balanceOf(wallet.address); + const expiry = Date.now() + 20 * 60; + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, connector.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + + const amount0 = new BigNumber("100000007061117456728"); + const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + { + supplyTokens: ["ETH-A"], + borrowTokens: ["DAI-A"], + convertStable: false, + flashLoanFees: [amount.toFixed(0)] + }, + { v: [v], r: [r], s: [s], expiry: [expiry] } + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayBack", + args: [daiAddress, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [daiAddress, amount0.toString(), 0, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA COMPOUND position", async () => { + const ethExchangeRate = 0.9289; + expect(new BigNumber(await aEth.connect(wallet).balanceOf(dsaWallet0.address)).dividedBy(1e8).toFixed(0)).to.eq( + new BigNumber(9).dividedBy(ethExchangeRate).toFixed(0) + ); + }); + }); +}); From 985a8d36982cc44d6267cb4a52ef08d093a5f6bd Mon Sep 17 00:00:00 2001 From: pradyuman-verma Date: Sat, 2 Apr 2022 20:36:31 +0530 Subject: [PATCH 04/25] fixed test case logic --- test/polygon/aave/aaveV3-import-test.ts | 235 +++++++++--------------- 1 file changed, 85 insertions(+), 150 deletions(-) diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index b12510f9..311808a7 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -8,84 +8,34 @@ import { defaultAbiCoder } from "@ethersproject/abi"; import { BigNumber } from "bignumber.js"; import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; import { addresses } from "../../../scripts/tests/polygon/addresses"; -import { tokens } from "../../../scripts/tests/polygon/tokens"; import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; import { abis } from "../../../scripts/constant/abis"; import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; import { parseEther, parseUnits } from "ethers/lib/utils"; import { encodeSpells } from "../../../scripts/tests/encodeSpells"; import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; -import { ConnectV2AaveV3ImportPermitPolygon__factory } from "../../../typechain"; -import { parse } from "path/posix"; -import { aave } from "dsa-connect/dist/abi/connectors/v1"; -const { provider } = waffle; +import { ConnectV2AaveV3ImportPermitPolygon__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; -const aEthAddress = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; -const daiAddress = tokens.dai.address; +const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; +const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; const mnemonic = "test test test test test test test test test test test junk"; -describe("Import Aave", async function () { +const token = new ethers.Contract(DAI, IERC20__factory.abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, IERC20__factory.abi); + +describe("Import Aave", function () { const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; - - const aEthAbi = [ - { - inputs: [{ internalType: "address", name: "user", type: "address" }], - name: "balanceOf", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "caller", type: "address" }, - { internalType: "address", name: "onBehalfOf", type: "address" }, - { internalType: "uint256", name: "amount", type: "uint256" }, - { internalType: "uint256", name: "index", type: "uint256" } - ], - name: "mint", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [{ internalType: "address", name: "owner", type: "address" }], - name: "nonces", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - } - ]; - - const aDaiAbi = [ - { - inputs: [{ internalType: "address", name: "user", type: "address" }], - name: "balanceOf", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "caller", type: "address" }, - { internalType: "address", name: "onBehalfOf", type: "address" }, - { internalType: "uint256", name: "amount", type: "uint256" }, - { internalType: "uint256", name: "index", type: "uint256" } - ], - name: "mint", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [{ internalType: "address", name: "owner", type: "address" }], - name: "nonces", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function" - } - ]; + let signer: any, wallet0: any; const aaveAbi = [ { @@ -179,37 +129,39 @@ describe("Import Aave", async function () { } ]; - const daiAbi = abis.basic.erc20; - - let aEth: Contract, aDai: Contract, Dai: any; + let aEth: Contract; let dsaWallet0: any; let masterSigner: Signer; let instaConnectorsV2: Contract; let connector: any; const wallet = ethers.Wallet.fromMnemonic(mnemonic); - console.log(wallet.address); before(async () => { - // await hre.network.provider.request({ - // method: "hardhat_reset", - // params: [ - // { - // forking: { - // //@ts-ignore - // jsonRpcUrl: hre.config.networks.hardhat.forking.url, - // blockNumber: await ethers.provider.getBlockNumber() - // } - // } - // ] - // }); + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 26652016 + } + } + ] + }); masterSigner = await getMasterSigner(); - console.log(await masterSigner.getAddress()); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); - await hre.network.provider.send("hardhat_setBalance", [ - wallet.address, - ethers.utils.parseEther("10").toHexString() - ]); + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("100")); instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); connector = await deployAndEnableConnector({ @@ -219,41 +171,20 @@ describe("Import Aave", async function () { connectors: instaConnectorsV2 }); - //check - // const signerAddr = "0xDA9dfA130Df4dE4673b89022EE50ff26f6EA73Cf"; - // await hre.network.provider.request({ - // method: "hardhat_impersonateAccount", - // params: [signerAddr] - // }); - // let sig = await ethers.getSigner(signerAddr); - console.log("Connector address", connector.address); - aEth = new ethers.Contract(aEthAddress, aEthAbi); - aDai = new ethers.Contract(aDaiAddress, aDaiAbi); - Dai = new ethers.Contract(daiAddress, daiAbi); + const aave = new ethers.Contract(aaveAddress, aaveAbi); - //deposit ether to aave: ETH-A - await aave.connect(wallet).deposit(aEthAddress, parseEther("9"), wallet.address, 3228); + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("100")); - //borrow - await aave.connect(wallet).borrow(aDaiAddress, parseUnits("100"), 1, 3228, wallet.address); + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("100"), wallet.address, 3228); + console.log("Supplied DAI on aave"); - // //building dsaWallet - dsaWallet0 = await buildDSAv2(wallet.address); - console.log(dsaWallet0.address); - await hre.network.provider.send("hardhat_setBalance", [ - wallet.address, - ethers.utils.parseEther("10").toHexString() - ]); - console.log("bro"); - - // deposit ETH to dsa wallet - await wallet.sendTransaction({ - to: dsaWallet0.address, - value: ethers.utils.parseEther("10") - }); - console.log(await ethers.provider.getBalance(dsaWallet0.address)); //should be 10 + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("10", 6), 1, 3228, wallet.address); + console.log("Borrowed USDC from aave"); }); describe("Deployment", async () => { @@ -262,39 +193,44 @@ describe("Import Aave", async function () { }); }); + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + describe("check user AAVE position", async () => { it("Should check position of user", async () => { - const exchangeRate = 0.9289; - expect(new BigNumber(await aEth.connect(wallet).balanceOf(wallet.address)).dividedBy(1e8).toFixed(0)).to.eq( - new BigNumber(9).dividedBy(exchangeRate).toFixed(0) + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(100).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e6).toString() ); - expect(await Dai.connect(wallet).balanceOf(wallet.address)).to.eq("100000000000000000000"); }); }); describe("Aave position migration", async () => { it("Should migrate Aave position", async () => { - const name = "Aave ETH"; - const chainId = 137; - const DOMAIN_SEPARATOR = keccak256( - defaultAbiCoder.encode( - ["bytes32", "bytes32", "bytes32", "uint256", "address"], - [ - keccak256( - toUtf8Bytes("EIP712Domain(string name, string version, uint256 chainId, address verifyingContract)") - ), - keccak256(toUtf8Bytes(name)), - keccak256(toUtf8Bytes("1")), - chainId, - aEth.address - ] - ) - ); + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; - let nonces = await aEth.connect(wallet).nonces(wallet.address); + + let nonces = await aDai.connect(wallet0).nonces(wallet.address); let nonce = nonces.toNumber(); - const amount = await aEth.connect(wallet).balanceOf(wallet.address); + const amount = new BigNumber(await aDai.connect(wallet0).balanceOf(wallet.address)); const expiry = Date.now() + 20 * 60; + const digest = keccak256( ethers.utils.solidityPack( ["bytes1", "bytes1", "bytes32", "bytes32"], @@ -305,7 +241,7 @@ describe("Import Aave", async function () { keccak256( defaultAbiCoder.encode( ["bytes32", "address", "address", "uint256", "uint256", "uint256"], - [PERMIT_TYPEHASH, wallet.address, connector.address, amount, nonce, expiry] + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] ) ) ] @@ -313,7 +249,7 @@ describe("Import Aave", async function () { ); const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); - const amount0 = new BigNumber("100000007061117456728"); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); @@ -324,8 +260,8 @@ describe("Import Aave", async function () { args: [ wallet.address, { - supplyTokens: ["ETH-A"], - borrowTokens: ["DAI-A"], + supplyTokens: [DAI], + borrowTokens: [USDC], convertStable: false, flashLoanFees: [amount.toFixed(0)] }, @@ -335,7 +271,7 @@ describe("Import Aave", async function () { { connector: "INSTAPOOL-C", method: "flashPayBack", - args: [daiAddress, amountWithFee.toFixed(0), 0, 0] + args: [USDC, amountWithFee.toFixed(0), 0, 0] } ]; @@ -343,17 +279,16 @@ describe("Import Aave", async function () { { connector: "INSTAPOOL-C", method: "flashBorrowAndCast", - args: [daiAddress, amount0.toString(), 0, encodeFlashcastData(flashSpells), "0x"] + args: [USDC, amount0.toString(), 0, encodeFlashcastData(flashSpells), "0x"] } ]; - const tx = await dsaWallet0.connect(wallet).cast(...encodeSpells(spells), wallet.address); + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); const receipt = await tx.wait(); }); - it("Should check DSA COMPOUND position", async () => { - const ethExchangeRate = 0.9289; - expect(new BigNumber(await aEth.connect(wallet).balanceOf(dsaWallet0.address)).dividedBy(1e8).toFixed(0)).to.eq( - new BigNumber(9).dividedBy(ethExchangeRate).toFixed(0) + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(100).multipliedBy(1e18).toString() ); }); }); From 423a09097888a5c87cf0a3f6d15618b9361b71b6 Mon Sep 17 00:00:00 2001 From: pradyuman-verma Date: Sat, 2 Apr 2022 22:39:19 +0530 Subject: [PATCH 05/25] minor fixees --- test/polygon/aave/aaveV3-import-test.ts | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 311808a7..50d13ac4 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -129,7 +129,6 @@ describe("Import Aave", function () { } ]; - let aEth: Contract; let dsaWallet0: any; let masterSigner: Signer; let instaConnectorsV2: Contract; @@ -183,7 +182,7 @@ describe("Import Aave", function () { console.log("Supplied DAI on aave"); //borrow USDC from aave - await aave.connect(wallet0).borrow(USDC, parseUnits("10", 6), 1, 3228, wallet.address); + await aave.connect(wallet0).borrow(USDC, parseUnits("10", 6), 2, 3228, wallet.address); console.log("Borrowed USDC from aave"); }); @@ -226,9 +225,8 @@ describe("Import Aave", function () { const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; - let nonces = await aDai.connect(wallet0).nonces(wallet.address); - let nonce = nonces.toNumber(); - const amount = new BigNumber(await aDai.connect(wallet0).balanceOf(wallet.address)); + let nonce = await aDai.connect(wallet0).nonces(wallet.address); + const amount = await aDai.connect(wallet0).balanceOf(wallet.address); const expiry = Date.now() + 20 * 60; const digest = keccak256( @@ -250,27 +248,18 @@ describe("Import Aave", function () { const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); - const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); + const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); const flashSpells = [ { connector: "AAVE-V3-IMPORT-PERMIT-X", method: "importAave", - args: [ - wallet.address, - { - supplyTokens: [DAI], - borrowTokens: [USDC], - convertStable: false, - flashLoanFees: [amount.toFixed(0)] - }, - { v: [v], r: [r], s: [s], expiry: [expiry] } - ] + args: [wallet.address, [[DAI], [USDC], false, [amount]], [[v], [r], [s], [expiry]]] }, { connector: "INSTAPOOL-C", - method: "flashPayBack", + method: "flashPayback", args: [USDC, amountWithFee.toFixed(0), 0, 0] } ]; @@ -279,7 +268,7 @@ describe("Import Aave", function () { { connector: "INSTAPOOL-C", method: "flashBorrowAndCast", - args: [USDC, amount0.toString(), 0, encodeFlashcastData(flashSpells), "0x"] + args: [USDC, amount0.toString(), 5, encodeFlashcastData(flashSpells), "0x"] } ]; const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); From 5f7449fd27b80bc37ec2dc0beace442564046621 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Sun, 3 Apr 2022 17:31:16 +0530 Subject: [PATCH 06/25] Modified tests, amount approved --- .../aave/v3-import-permit/helpers.sol | 7 +- .../connectors/aave/v3-import-permit/main.sol | 1 - test/polygon/aave/aaveV3-import-test.ts | 258 +++++++++++++++++- 3 files changed, 248 insertions(+), 18 deletions(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol index 7350486f..ce1ba6a9 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -7,6 +7,7 @@ import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; import "./events.sol"; import "./interface.sol"; +import "hardhat/console.sol"; abstract contract Helper is DSMath, Basic { /** @@ -234,14 +235,16 @@ contract AaveHelpers is Helper { address userAccount, ATokenInterface[] memory aTokenContracts, address[] memory tokens, - uint256[] memory amts, uint8[] memory v, bytes32[] memory r, bytes32[] memory s, uint256[] memory expiry ) internal { + console.log("this"); + console.log(address(this)); for(uint256 i = 0; i < tokens.length; i++) { - aTokenContracts[i].permit(userAccount, address(this), amts[i], expiry[i], v[i], r[i], s[i]); + ATokenInterface aTokenContracts = ATokenInterface(tokens[i]); + aTokenContracts.permit(userAccount, address(this), uint(-1), expiry[i], v[i], r[i], s[i]); } } diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index bbd3af95..4a449cd9 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -52,7 +52,6 @@ contract AaveV3ImportPermitResolver is AaveHelpers { userAccount, data.aTokens, data._supplyTokens, - data.supplyAmts, permitData.v, permitData.r, permitData.s, diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 50d13ac4..4c762685 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -25,13 +25,237 @@ const ABI = [ const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +// const account = "0x44df7e55c643c3cB048465E176A443Ad5670A6fa"; const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; const mnemonic = "test test test test test test test test test test test junk"; -const token = new ethers.Contract(DAI, IERC20__factory.abi); +const erc20Abi = [ + + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +]; +const token = new ethers.Contract(DAI, erc20Abi); const aDai = new ethers.Contract(aDaiAddress, ABI); -const usdcToken = new ethers.Contract(USDC, IERC20__factory.abi); +const usdcToken = new ethers.Contract(USDC, erc20Abi); describe("Import Aave", function () { const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; @@ -160,7 +384,8 @@ describe("Import Aave", function () { signer = await ethers.getSigner(account); - await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("100")); + console.log(await token.connect(signer).balanceOf(signer.address)); + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); connector = await deployAndEnableConnector({ @@ -175,14 +400,14 @@ describe("Import Aave", function () { const aave = new ethers.Contract(aaveAddress, aaveAbi); // approve DAI to aavePool - await token.connect(wallet0).approve(aaveAddress, parseEther("100")); + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); //deposit DAI in aave - await aave.connect(wallet0).supply(DAI, parseEther("100"), wallet.address, 3228); + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); console.log("Supplied DAI on aave"); //borrow USDC from aave - await aave.connect(wallet0).borrow(USDC, parseUnits("10", 6), 2, 3228, wallet.address); + await aave.connect(wallet0).borrow(USDC, parseUnits("5", 6), 2, 3228, wallet.address); console.log("Borrowed USDC from aave"); }); @@ -201,21 +426,21 @@ describe("Import Aave", function () { it("Deposit ETH into DSA wallet", async function () { await wallet0.sendTransaction({ to: dsaWallet0.address, - value: ethers.utils.parseEther("10") + value: ethers.utils.parseEther("5") }); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); }); }); describe("check user AAVE position", async () => { it("Should check position of user", async () => { expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(100).multipliedBy(1e18).toString() + new BigNumber(8).multipliedBy(1e18).toString() ); expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(10).multipliedBy(1e6).toString() + new BigNumber(5).multipliedBy(1e6).toString() ); }); }); @@ -224,8 +449,8 @@ describe("Import Aave", function () { it("Should migrate Aave position", async () => { const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; - - let nonce = await aDai.connect(wallet0).nonces(wallet.address); + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); const amount = await aDai.connect(wallet0).balanceOf(wallet.address); const expiry = Date.now() + 20 * 60; @@ -246,7 +471,10 @@ describe("Import Aave", function () { ) ); const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); - + // console.log(v); + // console.log(pubToAddress(ecrecover(Buffer.from(digest.slice(2),'hex'),v,r,s))); + // console.log(dsaWallet0.address); + // console.log(wallet.address); const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); @@ -255,7 +483,7 @@ describe("Import Aave", function () { { connector: "AAVE-V3-IMPORT-PERMIT-X", method: "importAave", - args: [wallet.address, [[DAI], [USDC], false, [amount]], [[v], [r], [s], [expiry]]] + args: [wallet.address, [[DAI], [USDC], false, [amountB]], [[v], [r], [s], [expiry]]] }, { connector: "INSTAPOOL-C", @@ -277,7 +505,7 @@ describe("Import Aave", function () { it("Should check DSA AAVE position", async () => { expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( - new BigNumber(100).multipliedBy(1e18).toString() + new BigNumber(8).multipliedBy(1e18).toString() ); }); }); From 775f062cf933bbc41eee39f456ad3403887ff3f9 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Sun, 3 Apr 2022 18:26:58 +0530 Subject: [PATCH 07/25] minor modifications --- contracts/polygon/connectors/aave/v3-import-permit/helpers.sol | 3 --- test/polygon/aave/aaveV3-import-test.ts | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol index ce1ba6a9..b7eb7cd9 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -7,7 +7,6 @@ import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; import "./events.sol"; import "./interface.sol"; -import "hardhat/console.sol"; abstract contract Helper is DSMath, Basic { /** @@ -240,8 +239,6 @@ contract AaveHelpers is Helper { bytes32[] memory s, uint256[] memory expiry ) internal { - console.log("this"); - console.log(address(this)); for(uint256 i = 0; i < tokens.length; i++) { ATokenInterface aTokenContracts = ATokenInterface(tokens[i]); aTokenContracts.permit(userAccount, address(this), uint(-1), expiry[i], v[i], r[i], s[i]); diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 4c762685..d9ddbf5c 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -478,12 +478,13 @@ describe("Import Aave", function () { const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); + console.log(amountWithFee); const flashSpells = [ { connector: "AAVE-V3-IMPORT-PERMIT-X", method: "importAave", - args: [wallet.address, [[DAI], [USDC], false, [amountB]], [[v], [r], [s], [expiry]]] + args: [wallet.address, [[DAI], [USDC], false, [amountB.toFixed(0)]], [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]]] }, { connector: "INSTAPOOL-C", From bc12131ccf6ec1cd78b44c1d289404654c6a809e Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Mon, 4 Apr 2022 01:41:52 +0530 Subject: [PATCH 08/25] updated tests --- .../aave/v3-import-permit/helpers.sol | 3 +- test/polygon/aave/aaveV3-import-test.ts | 157 +----------------- 2 files changed, 5 insertions(+), 155 deletions(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol index b7eb7cd9..54afb590 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -240,8 +240,7 @@ contract AaveHelpers is Helper { uint256[] memory expiry ) internal { for(uint256 i = 0; i < tokens.length; i++) { - ATokenInterface aTokenContracts = ATokenInterface(tokens[i]); - aTokenContracts.permit(userAccount, address(this), uint(-1), expiry[i], v[i], r[i], s[i]); + aTokenContracts[i].permit(userAccount, address(this), uint(-1), expiry[i], v[i], r[i], s[i]); } } diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index d9ddbf5c..c5729575 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -25,27 +25,11 @@ const ABI = [ const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; -// const account = "0x44df7e55c643c3cB048465E176A443Ad5670A6fa"; const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; const mnemonic = "test test test test test test test test test test test junk"; const erc20Abi = [ - - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, { "constant": false, "inputs": [ @@ -83,47 +67,6 @@ const erc20Abi = [ "stateMutability": "view", "type": "function" }, - { - "constant": false, - "inputs": [ - { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, { "constant": true, "inputs": [ @@ -143,20 +86,6 @@ const erc20Abi = [ "stateMutability": "view", "type": "function" }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, { "constant": false, "inputs": [ @@ -179,80 +108,9 @@ const erc20Abi = [ "payable": false, "stateMutability": "nonpayable", "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "from", - "type": "address" - }, - { - "indexed": true, - "name": "to", - "type": "address" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" } ]; + const token = new ethers.Contract(DAI, erc20Abi); const aDai = new ethers.Contract(aDaiAddress, ABI); const usdcToken = new ethers.Contract(USDC, erc20Abi); @@ -384,7 +242,6 @@ describe("Import Aave", function () { signer = await ethers.getSigner(account); - console.log(await token.connect(signer).balanceOf(signer.address)); await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); @@ -395,8 +252,6 @@ describe("Import Aave", function () { connectors: instaConnectorsV2 }); - console.log("Connector address", connector.address); - const aave = new ethers.Contract(aaveAddress, aaveAbi); // approve DAI to aavePool @@ -451,7 +306,8 @@ describe("Import Aave", function () { const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); - const amount = await aDai.connect(wallet0).balanceOf(wallet.address); + //Approving max amount + const amount = ethers.constants.MaxUint256; const expiry = Date.now() + 20 * 60; const digest = keccak256( @@ -471,20 +327,15 @@ describe("Import Aave", function () { ) ); const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); - // console.log(v); - // console.log(pubToAddress(ecrecover(Buffer.from(digest.slice(2),'hex'),v,r,s))); - // console.log(dsaWallet0.address); - // console.log(wallet.address); const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); - console.log(amountWithFee); const flashSpells = [ { connector: "AAVE-V3-IMPORT-PERMIT-X", method: "importAave", - args: [wallet.address, [[DAI], [USDC], false, [amountB.toFixed(0)]], [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]]] + args: [wallet.address, [[DAI], [USDC], false, [amountB.toFixed(0)]], [[ethers.utils.hexlify(v)], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]]] }, { connector: "INSTAPOOL-C", From b859ad49e64cae289ff54c135c0fcacf6bc0d19c Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Mon, 4 Apr 2022 02:01:04 +0530 Subject: [PATCH 09/25] linted code --- .../aave/v3-import-permit/helpers.sol | 12 ++- .../connectors/aave/v3-import-permit/main.sol | 12 ++- test/polygon/aave/aaveV3-import-test.ts | 100 +++++++++--------- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol index 54afb590..5c1ba5ba 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -239,8 +239,16 @@ contract AaveHelpers is Helper { bytes32[] memory s, uint256[] memory expiry ) internal { - for(uint256 i = 0; i < tokens.length; i++) { - aTokenContracts[i].permit(userAccount, address(this), uint(-1), expiry[i], v[i], r[i], s[i]); + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); } } diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 4a449cd9..199366f4 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -111,12 +111,20 @@ contract AaveV3ImportPermitResolver is AaveHelpers { * @param inputData The struct containing all the neccessary input data * @param permitData The struct containing signed permit data like v,r,s,expiry */ - function importAave(address userAccount, ImportInputData memory inputData, signedPermits memory permitData) + function importAave( + address userAccount, + ImportInputData memory inputData, + signedPermits memory permitData + ) external payable returns (string memory _eventName, bytes memory _eventParam) { - (_eventName, _eventParam) = _importAave(userAccount, inputData, permitData); + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); } } diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index c5729575..5dbbb5ed 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -31,83 +31,83 @@ const mnemonic = "test test test test test test test test test test test junk"; const erc20Abi = [ { - "constant": false, - "inputs": [ + constant: false, + inputs: [ { - "name": "_spender", - "type": "address" + name: "_spender", + type: "address" }, { - "name": "_value", - "type": "uint256" + name: "_value", + type: "uint256" } ], - "name": "approve", - "outputs": [ + name: "approve", + outputs: [ { - "name": "", - "type": "bool" + name: "", + type: "bool" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" + payable: false, + stateMutability: "nonpayable", + type: "function" }, { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ { - "name": "", - "type": "uint256" + name: "", + type: "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function" }, { - "constant": true, - "inputs": [ + constant: true, + inputs: [ { - "name": "_owner", - "type": "address" + name: "_owner", + type: "address" } ], - "name": "balanceOf", - "outputs": [ + name: "balanceOf", + outputs: [ { - "name": "balance", - "type": "uint256" + name: "balance", + type: "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function" }, { - "constant": false, - "inputs": [ + constant: false, + inputs: [ { - "name": "_to", - "type": "address" + name: "_to", + type: "address" }, { - "name": "_value", - "type": "uint256" + name: "_value", + type: "uint256" } ], - "name": "transfer", - "outputs": [ + name: "transfer", + outputs: [ { - "name": "", - "type": "bool" + name: "", + type: "bool" } ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" + payable: false, + stateMutability: "nonpayable", + type: "function" } ]; @@ -304,7 +304,7 @@ describe("Import Aave", function () { it("Should migrate Aave position", async () => { const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; - + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); //Approving max amount const amount = ethers.constants.MaxUint256; @@ -335,7 +335,11 @@ describe("Import Aave", function () { { connector: "AAVE-V3-IMPORT-PERMIT-X", method: "importAave", - args: [wallet.address, [[DAI], [USDC], false, [amountB.toFixed(0)]], [[ethers.utils.hexlify(v)], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]]] + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[ethers.utils.hexlify(v)], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] }, { connector: "INSTAPOOL-C", From 6c868a624429507b4f94f364a1ee6fddb90da785 Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:43:19 +0530 Subject: [PATCH 10/25] minor change Co-authored-by: Thrilok kumar --- contracts/polygon/connectors/aave/v3-import-permit/helpers.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol index 5c1ba5ba..dc514687 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/helpers.sol @@ -55,7 +55,7 @@ abstract contract Helper is DSMath, Basic { uint256[] flashLoanFees; } - struct signedPermits { + struct SignedPermits { uint8[] v; bytes32[] r; bytes32[] s; From 97018040e5dc470cd66fd521ed4599eaa0c70466 Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:43:37 +0530 Subject: [PATCH 11/25] minor change Co-authored-by: Thrilok kumar --- contracts/polygon/connectors/aave/v3-import-permit/main.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 199366f4..9ef1ea98 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -15,7 +15,7 @@ contract AaveV3ImportPermitResolver is AaveHelpers { function _importAave( address userAccount, ImportInputData memory inputData, - signedPermits memory permitData + SignedPermits memory permitData ) internal returns (string memory _eventName, bytes memory _eventParam) { require( AccountInterface(address(this)).isAuth(userAccount), From 4477a0c535da6f0ddb02d11f525a848d3d749119 Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:43:54 +0530 Subject: [PATCH 12/25] minor change Co-authored-by: Thrilok kumar --- contracts/polygon/connectors/aave/v3-import-permit/main.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 9ef1ea98..29593b3f 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -114,7 +114,7 @@ contract AaveV3ImportPermitResolver is AaveHelpers { function importAave( address userAccount, ImportInputData memory inputData, - signedPermits memory permitData + SignedPermits memory permitData ) external payable From 3b91d64d65754673b9a7080bf8523b7ea0389563 Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:47:01 +0530 Subject: [PATCH 13/25] minor modification Co-authored-by: Thrilok kumar --- test/polygon/aave/aaveV3-import-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 5dbbb5ed..9c9744f3 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -115,7 +115,7 @@ const token = new ethers.Contract(DAI, erc20Abi); const aDai = new ethers.Contract(aDaiAddress, ABI); const usdcToken = new ethers.Contract(USDC, erc20Abi); -describe("Import Aave", function () { +describe("Import Aave V3 with Permit", function () { const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; let signer: any, wallet0: any; From 82add64d437e13c37b276e17da5e5de106c45f90 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Mon, 4 Apr 2022 21:26:29 +0530 Subject: [PATCH 14/25] updated tests --- test/polygon/aave/aaveV3-import-test.ts | 235 ++++++++++++------------ 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 9c9744f3..5b65fd80 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -28,6 +28,100 @@ const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "uint256", + name: "interestRateMode", + type: "uint256" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; const erc20Abi = [ { @@ -114,103 +208,9 @@ const erc20Abi = [ const token = new ethers.Contract(DAI, erc20Abi); const aDai = new ethers.Contract(aDaiAddress, ABI); const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); -describe("Import Aave V3 with Permit", function () { - const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; - let signer: any, wallet0: any; - - const aaveAbi = [ - { - inputs: [ - { - internalType: "address", - name: "asset", - type: "address" - }, - { - internalType: "uint256", - name: "amount", - type: "uint256" - }, - { - internalType: "uint256", - name: "interestRateMode", - type: "uint256" - }, - { - internalType: "uint16", - name: "referralCode", - type: "uint16" - }, - { - internalType: "address", - name: "onBehalfOf", - type: "address" - } - ], - name: "borrow", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "address", - name: "asset", - type: "address" - }, - { - internalType: "uint256", - name: "amount", - type: "uint256" - }, - { - internalType: "address", - name: "onBehalfOf", - type: "address" - }, - { - internalType: "uint16", - name: "referralCode", - type: "uint16" - } - ], - name: "deposit", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "address", - name: "asset", - type: "address" - }, - { - internalType: "uint256", - name: "amount", - type: "uint256" - }, - { - internalType: "address", - name: "onBehalfOf", - type: "address" - }, - { - internalType: "uint16", - name: "referralCode", - type: "uint16" - } - ], - name: "supply", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ]; - +describe("Import Aave", function () { let dsaWallet0: any; let masterSigner: Signer; let instaConnectorsV2: Contract; @@ -251,19 +251,32 @@ describe("Import Aave V3 with Permit", function () { signer: masterSigner, connectors: instaConnectorsV2 }); + + }); - const aave = new ethers.Contract(aaveAddress, aaveAbi); + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); - // approve DAI to aavePool - await token.connect(wallet0).approve(aaveAddress, parseEther("8")); + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); + console.log("Supplied DAI on aave"); - //deposit DAI in aave - await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); - console.log("Supplied DAI on aave"); + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("5", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); - //borrow USDC from aave - await aave.connect(wallet0).borrow(USDC, parseUnits("5", 6), 2, 3228, wallet.address); - console.log("Borrowed USDC from aave"); + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(5).multipliedBy(1e6).toString() + ); + }); }); describe("Deployment", async () => { @@ -288,18 +301,6 @@ describe("Import Aave V3 with Permit", function () { }); }); - describe("check user AAVE position", async () => { - it("Should check position of user", async () => { - expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(8).multipliedBy(1e18).toString() - ); - - expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(5).multipliedBy(1e6).toString() - ); - }); - }); - describe("Aave position migration", async () => { it("Should migrate Aave position", async () => { const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); From a131fb00174c247331d13bf1f002e127331136b6 Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 21:48:01 +0530 Subject: [PATCH 15/25] updated event name Co-authored-by: 0xPradyuman <63545809+pradyuman-verma@users.noreply.github.com> --- contracts/polygon/connectors/aave/v3-import-permit/events.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/events.sol b/contracts/polygon/connectors/aave/v3-import-permit/events.sol index 03aafca4..30cd814e 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/events.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/events.sol @@ -3,7 +3,7 @@ pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; contract Events { - event LogAaveV3Import( + event LogAaveV3ImportWithPermit( address indexed user, address[] ctokens, string[] supplyIds, From 51111c07cdf99a65c044822ac707d69e72054e2e Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 21:48:26 +0530 Subject: [PATCH 16/25] updated event name Co-authored-by: 0xPradyuman <63545809+pradyuman-verma@users.noreply.github.com> --- contracts/polygon/connectors/aave/v3-import-permit/main.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/main.sol b/contracts/polygon/connectors/aave/v3-import-permit/main.sol index 29593b3f..8a6c61a5 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/main.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/main.sol @@ -91,7 +91,7 @@ contract AaveV3ImportPermitResolver is AaveHelpers { ); } - _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; _eventParam = abi.encode( userAccount, inputData.convertStable, From b39410963046137d67932db3a2dfacda68b7a45a Mon Sep 17 00:00:00 2001 From: Richa <76250660+Richa-iitr@users.noreply.github.com> Date: Mon, 4 Apr 2022 21:49:53 +0530 Subject: [PATCH 17/25] minor change Co-authored-by: 0xPradyuman <63545809+pradyuman-verma@users.noreply.github.com> --- contracts/polygon/connectors/aave/v3-import-permit/events.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/events.sol b/contracts/polygon/connectors/aave/v3-import-permit/events.sol index 30cd814e..10a93d70 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/events.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/events.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; contract Events { event LogAaveV3ImportWithPermit( address indexed user, - address[] ctokens, + address[] atokens, string[] supplyIds, string[] borrowIds, uint256[] flashLoanFees, From 7bb8a15186770c66dd0b1ede0a87d44c6beeacf0 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Mon, 4 Apr 2022 23:15:58 +0530 Subject: [PATCH 18/25] minor correction --- .../aave/v3-import-permit/interface.sol | 16 ++++++++-------- test/polygon/aave/aaveV3-import-test.ts | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/contracts/polygon/connectors/aave/v3-import-permit/interface.sol b/contracts/polygon/connectors/aave/v3-import-permit/interface.sol index ab9bbd09..32140247 100644 --- a/contracts/polygon/connectors/aave/v3-import-permit/interface.sol +++ b/contracts/polygon/connectors/aave/v3-import-permit/interface.sol @@ -49,14 +49,14 @@ interface ATokenInterface { function balanceOf(address _user) external view returns (uint256); function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; function transferFrom( address, diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts index 5b65fd80..86fd50dd 100644 --- a/test/polygon/aave/aaveV3-import-test.ts +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -210,7 +210,7 @@ const aDai = new ethers.Contract(aDaiAddress, ABI); const usdcToken = new ethers.Contract(USDC, erc20Abi); const aave = new ethers.Contract(aaveAddress, aaveAbi); -describe("Import Aave", function () { +describe("Import Aave v3 Position", function () { let dsaWallet0: any; let masterSigner: Signer; let instaConnectorsV2: Contract; @@ -251,7 +251,6 @@ describe("Import Aave", function () { signer: masterSigner, connectors: instaConnectorsV2 }); - }); describe("check user AAVE position", async () => { From 35e83ea25abe37f3c8459658611199edd02e9d7d Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Wed, 6 Apr 2022 01:30:27 +0530 Subject: [PATCH 19/25] added connector for avalanche, fantom, arbitrum, optimism --- .../aave/v3-import-permit/events.sol | 15 + .../aave/v3-import-permit/helpers.sol | 316 ++++++++++++++++ .../aave/v3-import-permit/interface.sol | 105 ++++++ .../connectors/aave/v3-import-permit/main.sol | 132 +++++++ .../aave/v3-import-permit/events.sol | 15 + .../aave/v3-import-permit/helpers.sol | 318 ++++++++++++++++ .../aave/v3-import-permit/interface.sol | 105 ++++++ .../connectors/aave/v3-import-permit/main.sol | 132 +++++++ .../aave/v3-import-permit/events.sol | 15 + .../aave/v3-import-permit/helpers.sol | 316 ++++++++++++++++ .../aave/v3-import-permit/interface.sol | 105 ++++++ .../connectors/aave/v3-import-permit/main.sol | 133 +++++++ .../aave/v3-import-permit/events.sol | 15 + .../aave/v3-import-permit/helpers.sol | 316 ++++++++++++++++ .../aave/v3-import-permit/interface.sol | 105 ++++++ .../connectors/aave/v3-import-permit/main.sol | 133 +++++++ test/arbitrum/aave/aaveV3-import-test.ts | 303 +++++++++++++++ test/avalanche/aave/aaveV3-import-test.ts | 353 ++++++++++++++++++ 18 files changed, 2932 insertions(+) create mode 100644 contracts/arbitrum/connectors/aave/v3-import-permit/events.sol create mode 100644 contracts/arbitrum/connectors/aave/v3-import-permit/helpers.sol create mode 100644 contracts/arbitrum/connectors/aave/v3-import-permit/interface.sol create mode 100644 contracts/arbitrum/connectors/aave/v3-import-permit/main.sol create mode 100644 contracts/avalanche/connectors/aave/v3-import-permit/events.sol create mode 100644 contracts/avalanche/connectors/aave/v3-import-permit/helpers.sol create mode 100644 contracts/avalanche/connectors/aave/v3-import-permit/interface.sol create mode 100644 contracts/avalanche/connectors/aave/v3-import-permit/main.sol create mode 100644 contracts/fantom/connectors/aave/v3-import-permit/events.sol create mode 100644 contracts/fantom/connectors/aave/v3-import-permit/helpers.sol create mode 100644 contracts/fantom/connectors/aave/v3-import-permit/interface.sol create mode 100644 contracts/fantom/connectors/aave/v3-import-permit/main.sol create mode 100644 contracts/optimism/connectors/aave/v3-import-permit/events.sol create mode 100644 contracts/optimism/connectors/aave/v3-import-permit/helpers.sol create mode 100644 contracts/optimism/connectors/aave/v3-import-permit/interface.sol create mode 100644 contracts/optimism/connectors/aave/v3-import-permit/main.sol create mode 100644 test/arbitrum/aave/aaveV3-import-test.ts create mode 100644 test/avalanche/aave/aaveV3-import-test.ts diff --git a/contracts/arbitrum/connectors/aave/v3-import-permit/events.sol b/contracts/arbitrum/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..10a93d70 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3ImportWithPermit( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/arbitrum/connectors/aave/v3-import-permit/helpers.sol b/contracts/arbitrum/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..61b1687d --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,316 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } + + struct SignedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = 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.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + 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.aTokens = new ATokenInterface[](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]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/arbitrum/connectors/aave/v3-import-permit/interface.sol b/contracts/arbitrum/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..dfd6945f --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,105 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +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, + uint256 + ) external returns (bool); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/arbitrum/connectors/aave/v3-import-permit/main.sol b/contracts/arbitrum/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..2a3aafe8 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,132 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportPermitResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) internal returns (string memory _eventName, bytes memory _eventParam) { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + * @param permitData The struct containing signed permit data like v,r,s,expiry + */ + function importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); + } +} + +contract ConnectV2AaveV3ImportPermitArbitrum is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-permit-v1"; +} diff --git a/contracts/avalanche/connectors/aave/v3-import-permit/events.sol b/contracts/avalanche/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..10a93d70 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3ImportWithPermit( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/avalanche/connectors/aave/v3-import-permit/helpers.sol b/contracts/avalanche/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..2d77c4fb --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,318 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } + + struct SignedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = 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] == avaxAddr + ? wavaxAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.variableBorrowAmtsWithFee[i] = data.variableBorrowAmts[ + i + ]; + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + 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.aTokens = new ATokenInterface[](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] == avaxAddr + ? wavaxAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/avalanche/connectors/aave/v3-import-permit/interface.sol b/contracts/avalanche/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..55f15512 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,105 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +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 permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/avalanche/connectors/aave/v3-import-permit/main.sol b/contracts/avalanche/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..96f000ac --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,132 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportPermitResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) internal returns (string memory _eventName, bytes memory _eventParam) { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + * @param permitData The struct containing signed permit data like v,r,s,expiry + */ + function importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); + } +} + +contract ConnectV2AaveV3ImportPermitAvalanche is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-permit-v1"; +} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/events.sol b/contracts/fantom/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..10a93d70 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3ImportWithPermit( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol b/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..7e0b3a8a --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,316 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } + + struct SignedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = 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] == ftmAddr + ? wftmAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + 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.aTokens = new ATokenInterface[](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] == ftmAddr + ? wftmAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/interface.sol b/contracts/fantom/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..32140247 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,105 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +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 permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/main.sol b/contracts/fantom/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..655d5a54 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,133 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ + +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportPermitResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) internal returns (string memory _eventName, bytes memory _eventParam) { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + * @param permitData The struct containing signed permit data like v,r,s,expiry + */ + function importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); + } +} + +contract ConnectV2AaveV3ImportPermitFantom is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/events.sol b/contracts/optimism/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..10a93d70 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3ImportWithPermit( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol b/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..1e8fbfa0 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,316 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } + + struct SignedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = 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.supplyTokens[i] == ethAddr + ? wethAddr + : inputData.supplyTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + 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.aTokens = new ATokenInterface[](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]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/interface.sol b/contracts/optimism/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..32140247 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,105 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +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 permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/main.sol b/contracts/optimism/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..17fd9a6c --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,133 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector. + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ + +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportPermitResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) internal returns (string memory _eventName, bytes memory _eventParam) { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + * @param permitData The struct containing signed permit data like v,r,s,expiry + */ + function importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); + } +} + +contract ConnectV2AaveV3ImportPermitOptimism is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-permit-v1"; +} diff --git a/test/arbitrum/aave/aaveV3-import-test.ts b/test/arbitrum/aave/aaveV3-import-test.ts new file mode 100644 index 00000000..88ce4533 --- /dev/null +++ b/test/arbitrum/aave/aaveV3-import-test.ts @@ -0,0 +1,303 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitArbitrum__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +let account = "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "interestRateMode", type: "uint256" }, + { internalType: "uint16", name: "referralCode", type: "uint16" }, + { internalType: "address", name: "onBehalfOf", type: "address" } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint16", name: "referralCode", type: "uint16" } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position for Arbitrum", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 9333600 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("10")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("10"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("3", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toString(), 5, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/avalanche/aave/aaveV3-import-test.ts b/test/avalanche/aave/aaveV3-import-test.ts new file mode 100644 index 00000000..c59cb7c5 --- /dev/null +++ b/test/avalanche/aave/aaveV3-import-test.ts @@ -0,0 +1,353 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitAvalanche__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358d6845594f94dc1db02a252b5b4814ad"; +// const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +let account = "0x95eEA1Bdd19A8C40E9575048Dd0d6577D11a84e5"; +const DAI = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; +const USDC = "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi =[ + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "uint256", + name: "interestRateMode", + type: "uint256" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13024200 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("3", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("3") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("3")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toString(), 1, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e18).toString() + ); + }); + }); +}); From e540b892acc592cf08825974d829794e58fdebac Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Wed, 6 Apr 2022 03:48:10 +0530 Subject: [PATCH 20/25] updated test for avalanche --- test/avalanche/aave/aaveV3-import-test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/avalanche/aave/aaveV3-import-test.ts b/test/avalanche/aave/aaveV3-import-test.ts index c59cb7c5..cb2cd71c 100644 --- a/test/avalanche/aave/aaveV3-import-test.ts +++ b/test/avalanche/aave/aaveV3-import-test.ts @@ -6,7 +6,7 @@ import { keccak256 } from "@ethersproject/keccak256"; import { defaultAbiCoder } from "@ethersproject/abi"; import { BigNumber } from "bignumber.js"; import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; -import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; import { abis } from "../../../scripts/constant/abis"; import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; @@ -26,7 +26,7 @@ const aaveAddress = "0x794a61358d6845594f94dc1db02a252b5b4814ad"; // const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; let account = "0x95eEA1Bdd19A8C40E9575048Dd0d6577D11a84e5"; const DAI = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; -const USDC = "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; +const ETH = "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB"; const mnemonic = "test test test test test test test test test test test junk"; const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; let signer: any, wallet0: any; @@ -193,10 +193,10 @@ const erc20Abi = [ const token = new ethers.Contract(DAI, erc20Abi); const aDai = new ethers.Contract(aDaiAddress, ABI); -const usdcToken = new ethers.Contract(USDC, erc20Abi); +const ethToken = new ethers.Contract(ETH, erc20Abi); const aave = new ethers.Contract(aaveAddress, aaveAbi); -describe("Import Aave v3 Position", function () { +describe("Import Aave v3 Position for Avalanche", function () { let dsaWallet0: any; let masterSigner: Signer; let instaConnectorsV2: Contract; @@ -248,7 +248,7 @@ describe("Import Aave v3 Position", function () { console.log("Supplied DAI on aave"); //borrow USDC from aave - await aave.connect(wallet0).borrow(USDC, parseUnits("3", 6), 2, 3228, wallet.address); + await aave.connect(wallet0).borrow(ETH, parseUnits("3", 6), 2, 3228, wallet.address); console.log("Borrowed USDC from aave"); }); @@ -257,7 +257,7 @@ describe("Import Aave v3 Position", function () { new BigNumber(8).multipliedBy(1e18).toString() ); - expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + expect(await ethToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( new BigNumber(3).multipliedBy(1e6).toString() ); }); @@ -312,7 +312,7 @@ describe("Import Aave v3 Position", function () { ) ); const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); - const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + const amount0 = new BigNumber(await ethToken.connect(wallet0).balanceOf(wallet.address)); const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); const amountWithFee = amount0.plus(amountB); @@ -322,14 +322,14 @@ describe("Import Aave v3 Position", function () { method: "importAave", args: [ wallet.address, - [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[DAI], [ETH], false, [amountB.toFixed(0)]], [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] ] }, { connector: "INSTAPOOL-C", method: "flashPayback", - args: [USDC, amountWithFee.toFixed(0), 0, 0] + args: [ETH, amountWithFee.toFixed(0), 0, 0] } ]; @@ -337,7 +337,7 @@ describe("Import Aave v3 Position", function () { { connector: "INSTAPOOL-C", method: "flashBorrowAndCast", - args: [USDC, amount0.toString(), 1, encodeFlashcastData(flashSpells), "0x"] + args: [ETH, amount0.toString(), 1, encodeFlashcastData(flashSpells), "0x"] } ]; const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); From a560efd0c1e10a9ab2ec88952e4945223c339bf7 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Wed, 6 Apr 2022 21:25:49 +0530 Subject: [PATCH 21/25] added test for optimism, support for optimism tests --- scripts/tests/buildDSAv2.ts | 2 + scripts/tests/deployAndEnableConnector.ts | 2 + scripts/tests/getMasterSigner.ts | 2 + scripts/tests/optimism/addresses.ts | 10 + scripts/tests/optimism/tokens.ts | 94 +++++++ scripts/tests/run-tests.ts | 2 +- test/avalanche/aave/aaveV3-import-test.ts | 8 +- test/optimism/aave/aaveV3-import-test.ts | 314 ++++++++++++++++++++++ 8 files changed, 429 insertions(+), 5 deletions(-) create mode 100644 scripts/tests/optimism/addresses.ts create mode 100644 scripts/tests/optimism/tokens.ts create mode 100644 test/optimism/aave/aaveV3-import-test.ts diff --git a/scripts/tests/buildDSAv2.ts b/scripts/tests/buildDSAv2.ts index 163db741..e54dd68d 100644 --- a/scripts/tests/buildDSAv2.ts +++ b/scripts/tests/buildDSAv2.ts @@ -3,6 +3,7 @@ import { ethers } from "hardhat"; import { addresses as addressesPolygon } from "./polygon/addresses"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; import { addresses } from "./mainnet/addresses"; import { abis } from "../constant/abis"; import { abi } from "../../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json"; @@ -11,6 +12,7 @@ function getAddress(network: string | undefined) { if (network === "polygon") return addressesPolygon.core.instaIndex; else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; else if (network === "avalanche") return addressesAvalanche.core.instaIndex; + else if (network === "optimism") return addressesOptimism.core.instaIndex; else return addresses.core.instaIndex; } diff --git a/scripts/tests/deployAndEnableConnector.ts b/scripts/tests/deployAndEnableConnector.ts index e08142e4..fe3d71dd 100644 --- a/scripts/tests/deployAndEnableConnector.ts +++ b/scripts/tests/deployAndEnableConnector.ts @@ -3,6 +3,7 @@ import { addresses } from "./mainnet/addresses"; import { abis } from "../constant/abis"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; import hre from "hardhat"; import type { Signer, Contract } from "ethers"; @@ -22,6 +23,7 @@ function getAddress(network: string | undefined) { if (network === "polygon") return addressesPolygon; else if (network === "arbitrum") return addressesArbitrum; else if (network === "avalanche") return addressesAvalanche; + else if (network === "optimism") return addressesOptimism; else return addresses; } diff --git a/scripts/tests/getMasterSigner.ts b/scripts/tests/getMasterSigner.ts index c09fec74..040e4e4f 100644 --- a/scripts/tests/getMasterSigner.ts +++ b/scripts/tests/getMasterSigner.ts @@ -3,12 +3,14 @@ import { addresses } from "./mainnet/addresses"; import { addresses as addressesPolygon } from "./polygon/addresses"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; import { abis } from "../constant/abis"; function getAddress(network: string | undefined) { if (network === "polygon") return addressesPolygon.core.instaIndex; else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; else if (network === "avalanche") return addressesAvalanche.core.instaIndex; + else if (network === "optimism") return addressesOptimism.core.instaIndex; else return addresses.core.instaIndex; } diff --git a/scripts/tests/optimism/addresses.ts b/scripts/tests/optimism/addresses.ts new file mode 100644 index 00000000..c41acccc --- /dev/null +++ b/scripts/tests/optimism/addresses.ts @@ -0,0 +1,10 @@ +export const addresses: Record = { + connectors: { + basic: "", + auth: "", + }, + core: { + connectorsV2: "0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14", + instaIndex: "0x6CE3e607C808b4f4C26B7F6aDAeB619e49CAbb25", + }, +}; \ No newline at end of file diff --git a/scripts/tests/optimism/tokens.ts b/scripts/tests/optimism/tokens.ts new file mode 100644 index 00000000..83b66b68 --- /dev/null +++ b/scripts/tests/optimism/tokens.ts @@ -0,0 +1,94 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + matic: { + type: "token", + symbol: "MATIC", + name: "Matic", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18, + }, + eth: { + type: "token", + symbol: "ETH", + name: "Ethereum", + address: "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + decimals: 18, + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + decimals: 18, + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + decimals: 6, + }, +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0x6e7a5fafcec6bb1e78bae2a1f0b612012bf14827", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool);", + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + }, + }, + dai: { + impersonateSigner: "0x4a35582a710e1f4b2030a3f826da20bfb6703c09", + address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + }, + }, + usdt: { + impersonateSigner: "0x0d0707963952f2fba59dd06f2b425ace40b492fe", + address: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f", + abi: [ + "function issue(uint amount)", + "function transfer(address to, uint value)", + ], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.issue(amt)); + await mineTx(contract.transfer(address, amt)); + }, + }, + wbtc: { + impersonateSigner: "0xdc9232e2df177d7a12fdff6ecbab114e2231198d", + address: "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", + abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.mint(address, amt)); + }, + }, + inst: { + impersonateSigner: "0xf1f22f25f748f79263d44735198e023b72806ab1", + address: "0xf50d05a1402d0adafa880d36050736f9f6ee7dee", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(address, amt)); + }, + }, +}; diff --git a/scripts/tests/run-tests.ts b/scripts/tests/run-tests.ts index 9a3c3927..5b2f22bd 100644 --- a/scripts/tests/run-tests.ts +++ b/scripts/tests/run-tests.ts @@ -12,7 +12,7 @@ async function testRunner() { name: "chain", message: "What chain do you want to run tests on?", type: "list", - choices: ["mainnet", "polygon", "avalanche", "arbitrum"], + choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism"], }, ]); const testsPath = join(__dirname, "../../test", chain); diff --git a/test/avalanche/aave/aaveV3-import-test.ts b/test/avalanche/aave/aaveV3-import-test.ts index cb2cd71c..563ccec0 100644 --- a/test/avalanche/aave/aaveV3-import-test.ts +++ b/test/avalanche/aave/aaveV3-import-test.ts @@ -239,7 +239,7 @@ describe("Import Aave v3 Position for Avalanche", function () { }); describe("check user AAVE position", async () => { - it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + it("Should create Aave v3 position of DAI(collateral) and ETH(debt)", async () => { // approve DAI to aavePool await token.connect(wallet0).approve(aaveAddress, parseEther("8")); @@ -247,9 +247,9 @@ describe("Import Aave v3 Position for Avalanche", function () { await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); console.log("Supplied DAI on aave"); - //borrow USDC from aave + //borrow ETH from aave await aave.connect(wallet0).borrow(ETH, parseUnits("3", 6), 2, 3228, wallet.address); - console.log("Borrowed USDC from aave"); + console.log("Borrowed ETH from aave"); }); it("Should check position of user", async () => { @@ -350,4 +350,4 @@ describe("Import Aave v3 Position for Avalanche", function () { ); }); }); -}); +}); \ No newline at end of file diff --git a/test/optimism/aave/aaveV3-import-test.ts b/test/optimism/aave/aaveV3-import-test.ts new file mode 100644 index 00000000..1305f4ca --- /dev/null +++ b/test/optimism/aave/aaveV3-import-test.ts @@ -0,0 +1,314 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitOptimism__factory, IERC20__factory } from "../../../typechain"; +import { Hex } from "web3/utils"; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +let account = "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; +const flashAddress = "0x810D6b2425Dc5523525D1F45CC548ae9a085F5Ea"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const flashAbi = [ + { + inputs: [ + { internalType: "address[]", name: "_tokens", type: "address[]" }, + { internalType: "uint256[]", name: "_amounts", type: "uint256[]" } + ], + name: "getBestRoutes", + outputs: [ + { internalType: "uint16[]", name: "", type: "uint16[]" }, + { internalType: "uint256", name: "", type: "uint256" }, + { internalType: "bytes[]", name: "", type: "bytes[]" } + ], + stateMutability: "view", + type: "function" + } +]; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "interestRateMode", type: "uint256" }, + { internalType: "uint16", name: "referralCode", type: "uint16" }, + { internalType: "address", name: "onBehalfOf", type: "address" } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint16", name: "referralCode", type: "uint16" } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); +const flashLoan = new ethers.Contract(flashAddress, flashAbi); + +describe("Import Aave v3 Position for Optimism", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 5405099 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("1", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("8") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("8")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + let flashData = await flashLoan.connect(wallet0).getBestRoutes([USDC], [amount0.toFixed(0)]); + const fees = flashData[1].toNumber(); + const amountB = new BigNumber(amount0.toString()).multipliedBy(fees).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + const data = flashData[2][0]; + console.log(data.toString()); + console.log(amountWithFee.toFixed(0)); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toString(), 8, encodeFlashcastData(flashSpells), data.toString()] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + }); + }); +}); From 893ec0b0d7db09f498df0c24233f2388a29a4954 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Wed, 6 Apr 2022 22:19:40 +0530 Subject: [PATCH 22/25] updated addresses for optimism-test-script --- scripts/tests/optimism/addresses.ts | 4 +-- scripts/tests/optimism/tokens.ts | 41 ++++++++++++----------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/scripts/tests/optimism/addresses.ts b/scripts/tests/optimism/addresses.ts index c41acccc..ecd46a57 100644 --- a/scripts/tests/optimism/addresses.ts +++ b/scripts/tests/optimism/addresses.ts @@ -1,7 +1,7 @@ export const addresses: Record = { connectors: { - basic: "", - auth: "", + basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6", + auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9", }, core: { connectorsV2: "0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14", diff --git a/scripts/tests/optimism/tokens.ts b/scripts/tests/optimism/tokens.ts index 83b66b68..2ec0a48d 100644 --- a/scripts/tests/optimism/tokens.ts +++ b/scripts/tests/optimism/tokens.ts @@ -7,18 +7,11 @@ const mineTx = async (tx: any) => { }; export const tokens = { - matic: { - type: "token", - symbol: "MATIC", - name: "Matic", - address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - decimals: 18, - }, eth: { type: "token", symbol: "ETH", - name: "Ethereum", - address: "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + name: "Eth", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", decimals: 18, }, dai: { @@ -39,7 +32,7 @@ export const tokens = { export const tokenMapping: Record = { usdc: { - impersonateSigner: "0x6e7a5fafcec6bb1e78bae2a1f0b612012bf14827", + impersonateSigner: "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09", address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", abi: [ "function mint(address _to, uint256 _amount) external returns (bool);", @@ -51,8 +44,8 @@ export const tokenMapping: Record = { }, }, dai: { - impersonateSigner: "0x4a35582a710e1f4b2030a3f826da20bfb6703c09", - address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + impersonateSigner: "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09", + address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", abi: ["function transfer(address to, uint value)"], process: async function (owner: Signer | Provider, to: any, amt: any) { const contract = new ethers.Contract(this.address, this.abi, owner); @@ -60,8 +53,8 @@ export const tokenMapping: Record = { }, }, usdt: { - impersonateSigner: "0x0d0707963952f2fba59dd06f2b425ace40b492fe", - address: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f", + impersonateSigner: "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09", + address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", abi: [ "function issue(uint amount)", "function transfer(address to, uint value)", @@ -74,21 +67,21 @@ export const tokenMapping: Record = { }, }, wbtc: { - impersonateSigner: "0xdc9232e2df177d7a12fdff6ecbab114e2231198d", - address: "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", + impersonateSigner: "0x3aa76aa74bdfa09d68d9ebeb462c5f40d727283f", + address: "0x68f180fcCe6836688e9084f035309E29Bf0A2095", abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], process: async function (owner: Signer | Provider, address: any, amt: any) { const contract = new ethers.Contract(this.address, this.abi, owner); await mineTx(contract.mint(address, amt)); }, }, - inst: { - impersonateSigner: "0xf1f22f25f748f79263d44735198e023b72806ab1", - address: "0xf50d05a1402d0adafa880d36050736f9f6ee7dee", - abi: ["function transfer(address to, uint value)"], - process: async function (owner: Signer | Provider, address: any, amt: any) { - const contract = new ethers.Contract(this.address, this.abi, owner); - await mineTx(contract.transfer(address, amt)); - }, + // inst: { + // impersonateSigner: "0xf1f22f25f748f79263d44735198e023b72806ab1", + // address: "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb", + // abi: ["function transfer(address to, uint value)"], + // process: async function (owner: Signer | Provider, address: any, amt: any) { + // const contract = new ethers.Contract(this.address, this.abi, owner); + // await mineTx(contract.transfer(address, amt)); + // }, }, }; From 94e90252c848028d2b4cf4bc8ac5b67f8a3f6210 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Wed, 6 Apr 2022 22:21:36 +0530 Subject: [PATCH 23/25] minor correction --- scripts/tests/optimism/tokens.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/optimism/tokens.ts b/scripts/tests/optimism/tokens.ts index 2ec0a48d..d12cd92c 100644 --- a/scripts/tests/optimism/tokens.ts +++ b/scripts/tests/optimism/tokens.ts @@ -83,5 +83,5 @@ export const tokenMapping: Record = { // const contract = new ethers.Contract(this.address, this.abi, owner); // await mineTx(contract.transfer(address, amt)); // }, - }, + // }, }; From b74492c4bee8f785201b8f46d6e9a6ada7c7be91 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Thu, 7 Apr 2022 15:22:05 +0530 Subject: [PATCH 24/25] test support for optimism --- scripts/tests/addLiquidity.ts | 4 +++- scripts/tests/global-test.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/tests/addLiquidity.ts b/scripts/tests/addLiquidity.ts index a1250bcc..fd969222 100644 --- a/scripts/tests/addLiquidity.ts +++ b/scripts/tests/addLiquidity.ts @@ -4,6 +4,7 @@ import { impersonateAccounts } from "./impersonate"; import { tokenMapping as mainnetMapping } from "./mainnet/tokens"; import { tokenMapping as polygonMapping } from "./polygon/tokens"; import { tokenMapping as avalancheMapping } from "./avalanche/tokens"; +import { tokenMapping as optimismMapping } from "./optimism/tokens"; const mineTx = async (tx: any) => { await (await tx).wait(); @@ -12,7 +13,8 @@ const mineTx = async (tx: any) => { const tokenMapping: Record> = { mainnet: mainnetMapping, polygon: polygonMapping, - avalanche: avalancheMapping + avalanche: avalancheMapping, + optimism: optimismMapping }; export async function addLiquidity(tokenName: string, address: any, amt: any) { diff --git a/scripts/tests/global-test.ts b/scripts/tests/global-test.ts index 444abe8e..4c137c33 100644 --- a/scripts/tests/global-test.ts +++ b/scripts/tests/global-test.ts @@ -6,7 +6,7 @@ import { execScript } from "./command"; let start: number, end: number; async function testRunner() { - const chain = ["avalanche", "mainnet", "polygon", "arbitrum"]; + const chain = ["avalanche", "mainnet", "polygon", "arbitrum", "optimism"]; start = Date.now(); for (let ch of chain) { From cd3f8f26e78d9039130bf5d7484667415fd5ecdc Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Mon, 11 Apr 2022 01:30:25 +0530 Subject: [PATCH 25/25] removed aave v3 permit connector for optimism, fantom --- .../aave/v3-import-permit/events.sol | 15 - .../aave/v3-import-permit/helpers.sol | 316 ------------------ .../aave/v3-import-permit/interface.sol | 105 ------ .../connectors/aave/v3-import-permit/main.sol | 133 -------- .../aave/v3-import-permit/events.sol | 15 - .../aave/v3-import-permit/helpers.sol | 316 ------------------ .../aave/v3-import-permit/interface.sol | 105 ------ .../connectors/aave/v3-import-permit/main.sol | 133 -------- test/optimism/aave/aaveV3-import-test.ts | 314 ----------------- 9 files changed, 1452 deletions(-) delete mode 100644 contracts/fantom/connectors/aave/v3-import-permit/events.sol delete mode 100644 contracts/fantom/connectors/aave/v3-import-permit/helpers.sol delete mode 100644 contracts/fantom/connectors/aave/v3-import-permit/interface.sol delete mode 100644 contracts/fantom/connectors/aave/v3-import-permit/main.sol delete mode 100644 contracts/optimism/connectors/aave/v3-import-permit/events.sol delete mode 100644 contracts/optimism/connectors/aave/v3-import-permit/helpers.sol delete mode 100644 contracts/optimism/connectors/aave/v3-import-permit/interface.sol delete mode 100644 contracts/optimism/connectors/aave/v3-import-permit/main.sol delete mode 100644 test/optimism/aave/aaveV3-import-test.ts diff --git a/contracts/fantom/connectors/aave/v3-import-permit/events.sol b/contracts/fantom/connectors/aave/v3-import-permit/events.sol deleted file mode 100644 index 10a93d70..00000000 --- a/contracts/fantom/connectors/aave/v3-import-permit/events.sol +++ /dev/null @@ -1,15 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; -pragma experimental ABIEncoderV2; - -contract Events { - event LogAaveV3ImportWithPermit( - address indexed user, - address[] atokens, - string[] supplyIds, - string[] borrowIds, - uint256[] flashLoanFees, - uint256[] supplyAmts, - uint256[] borrowAmts - ); -} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol b/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol deleted file mode 100644 index 7e0b3a8a..00000000 --- a/contracts/fantom/connectors/aave/v3-import-permit/helpers.sol +++ /dev/null @@ -1,316 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; - -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; -import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; -import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; -import "./events.sol"; -import "./interface.sol"; - -abstract contract Helper is DSMath, Basic { - /** - * @dev Aave referal code - */ - uint16 internal constant referalCode = 3228; - - /** - * @dev Aave Lending Pool Provider - */ - AavePoolProviderInterface internal constant aaveProvider = - AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); - - /** - * @dev Aave Protocol Data Provider - */ - AaveDataProviderInterface internal constant aaveData = - AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); - - function getIsColl(address token, address user) - internal - view - returns (bool isCol) - { - (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); - } - - struct ImportData { - address[] _supplyTokens; - address[] _borrowTokens; - ATokenInterface[] aTokens; - uint256[] supplyAmts; - uint256[] variableBorrowAmts; - uint256[] variableBorrowAmtsWithFee; - uint256[] stableBorrowAmts; - uint256[] stableBorrowAmtsWithFee; - uint256[] totalBorrowAmts; - uint256[] totalBorrowAmtsWithFee; - bool convertStable; - } - - struct ImportInputData { - address[] supplyTokens; - address[] borrowTokens; - bool convertStable; - uint256[] flashLoanFees; - } - - struct SignedPermits { - uint8[] v; - bytes32[] r; - bytes32[] s; - uint256[] expiry; - } -} - -contract AaveHelpers is Helper { - function getBorrowAmount(address _token, address userAccount) - internal - view - returns (uint256 stableBorrow, uint256 variableBorrow) - { - ( - , - address stableDebtTokenAddress, - address variableDebtTokenAddress - ) = aaveData.getReserveTokensAddresses(_token); - - stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( - userAccount - ); - variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( - userAccount - ); - } - - function getBorrowAmounts( - address userAccount, - AaveInterface aave, - ImportInputData memory inputData, - ImportData memory data - ) internal returns (ImportData memory) { - if (inputData.borrowTokens.length > 0) { - data._borrowTokens = new address[](inputData.borrowTokens.length); - data.variableBorrowAmts = new uint256[]( - inputData.borrowTokens.length - ); - data.variableBorrowAmtsWithFee = new uint256[]( - inputData.borrowTokens.length - ); - data.stableBorrowAmts = new uint256[]( - inputData.borrowTokens.length - ); - data.stableBorrowAmtsWithFee = new uint256[]( - inputData.borrowTokens.length - ); - data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); - data.totalBorrowAmtsWithFee = 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] == ftmAddr - ? wftmAddr - : inputData.borrowTokens[i]; - data._borrowTokens[i] = _token; - - ( - data.stableBorrowAmts[i], - data.variableBorrowAmts[i] - ) = getBorrowAmount(_token, userAccount); - - if (data.variableBorrowAmts[i] != 0) { - data.variableBorrowAmtsWithFee[i] = add( - data.variableBorrowAmts[i], - inputData.flashLoanFees[i] - ); - data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; - } else { - data.stableBorrowAmtsWithFee[i] = add( - data.stableBorrowAmts[i], - inputData.flashLoanFees[i] - ); - } - - data.totalBorrowAmts[i] = add( - data.stableBorrowAmts[i], - data.variableBorrowAmts[i] - ); - data.totalBorrowAmtsWithFee[i] = add( - data.stableBorrowAmtsWithFee[i], - data.variableBorrowAmtsWithFee[i] - ); - - if (data.totalBorrowAmts[i] > 0) { - uint256 _amt = data.totalBorrowAmts[i]; - TokenInterface(_token).approve(address(aave), _amt); - } - } - } - return data; - } - - 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.aTokens = new ATokenInterface[](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] == ftmAddr - ? wftmAddr - : inputData.supplyTokens[i]; - (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); - data._supplyTokens[i] = _token; - data.aTokens[i] = ATokenInterface(_aToken); - data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); - } - - return data; - } - - function _paybackBehalfOne( - AaveInterface aave, - address token, - uint256 amt, - uint256 rateMode, - address user - ) private { - aave.repay(token, amt, rateMode, user); - } - - function _PaybackStable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts, - address user - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); - } - } - } - - function _PaybackVariable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts, - address user - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); - } - } - } - - function _PermitATokens( - address userAccount, - ATokenInterface[] memory aTokenContracts, - address[] memory tokens, - uint8[] memory v, - bytes32[] memory r, - bytes32[] memory s, - uint256[] memory expiry - ) internal { - for (uint256 i = 0; i < tokens.length; i++) { - aTokenContracts[i].permit( - userAccount, - address(this), - uint256(-1), - expiry[i], - v[i], - r[i], - s[i] - ); - } - } - - function _TransferAtokens( - uint256 _length, - AaveInterface aave, - ATokenInterface[] memory atokenContracts, - uint256[] memory amts, - address[] memory tokens, - address userAccount - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - uint256 _amt = amts[i]; - require( - atokenContracts[i].transferFrom( - userAccount, - address(this), - _amt - ), - "allowance?" - ); - - if (!getIsColl(tokens[i], address(this))) { - aave.setUserUseReserveAsCollateral(tokens[i], true); - } - } - } - } - - function _BorrowVariable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _borrowOne(aave, tokens[i], amts[i], 2); - } - } - } - - function _BorrowStable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _borrowOne(aave, tokens[i], amts[i], 1); - } - } - } - - function _borrowOne( - AaveInterface aave, - address token, - uint256 amt, - uint256 rateMode - ) private { - aave.borrow(token, amt, rateMode, referalCode, address(this)); - } -} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/interface.sol b/contracts/fantom/connectors/aave/v3-import-permit/interface.sol deleted file mode 100644 index 32140247..00000000 --- a/contracts/fantom/connectors/aave/v3-import-permit/interface.sol +++ /dev/null @@ -1,105 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; - -interface AaveInterface { - function supply( - 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; -} - -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 permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - function transferFrom( - address, - address, - uint256 - ) external returns (bool); - - function allowance(address, address) external returns (uint256); -} - -interface AavePoolProviderInterface { - function getPool() 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 - ); -} - -interface AaveAddressProviderRegistryInterface { - function getAddressesProvidersList() - external - view - returns (address[] memory); -} diff --git a/contracts/fantom/connectors/aave/v3-import-permit/main.sol b/contracts/fantom/connectors/aave/v3-import-permit/main.sol deleted file mode 100644 index 655d5a54..00000000 --- a/contracts/fantom/connectors/aave/v3-import-permit/main.sol +++ /dev/null @@ -1,133 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; -pragma experimental ABIEncoderV2; -/** - * @title Aave v3 import connector . - * @dev Import EOA's aave V3 position to DSA's aave v3 position - */ - -import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; -import { AaveInterface, ATokenInterface } from "./interface.sol"; -import "./helpers.sol"; -import "./events.sol"; - -contract AaveV3ImportPermitResolver is AaveHelpers { - function _importAave( - address userAccount, - ImportInputData memory inputData, - SignedPermits memory permitData - ) internal returns (string memory _eventName, bytes memory _eventParam) { - require( - AccountInterface(address(this)).isAuth(userAccount), - "user-account-not-auth" - ); - - require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); - - ImportData memory data; - - AaveInterface aave = AaveInterface(aaveProvider.getPool()); - - data = getBorrowAmounts(userAccount, aave, inputData, data); - data = getSupplyAmounts(userAccount, inputData, data); - - // payback borrowed amount; - _PaybackStable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.stableBorrowAmts, - userAccount - ); - _PaybackVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.variableBorrowAmts, - userAccount - ); - - //permit this address to transfer aTokens - _PermitATokens( - userAccount, - data.aTokens, - data._supplyTokens, - permitData.v, - permitData.r, - permitData.s, - permitData.expiry - ); - - // transfer atokens to this address; - _TransferAtokens( - data._supplyTokens.length, - aave, - data.aTokens, - data.supplyAmts, - data._supplyTokens, - userAccount - ); - - // borrow assets after migrating position - if (data.convertStable) { - _BorrowVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.totalBorrowAmtsWithFee - ); - } else { - _BorrowStable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.stableBorrowAmtsWithFee - ); - _BorrowVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.variableBorrowAmtsWithFee - ); - } - - _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; - _eventParam = abi.encode( - userAccount, - inputData.convertStable, - inputData.supplyTokens, - inputData.borrowTokens, - inputData.flashLoanFees, - data.supplyAmts, - data.stableBorrowAmts, - data.variableBorrowAmts - ); - } - - /** - * @dev Import aave V3 position . - * @notice Import EOA's aave V3 position to DSA's aave v3 position - * @param userAccount The address of the EOA from which aave position will be imported - * @param inputData The struct containing all the neccessary input data - * @param permitData The struct containing signed permit data like v,r,s,expiry - */ - function importAave( - address userAccount, - ImportInputData memory inputData, - SignedPermits memory permitData - ) - external - payable - returns (string memory _eventName, bytes memory _eventParam) - { - (_eventName, _eventParam) = _importAave( - userAccount, - inputData, - permitData - ); - } -} - -contract ConnectV2AaveV3ImportPermitFantom is AaveV3ImportPermitResolver { - string public constant name = "Aave-v3-import-v1"; -} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/events.sol b/contracts/optimism/connectors/aave/v3-import-permit/events.sol deleted file mode 100644 index 10a93d70..00000000 --- a/contracts/optimism/connectors/aave/v3-import-permit/events.sol +++ /dev/null @@ -1,15 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; -pragma experimental ABIEncoderV2; - -contract Events { - event LogAaveV3ImportWithPermit( - address indexed user, - address[] atokens, - string[] supplyIds, - string[] borrowIds, - uint256[] flashLoanFees, - uint256[] supplyAmts, - uint256[] borrowAmts - ); -} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol b/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol deleted file mode 100644 index 1e8fbfa0..00000000 --- a/contracts/optimism/connectors/aave/v3-import-permit/helpers.sol +++ /dev/null @@ -1,316 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; - -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; -import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; -import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; -import "./events.sol"; -import "./interface.sol"; - -abstract contract Helper is DSMath, Basic { - /** - * @dev Aave referal code - */ - uint16 internal constant referalCode = 3228; - - /** - * @dev Aave Lending Pool Provider - */ - AavePoolProviderInterface internal constant aaveProvider = - AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); - - /** - * @dev Aave Protocol Data Provider - */ - AaveDataProviderInterface internal constant aaveData = - AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); - - function getIsColl(address token, address user) - internal - view - returns (bool isCol) - { - (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); - } - - struct ImportData { - address[] _supplyTokens; - address[] _borrowTokens; - ATokenInterface[] aTokens; - uint256[] supplyAmts; - uint256[] variableBorrowAmts; - uint256[] variableBorrowAmtsWithFee; - uint256[] stableBorrowAmts; - uint256[] stableBorrowAmtsWithFee; - uint256[] totalBorrowAmts; - uint256[] totalBorrowAmtsWithFee; - bool convertStable; - } - - struct ImportInputData { - address[] supplyTokens; - address[] borrowTokens; - bool convertStable; - uint256[] flashLoanFees; - } - - struct SignedPermits { - uint8[] v; - bytes32[] r; - bytes32[] s; - uint256[] expiry; - } -} - -contract AaveHelpers is Helper { - function getBorrowAmount(address _token, address userAccount) - internal - view - returns (uint256 stableBorrow, uint256 variableBorrow) - { - ( - , - address stableDebtTokenAddress, - address variableDebtTokenAddress - ) = aaveData.getReserveTokensAddresses(_token); - - stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( - userAccount - ); - variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( - userAccount - ); - } - - function getBorrowAmounts( - address userAccount, - AaveInterface aave, - ImportInputData memory inputData, - ImportData memory data - ) internal returns (ImportData memory) { - if (inputData.borrowTokens.length > 0) { - data._borrowTokens = new address[](inputData.borrowTokens.length); - data.variableBorrowAmts = new uint256[]( - inputData.borrowTokens.length - ); - data.variableBorrowAmtsWithFee = new uint256[]( - inputData.borrowTokens.length - ); - data.stableBorrowAmts = new uint256[]( - inputData.borrowTokens.length - ); - data.stableBorrowAmtsWithFee = new uint256[]( - inputData.borrowTokens.length - ); - data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); - data.totalBorrowAmtsWithFee = 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.supplyTokens[i] == ethAddr - ? wethAddr - : inputData.supplyTokens[i]; - data._borrowTokens[i] = _token; - - ( - data.stableBorrowAmts[i], - data.variableBorrowAmts[i] - ) = getBorrowAmount(_token, userAccount); - - if (data.variableBorrowAmts[i] != 0) { - data.variableBorrowAmtsWithFee[i] = add( - data.variableBorrowAmts[i], - inputData.flashLoanFees[i] - ); - data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; - } else { - data.stableBorrowAmtsWithFee[i] = add( - data.stableBorrowAmts[i], - inputData.flashLoanFees[i] - ); - } - - data.totalBorrowAmts[i] = add( - data.stableBorrowAmts[i], - data.variableBorrowAmts[i] - ); - data.totalBorrowAmtsWithFee[i] = add( - data.stableBorrowAmtsWithFee[i], - data.variableBorrowAmtsWithFee[i] - ); - - if (data.totalBorrowAmts[i] > 0) { - uint256 _amt = data.totalBorrowAmts[i]; - TokenInterface(_token).approve(address(aave), _amt); - } - } - } - return data; - } - - 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.aTokens = new ATokenInterface[](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]; - (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); - data._supplyTokens[i] = _token; - data.aTokens[i] = ATokenInterface(_aToken); - data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); - } - - return data; - } - - function _paybackBehalfOne( - AaveInterface aave, - address token, - uint256 amt, - uint256 rateMode, - address user - ) private { - aave.repay(token, amt, rateMode, user); - } - - function _PaybackStable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts, - address user - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); - } - } - } - - function _PaybackVariable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts, - address user - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); - } - } - } - - function _PermitATokens( - address userAccount, - ATokenInterface[] memory aTokenContracts, - address[] memory tokens, - uint8[] memory v, - bytes32[] memory r, - bytes32[] memory s, - uint256[] memory expiry - ) internal { - for (uint256 i = 0; i < tokens.length; i++) { - aTokenContracts[i].permit( - userAccount, - address(this), - uint256(-1), - expiry[i], - v[i], - r[i], - s[i] - ); - } - } - - function _TransferAtokens( - uint256 _length, - AaveInterface aave, - ATokenInterface[] memory atokenContracts, - uint256[] memory amts, - address[] memory tokens, - address userAccount - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - uint256 _amt = amts[i]; - require( - atokenContracts[i].transferFrom( - userAccount, - address(this), - _amt - ), - "allowance?" - ); - - if (!getIsColl(tokens[i], address(this))) { - aave.setUserUseReserveAsCollateral(tokens[i], true); - } - } - } - } - - function _BorrowVariable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _borrowOne(aave, tokens[i], amts[i], 2); - } - } - } - - function _BorrowStable( - uint256 _length, - AaveInterface aave, - address[] memory tokens, - uint256[] memory amts - ) internal { - for (uint256 i = 0; i < _length; i++) { - if (amts[i] > 0) { - _borrowOne(aave, tokens[i], amts[i], 1); - } - } - } - - function _borrowOne( - AaveInterface aave, - address token, - uint256 amt, - uint256 rateMode - ) private { - aave.borrow(token, amt, rateMode, referalCode, address(this)); - } -} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/interface.sol b/contracts/optimism/connectors/aave/v3-import-permit/interface.sol deleted file mode 100644 index 32140247..00000000 --- a/contracts/optimism/connectors/aave/v3-import-permit/interface.sol +++ /dev/null @@ -1,105 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; - -interface AaveInterface { - function supply( - 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 swapBorrowRateMode(address _asset, uint256 _rateMode) external; -} - -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 permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - function transferFrom( - address, - address, - uint256 - ) external returns (bool); - - function allowance(address, address) external returns (uint256); -} - -interface AavePoolProviderInterface { - function getPool() 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 - ); -} - -interface AaveAddressProviderRegistryInterface { - function getAddressesProvidersList() - external - view - returns (address[] memory); -} diff --git a/contracts/optimism/connectors/aave/v3-import-permit/main.sol b/contracts/optimism/connectors/aave/v3-import-permit/main.sol deleted file mode 100644 index 17fd9a6c..00000000 --- a/contracts/optimism/connectors/aave/v3-import-permit/main.sol +++ /dev/null @@ -1,133 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; -pragma experimental ABIEncoderV2; -/** - * @title Aave v3 import connector. - * @dev Import EOA's aave V3 position to DSA's aave v3 position - */ - -import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; -import { AaveInterface, ATokenInterface } from "./interface.sol"; -import "./helpers.sol"; -import "./events.sol"; - -contract AaveV3ImportPermitResolver is AaveHelpers { - function _importAave( - address userAccount, - ImportInputData memory inputData, - SignedPermits memory permitData - ) internal returns (string memory _eventName, bytes memory _eventParam) { - require( - AccountInterface(address(this)).isAuth(userAccount), - "user-account-not-auth" - ); - - require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); - - ImportData memory data; - - AaveInterface aave = AaveInterface(aaveProvider.getPool()); - - data = getBorrowAmounts(userAccount, aave, inputData, data); - data = getSupplyAmounts(userAccount, inputData, data); - - // payback borrowed amount; - _PaybackStable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.stableBorrowAmts, - userAccount - ); - _PaybackVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.variableBorrowAmts, - userAccount - ); - - //permit this address to transfer aTokens - _PermitATokens( - userAccount, - data.aTokens, - data._supplyTokens, - permitData.v, - permitData.r, - permitData.s, - permitData.expiry - ); - - // transfer atokens to this address; - _TransferAtokens( - data._supplyTokens.length, - aave, - data.aTokens, - data.supplyAmts, - data._supplyTokens, - userAccount - ); - - // borrow assets after migrating position - if (data.convertStable) { - _BorrowVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.totalBorrowAmtsWithFee - ); - } else { - _BorrowStable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.stableBorrowAmtsWithFee - ); - _BorrowVariable( - data._borrowTokens.length, - aave, - data._borrowTokens, - data.variableBorrowAmtsWithFee - ); - } - - _eventName = "LogAaveV3ImportWithPermit(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; - _eventParam = abi.encode( - userAccount, - inputData.convertStable, - inputData.supplyTokens, - inputData.borrowTokens, - inputData.flashLoanFees, - data.supplyAmts, - data.stableBorrowAmts, - data.variableBorrowAmts - ); - } - - /** - * @dev Import aave V3 position . - * @notice Import EOA's aave V3 position to DSA's aave v3 position - * @param userAccount The address of the EOA from which aave position will be imported - * @param inputData The struct containing all the neccessary input data - * @param permitData The struct containing signed permit data like v,r,s,expiry - */ - function importAave( - address userAccount, - ImportInputData memory inputData, - SignedPermits memory permitData - ) - external - payable - returns (string memory _eventName, bytes memory _eventParam) - { - (_eventName, _eventParam) = _importAave( - userAccount, - inputData, - permitData - ); - } -} - -contract ConnectV2AaveV3ImportPermitOptimism is AaveV3ImportPermitResolver { - string public constant name = "Aave-v3-import-permit-v1"; -} diff --git a/test/optimism/aave/aaveV3-import-test.ts b/test/optimism/aave/aaveV3-import-test.ts deleted file mode 100644 index 1305f4ca..00000000 --- a/test/optimism/aave/aaveV3-import-test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { expect, should } from "chai"; -import hre, { ethers, waffle } from "hardhat"; -import type { Signer, Contract } from "ethers"; -import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; -import { keccak256 } from "@ethersproject/keccak256"; -import { defaultAbiCoder } from "@ethersproject/abi"; -import { BigNumber } from "bignumber.js"; -import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; -import { addresses } from "../../../scripts/tests/optimism/addresses"; -import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; -import { abis } from "../../../scripts/constant/abis"; -import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; -import { parseEther, parseUnits } from "ethers/lib/utils"; -import { encodeSpells } from "../../../scripts/tests/encodeSpells"; -import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; -import { ConnectV2AaveV3ImportPermitOptimism__factory, IERC20__factory } from "../../../typechain"; -import { Hex } from "web3/utils"; - -const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; -const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; -let account = "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09"; -const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; -const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; -const flashAddress = "0x810D6b2425Dc5523525D1F45CC548ae9a085F5Ea"; -const mnemonic = "test test test test test test test test test test test junk"; -const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; -let signer: any, wallet0: any; - -const ABI = [ - "function DOMAIN_SEPARATOR() public view returns (bytes32)", - "function balanceOf(address account) public view returns (uint256)", - "function nonces(address owner) public view returns (uint256)" -]; - -const flashAbi = [ - { - inputs: [ - { internalType: "address[]", name: "_tokens", type: "address[]" }, - { internalType: "uint256[]", name: "_amounts", type: "uint256[]" } - ], - name: "getBestRoutes", - outputs: [ - { internalType: "uint16[]", name: "", type: "uint16[]" }, - { internalType: "uint256", name: "", type: "uint256" }, - { internalType: "bytes[]", name: "", type: "bytes[]" } - ], - stateMutability: "view", - type: "function" - } -]; - -const aaveAbi = [ - { - inputs: [ - { internalType: "address", name: "asset", type: "address" }, - { internalType: "uint256", name: "amount", type: "uint256" }, - { internalType: "uint256", name: "interestRateMode", type: "uint256" }, - { internalType: "uint16", name: "referralCode", type: "uint16" }, - { internalType: "address", name: "onBehalfOf", type: "address" } - ], - name: "borrow", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { internalType: "address", name: "asset", type: "address" }, - { internalType: "uint256", name: "amount", type: "uint256" }, - { internalType: "address", name: "onBehalfOf", type: "address" }, - { internalType: "uint16", name: "referralCode", type: "uint16" } - ], - name: "supply", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } -]; - -const erc20Abi = [ - { - constant: false, - inputs: [ - { - name: "_spender", - type: "address" - }, - { - name: "_value", - type: "uint256" - } - ], - name: "approve", - outputs: [ - { - name: "", - type: "bool" - } - ], - payable: false, - stateMutability: "nonpayable", - type: "function" - }, - { - constant: true, - inputs: [ - { - name: "_owner", - type: "address" - } - ], - name: "balanceOf", - outputs: [ - { - name: "balance", - type: "uint256" - } - ], - payable: false, - stateMutability: "view", - type: "function" - }, - { - constant: false, - inputs: [ - { - name: "_to", - type: "address" - }, - { - name: "_value", - type: "uint256" - } - ], - name: "transfer", - outputs: [ - { - name: "", - type: "bool" - } - ], - payable: false, - stateMutability: "nonpayable", - type: "function" - } -]; - -const token = new ethers.Contract(DAI, erc20Abi); -const aDai = new ethers.Contract(aDaiAddress, ABI); -const usdcToken = new ethers.Contract(USDC, erc20Abi); -const aave = new ethers.Contract(aaveAddress, aaveAbi); -const flashLoan = new ethers.Contract(flashAddress, flashAbi); - -describe("Import Aave v3 Position for Optimism", function () { - let dsaWallet0: any; - let masterSigner: Signer; - let instaConnectorsV2: Contract; - let connector: any; - - const wallet = ethers.Wallet.fromMnemonic(mnemonic); - - before(async () => { - await hre.network.provider.request({ - method: "hardhat_reset", - params: [ - { - forking: { - //@ts-ignore - jsonRpcUrl: hre.config.networks.hardhat.forking.url, - blockNumber: 5405099 - } - } - ] - }); - masterSigner = await getMasterSigner(); - [wallet0] = await ethers.getSigners(); - await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); - - await hre.network.provider.request({ - method: "hardhat_impersonateAccount", - params: [account] - }); - - signer = await ethers.getSigner(account); - - await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); - - instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); - connector = await deployAndEnableConnector({ - connectorName, - contractArtifact: ConnectV2AaveV3ImportPermitOptimism__factory, - signer: masterSigner, - connectors: instaConnectorsV2 - }); - }); - - describe("check user AAVE position", async () => { - it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { - // approve DAI to aavePool - await token.connect(wallet0).approve(aaveAddress, parseEther("8")); - - //deposit DAI in aave - await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); - console.log("Supplied DAI on aave"); - - //borrow USDC from aave - await aave.connect(wallet0).borrow(USDC, parseUnits("1", 6), 2, 3228, wallet.address); - console.log("Borrowed USDC from aave"); - }); - - it("Should check position of user", async () => { - expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(8).multipliedBy(1e18).toString() - ); - - expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( - new BigNumber(1).multipliedBy(1e6).toString() - ); - }); - }); - - describe("Deployment", async () => { - it("Should set correct name", async () => { - expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); - }); - }); - - describe("DSA wallet setup", async () => { - it("Should build DSA v2", async () => { - dsaWallet0 = await buildDSAv2(wallet.address); - expect(!!dsaWallet0.address).to.be.true; - }); - - it("Deposit ETH into DSA wallet", async function () { - await wallet0.sendTransaction({ - to: dsaWallet0.address, - value: ethers.utils.parseEther("8") - }); - - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("8")); - }); - }); - - describe("Aave position migration", async () => { - it("Should migrate Aave position", async () => { - const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); - const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; - - let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); - //Approving max amount - const amount = ethers.constants.MaxUint256; - const expiry = Date.now() + 20 * 60; - - const digest = keccak256( - ethers.utils.solidityPack( - ["bytes1", "bytes1", "bytes32", "bytes32"], - [ - "0x19", - "0x01", - DOMAIN_SEPARATOR, - keccak256( - defaultAbiCoder.encode( - ["bytes32", "address", "address", "uint256", "uint256", "uint256"], - [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] - ) - ) - ] - ) - ); - const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); - const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); - let flashData = await flashLoan.connect(wallet0).getBestRoutes([USDC], [amount0.toFixed(0)]); - const fees = flashData[1].toNumber(); - const amountB = new BigNumber(amount0.toString()).multipliedBy(fees).dividedBy(1e4); - const amountWithFee = amount0.plus(amountB); - const data = flashData[2][0]; - console.log(data.toString()); - console.log(amountWithFee.toFixed(0)); - - const flashSpells = [ - { - connector: "AAVE-V3-IMPORT-PERMIT-X", - method: "importAave", - args: [ - wallet.address, - [[DAI], [USDC], false, [amountB.toFixed(0)]], - [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] - ] - }, - { - connector: "INSTAPOOL-C", - method: "flashPayback", - args: [USDC, amountWithFee.toFixed(0), 0, 0] - } - ]; - - const spells = [ - { - connector: "INSTAPOOL-C", - method: "flashBorrowAndCast", - args: [USDC, amount0.toString(), 8, encodeFlashcastData(flashSpells), data.toString()] - } - ]; - const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); - const receipt = await tx.wait(); - }); - - it("Should check DSA AAVE position", async () => { - expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( - new BigNumber(8).multipliedBy(1e18).toString() - ); - }); - }); -});