mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
aave-v3-import-permit-opt
This commit is contained in:
parent
e99ee70ead
commit
2b0e52ed82
|
@ -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
|
||||
);
|
||||
}
|
316
contracts/optimism/connectors/aave/v3-import-permit/helpers.sol
Normal file
316
contracts/optimism/connectors/aave/v3-import-permit/helpers.sol
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
//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;
|
||||
|
||||
function getUserAccountData(address user)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256 totalCollateralBase,
|
||||
uint256 totalDebtBase,
|
||||
uint256 availableBorrowsBase,
|
||||
uint256 currentLiquidationThreshold,
|
||||
uint256 ltv,
|
||||
uint256 healthFactor
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
133
contracts/optimism/connectors/aave/v3-import-permit/main.sol
Normal file
133
contracts/optimism/connectors/aave/v3-import-permit/main.sol
Normal file
|
@ -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";
|
||||
}
|
357
test/optimism/aave/aaveV3-import-test.ts
Normal file
357
test/optimism/aave/aaveV3-import-test.ts
Normal file
|
@ -0,0 +1,357 @@
|
|||
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 axios from "axios";
|
||||
|
||||
const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE";
|
||||
const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD";
|
||||
let account = "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09";
|
||||
const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1";
|
||||
const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607";
|
||||
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, wallet: 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 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"
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "user",
|
||||
type: "address"
|
||||
}
|
||||
],
|
||||
name: "getUserAccountData",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "totalCollateralBase",
|
||||
type: "uint256"
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "totalDebtBase",
|
||||
type: "uint256"
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "availableBorrowsBase",
|
||||
type: "uint256"
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "currentLiquidationThreshold",
|
||||
type: "uint256"
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "ltv",
|
||||
type: "uint256"
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "healthFactor",
|
||||
type: "uint256"
|
||||
}
|
||||
],
|
||||
stateMutability: "view",
|
||||
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 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("10"));
|
||||
|
||||
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("10"));
|
||||
|
||||
//deposit DAI in aave
|
||||
await aave.connect(wallet0).supply(DAI, parseEther("10"), wallet.address, 3228);
|
||||
console.log("\tSupplied DAI on aave");
|
||||
|
||||
//borrow USDC from aave
|
||||
await aave.connect(wallet0).borrow(USDC, parseUnits("1", 6), 1, 3228, wallet.address);
|
||||
console.log("\tBorrowed 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(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("10")
|
||||
});
|
||||
|
||||
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("User's borrows", async () => {
|
||||
it("Should check the totalDebtBase of user", async () => {
|
||||
console.log(
|
||||
`\ttotalDebtBase: ${(await aave.connect(wallet0).getUserAccountData(wallet.address)).totalDebtBase.toString()}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
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(
|
||||
new BigNumber((await aave.connect(wallet0).getUserAccountData(wallet.address)).totalDebtBase)
|
||||
);
|
||||
let params = {
|
||||
tokens: [USDC],
|
||||
amounts: [amount0.toFixed(0)]
|
||||
};
|
||||
const flashData = (
|
||||
await axios.get("https://api.instadapp.io/defi/optimism/flashloan/v2", {
|
||||
params: params
|
||||
})
|
||||
).data;
|
||||
|
||||
const fees = flashData.bestFee;
|
||||
const amountB = new BigNumber(amount0.toString()).multipliedBy(fees).dividedBy(1e4);
|
||||
const amountWithFee = amount0.plus(amountB);
|
||||
const data = flashData.bestData[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.toFixed(0), flashData.bestRoutes[0], 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(10).multipliedBy(1e18).toString()
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user