From 52405cfb8fd545ac9129ccb4395a18150cab0b02 Mon Sep 17 00:00:00 2001 From: Shivva Date: Mon, 26 Oct 2020 18:33:36 +0100 Subject: [PATCH] Test ETH-A to ETH-B Debt bridge case --- hardhat.config.js | 17 + pre-compiles/InstaMapping.json | 96 ++++++ .../2_Full-Debt-Bridge-Maker-Compound.test.js | 6 +- test/3_Full-Debt-Bridge-ETHA-ETHB.test.js | 271 ++++++++++++++++ ...Full-Refinance-External-Provider.helper.js | 304 +++++++++++++++--- 5 files changed, 649 insertions(+), 45 deletions(-) create mode 100644 pre-compiles/InstaMapping.json create mode 100644 test/3_Full-Debt-Bridge-ETHA-ETHB.test.js diff --git a/hardhat.config.js b/hardhat.config.js index f7e41dc..ff3b952 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -36,6 +36,7 @@ module.exports = { InstaList: "0x4c8a1BEb8a87765788946D6B19C6C6355194AbEb", InstaConnectors: "0xD6A602C01a023B98Ecfb29Df02FBA380d3B21E0c", InstaAccount: "0x939Daad09fC4A9B8f8A9352A485DAb2df4F4B3F8", + InstaMapping: "0xe81F70Cc7C0D46e12d70efc60607F16bbD617E88", ConnectAuth: "0xd1aFf9f2aCf800C876c409100D6F39AEa93Fc3D9", ConnectBasic: "0x6a31c5982C5Bc5533432913cf06a66b6D3333a95", ConnectGelato: "0x37A7009d424951dd5D5F155fA588D9a03C455163", @@ -165,3 +166,19 @@ task( process.exit(1); } }); + +task("hardhatReset", "Reset back to a fresh forked state during runtime") + .addPositionalParam("provider", "Network Provider", undefined, types.json) + .setAction(async (taskArgs) => { + await taskArgs.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, + blockNumber: 11104384, + }, + }, + ], + }); + }); diff --git a/pre-compiles/InstaMapping.json b/pre-compiles/InstaMapping.json new file mode 100644 index 0000000..d4b2a9d --- /dev/null +++ b/pre-compiles/InstaMapping.json @@ -0,0 +1,96 @@ +{ + "contractName": "ConnectAuth", + "abi": [ + {"inputs": [], "stateMutability": "nonpayable", "type": "constructor"}, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "cTokens", + "type": "address[]" + } + ], + "name": "LogAddCTokenMapping", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "gemJoin", + "type": "address[]" + } + ], + "name": "LogAddGemJoinMapping", + "type": "event" + }, + { + "inputs": [ + {"internalType": "address[]", "name": "cTkn", "type": "address[]"} + ], + "name": "addCtknMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + {"internalType": "address[]", "name": "gemJoins", "type": "address[]"} + ], + "name": "addGemJoinMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{"internalType": "address", "name": "", "type": "address"}], + "name": "cTokenMapping", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "connectors", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{"internalType": "bytes32", "name": "", "type": "bytes32"}], + "name": "gemJoinMapping", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "instaIndex", + "outputs": [{"internalType": "address", "name": "", "type": "address"}], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{"internalType": "string", "name": "", "type": "string"}], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/test/2_Full-Debt-Bridge-Maker-Compound.test.js b/test/2_Full-Debt-Bridge-Maker-Compound.test.js index da6e014..8e9010e 100644 --- a/test/2_Full-Debt-Bridge-Maker-Compound.test.js +++ b/test/2_Full-Debt-Bridge-Maker-Compound.test.js @@ -30,7 +30,7 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function () let taskReceipt; before(async function () { - const result = await helper.setup(); + const result = await helper.makerToCompoundTestSetup(); address = result.address; contracts = result.contracts; @@ -207,10 +207,10 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function () // 🚧 For Debugging: // const txResponse2 = await gelatoCore - // .connect(providerWallet) + // .connect(address.executorWallet) // .exec(taskReceipt, { // gasPrice: gelatoGasPrice, - // gasLimit: GAS_LIMIT, + // gasLimit: constants.GAS_LIMIT, // }); // const {blockHash} = await txResponse2.wait(); // const logs = await ethers.provider.getLogs({blockHash}); diff --git a/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js b/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js new file mode 100644 index 0000000..fe3c823 --- /dev/null +++ b/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js @@ -0,0 +1,271 @@ +const {expect} = require("chai"); +const hre = require("hardhat"); +const {ethers, network} = hre; +const GelatoCoreLib = require("@gelatonetwork/core"); + +const Helper = require("./helpers/Full-Refinance-External-Provider.helper"); +const helper = new Helper(); + +// This test showcases how to submit a task refinancing a Users debt position from +// Maker to Compound using Gelato +describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () { + this.timeout(0); + if (hre.network.name !== "hardhat") { + console.error("Test Suite is meant to be run on hardhat only"); + process.exit(1); + } + + let contracts; + let address; + let constants; + let ABI; + + // Payload Params for ConnectGelatoDebtBridgeFromMaker and ConditionMakerVaultUnsafe + let vaultAId; + + // For TaskSpec and for Task + let spells = []; + + // Cross test var + let taskReceipt; + + before(async function () { + // Reset back to a fresh forked state during runtime + await hre.run("hardhatReset", {provider: network.provider}); + const result = await helper.makerETHAToMakerETHBSetup(); + + address = result.address; + contracts = result.contracts; + vaultAId = result.vaultAId; + spells = result.spells; + + ABI = await helper.getABI(); + constants = await helper.getConstants(); + }); + + it("#1: DSA give Authorization to Gelato to execute action his behalf.", async function () { + //#region User give authorization to gelato to use his DSA on his behalf. + + // Instadapp DSA contract give the possibility to the user to delegate + // action by giving authorization. + // In this case user give authorization to gelato to execute + // task for him if needed. + + await contracts.dsa.cast( + [hre.network.config.ConnectAuth], + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectAuthABI, + functionname: "add", + inputs: [contracts.gelatoCore.address], + }), + ], + address.userAddress + ); + + expect(await contracts.dsa.isAuth(contracts.gelatoCore.address)).to.be.true; + + //#endregion + }); + + it("#2: User submits Debt refinancing task if market move to Gelato via DSA", async function () { + //#region User submit a Debt Refinancing task if market move against him + + // User submit the refinancing task if market move against him. + // So in this case if the maker vault go to the unsafe area + // the refinancing task will be executed and the position + // will be split on two position on maker and compound. + // It will be done through a algorithm that will optimize the + // total borrow rate. + + const conditionMakerVaultUnsafeObj = new GelatoCoreLib.Condition({ + inst: contracts.conditionMakerVaultUnsafe.address, + data: await contracts.conditionMakerVaultUnsafe.getConditionData( + vaultAId, + contracts.priceOracleResolver.address, + await hre.run("abi-encode-withselector", { + abi: ABI.PriceOracleResolverABI, + functionname: "getMockPrice", + inputs: [address.userAddress], + }), + constants.MIN_COL_RATIO_MAKER + ), + }); + + // ======= GELATO TASK SETUP ====== + const refinanceIfCompoundBorrowIsBetter = new GelatoCoreLib.Task({ + conditions: [conditionMakerVaultUnsafeObj], + actions: spells, + }); + + const gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({ + addr: address.providerAddress, // Gelato Provider Address + module: contracts.dsaProviderModule.address, // Gelato DSA module + }); + + const expiryDate = 0; + + await expect( + contracts.dsa.cast( + [contracts.connectGelato.address], // targets + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectGelatoABI, + functionname: "submitTask", + inputs: [ + gelatoExternalProvider, + refinanceIfCompoundBorrowIsBetter, + expiryDate, + ], + }), + ], // datas + address.userAddress, // origin + { + gasLimit: 5000000, + } + ) + ).to.emit(contracts.gelatoCore, "LogTaskSubmitted"); + + taskReceipt = new GelatoCoreLib.TaskReceipt({ + id: await contracts.gelatoCore.currentTaskReceiptId(), + userProxy: contracts.dsa.address, + provider: gelatoExternalProvider, + tasks: [refinanceIfCompoundBorrowIsBetter], + expiryDate, + }); + + //#endregion + }); + + // This test showcases the part which is automatically done by the Gelato Executor Network on mainnet + // Bots constatly check whether the submitted task is executable (by calling canExec) + // If the task becomes executable (returns "OK"), the "exec" function will be called + // which will execute the debt refinancing on behalf of the user + it("#3: Use ETH-A to ETH-B refinancing if the maker vault become unsafe after a market move.", async function () { + // Steps + // Step 1: Market Move against the user (Mock) + // Step 2: Executor execute the user's task + + //#region Step 1 Market Move against the user (Mock) + + // Ether market price went from the current price to 250$ + + const gelatoGasPrice = await hre.run("fetchGelatoGasPrice"); + expect(gelatoGasPrice).to.be.lte(constants.GAS_PRICE_CEIL); + + // TO DO: base mock price off of real price data + await contracts.priceOracleResolver.setMockPrice( + ethers.utils.parseUnits("400", 18) + ); + + expect( + await contracts.gelatoCore + .connect(address.executorWallet) + .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice) + ).to.be.equal("ConditionNotOk:MakerVaultNotUnsafe"); + + // TO DO: base mock price off of real price data + await contracts.priceOracleResolver.setMockPrice( + ethers.utils.parseUnits("250", 18) + ); + + expect( + await contracts.gelatoCore + .connect(address.executorWallet) + .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice) + ).to.be.equal("OK"); + + //#endregion + + //#region Step 2 Executor execute the user's task + + // The market move make the vault unsafe, so the executor + // will execute the user's task to make the user position safe + // by a debt refinancing in compound. + + //#region EXPECTED OUTCOME + + const gasFeesPaidFromCol = ethers.utils + .parseUnits(String(1933090 + 19331 * 2), 0) + .mul(gelatoGasPrice); + const debtOnMakerBefore = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultDebt( + vaultAId + ); + const pricedCollateral = ( + await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultCollateralBalance( + vaultAId + ) + ).sub(gasFeesPaidFromCol); + + //#endregion + const providerBalanceBeforeExecution = await address.providerWallet.getBalance(); + + await expect( + contracts.gelatoCore.connect(address.executorWallet).exec(taskReceipt, { + gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei) + gasLimit: constants.GAS_LIMIT, + }) + ).to.emit(contracts.gelatoCore, "LogExecSuccess"); + + // 🚧 For Debugging: + // const txResponse2 = await contracts.gelatoCore + // .connect(address.executorWallet) + // .exec(taskReceipt, { + // gasPrice: gelatoGasPrice, + // gasLimit: constants.GAS_LIMIT, + // }); + // const {blockHash} = await txResponse2.wait(); + // const logs = await ethers.provider.getLogs({blockHash}); + // const iFace = new ethers.utils.Interface(GelatoCoreLib.GelatoCore.abi); + // for (const log of logs) { + // console.log(iFace.parseLog(log).args.reason); + // } + // await GelatoCoreLib.sleep(10000); + + const cdps = await contracts.getCdps.getCdpsAsc( + contracts.dssCdpManager.address, + contracts.dsa.address + ); + let vaultBId = String(cdps.ids[1]); + expect(cdps.ids[1].isZero()).to.be.false; + + const debtOnMakerVaultB = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultDebt( + vaultBId + ); + const pricedCollateralonVaultB = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultCollateralBalance( + vaultBId + ); + + expect(await address.providerWallet.getBalance()).to.be.gt( + providerBalanceBeforeExecution + ); + + // Estimated amount to borrowed token should be equal to the actual one read on compound contracts + expect(debtOnMakerBefore.sub(debtOnMakerVaultB)).to.be.lt( + ethers.utils.parseUnits("2", 0) + ); + + // Estimated amount of collateral should be equal to the actual one read on compound contracts + expect(pricedCollateral).to.be.equal(pricedCollateralonVaultB); + + const debtOnMakerOnVaultAAfter = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultDebt( + vaultAId + ); + const collateralOnMakerOnVaultAAfter = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultCollateralBalance( + vaultAId + ); // in Ether. + + // We should not have borrowed DAI on maker or deposited ether on it. + expect(debtOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero); + expect(collateralOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero); + + // DSA contain 1000 DAI + expect( + (await contracts.daiToken.balanceOf(contracts.dsa.address)).sub( + constants.MAKER_INITIAL_DEBT + ) + ).to.be.lt(ethers.utils.parseUnits("2", 0)); + + //#endregion + }); +}); diff --git a/test/helpers/Full-Refinance-External-Provider.helper.js b/test/helpers/Full-Refinance-External-Provider.helper.js index e9e1f20..2c0ec3a 100644 --- a/test/helpers/Full-Refinance-External-Provider.helper.js +++ b/test/helpers/Full-Refinance-External-Provider.helper.js @@ -13,6 +13,7 @@ const ConnectInstaPool = require("../../pre-compiles/ConnectInstaPool.json"); const ConnectAuth = require("../../pre-compiles/ConnectAuth.json"); const InstaConnector = require("../../pre-compiles/InstaConnectors.json"); +const InstaMapping = require("../../pre-compiles/InstaMapping.json"); const DssCdpManager = require("../../pre-compiles/DssCdpManager.json"); const GetCdps = require("../../pre-compiles/GetCdps.json"); const IERC20 = require("../../pre-compiles/IERC20.json"); @@ -81,6 +82,7 @@ class Helper { let cDaiToken; let cEthToken; let instaMaster; + let instaMapping; let instaConnectors; let compoundResolver; // Contracts to deploy and use for local testing @@ -99,6 +101,10 @@ class Helper { InstaIndex.abi, hre.network.config.InstaIndex ); + instaMapping = await ethers.getContractAt( + InstaMapping.abi, + hre.network.config.InstaMapping + ); instaList = await ethers.getContractAt( InstaList.abi, hre.network.config.InstaList @@ -195,6 +201,7 @@ class Helper { connectCompound: connectCompound, instaIndex: instaIndex, instaList: instaList, + instaMapping: instaMapping, dssCdpManager: dssCdpManager, getCdps: getCdps, daiToken: daiToken, @@ -213,44 +220,63 @@ class Helper { }; } - async setup() { + async makerToCompoundTestSetup() { let address = await this.address(); let contracts = await this.contracts(); - let dsa; let vaultId; - /////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////// Setup /////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////// // Gelato Testing environment setup. - // Step 1 : Add EUR/USD Maker Medianizer in the PriceOracleResolver - // Step 2 : Enable Debt Bridge Connector and Gelato Provider Payment Connector - // Step 3 : Executor Staking on Gelato - // Step 4 : Provider put some fund on gelato for paying future tasks executions - // Step 5 : Provider choose a executor - // Step 6 : Provider will add a module - // Step 7 : User create a DeFi Smart Account - // Step 8 : User open a Vault, put some ether on it and borrow some dai - // Step 9 : Provider should whitelist task + await this.enableGelatoConnectorsForFromMaker(address, contracts); + await this.executorDoStaking(address, contracts); + await this.providerDoFunding(address, contracts); + await this.providerChooseExecutor(address, contracts); + await this.providerAddCustomModuleForPayment(address, contracts); + await this.userCreateADSA(address, contracts); + vaultId = await this.userOpenDepositBorrowOnMakerVault(address, contracts); + let spells = await this.providerWhiteListTaskForMakerToCompound( + address, + contracts, + vaultId + ); - //#region Step 1 Add EUR/USD Maker Medianizer in the PriceOracleResolver + return { + address: address, + contracts: contracts, + vaultId: vaultId, + spells: spells, + }; + } - // PriceOracleResolver is a price feeder aggregator - // You will be able to query price from multiple source through this aggregator - // For the demo we add the ETH/USD Medianizer to the aggregator - // MakerDAO price oracle are called Medianizer + async makerETHAToMakerETHBSetup() { + let address = await this.address(); + let contracts = await this.contracts(); + let vaultAId; - // await priceOracleResolver.addOracle( - // ORACLE_MAKER_ETH_USD, - // ORACLE_MAKER_ETH_USD_ADDR, - // PRICE_ORACLE_MAKER_PAYLOAD - // ); + // Gelato Testing environment setup. + await this.enableGelatoConnectorsForFromMaker(address, contracts); + await this.executorDoStaking(address, contracts); + await this.providerDoFunding(address, contracts); + await this.providerChooseExecutor(address, contracts); + await this.providerAddCustomModuleForPayment(address, contracts); + await this.userCreateADSA(address, contracts); + await this.masterAddETHBOnGemJoinMapping(address, contracts); + vaultAId = await this.userOpenDepositBorrowOnMakerVault(address, contracts); + let spells = await this.providerWhiteListTaskForMakerETHAToMakerETHB( + address, + contracts, + vaultAId + ); - //#endregion + return { + address: address, + contracts: contracts, + vaultAId: vaultAId, + spells: spells, + }; + } - //#region Step 2 Enable Debt Bridge Connector and Gelato Provider Payment Connector + async enableGelatoConnectorsForFromMaker(address, contracts) { + //#region Enable Debt Bridge Connector and Gelato Provider Payment Connector // Debt Bridge Connector is used during refinancing of debt // This Connect help the user to split a position in one protocol. @@ -296,8 +322,10 @@ class Helper { ).to.be.true; //#endregion + } - //#region Step 3 Executor Staking on Gelato + async executorDoStaking(address, contracts) { + //#region Executor Staking on Gelato // For task execution provider will ask a executor to watch the // blockchain for possible execution autorization given by @@ -315,8 +343,10 @@ class Helper { ).to.be.true; //#endregion + } - //#region Step 4 Provider put some fund on gelato for paying future tasks executions + async providerDoFunding(address, contracts) { + //#region Provider put some fund on gelato for paying future tasks executions // Provider put some funds in gelato system for paying the // Executor when this one will execute task on behalf of the @@ -341,8 +371,10 @@ class Helper { ).to.be.equal(TASK_AUTOMATION_FUNDS); //#endregion + } - //#region Step 5 Provider choose a executor + async providerChooseExecutor(address, contracts) { + //#region Provider choose a executor // Provider choose a executor who will execute futur task // for the provider, it will be compensated by the provider. @@ -358,8 +390,10 @@ class Helper { ).to.be.equal(address.executorAddress); //#endregion + } - //#region Step 6 Provider will add a module + async providerAddCustomModuleForPayment(address, contracts) { + //#region Provider will add a module // By adding a module the provider will format future task's // payload by adding some specificity like his address to the @@ -381,8 +415,10 @@ class Helper { ).to.be.true; //#endregion + } - //#region Step 7 User create a DeFi Smart Account + async userCreateADSA(address, contracts) { + //#region User create a DeFi Smart Account // User create a Instadapp DeFi Smart Account // who give him the possibility to interact @@ -398,7 +434,7 @@ class Helper { await expect(await contracts.instaList.accounts()).to.be.equal(dsaID); // Instantiate the DSA - dsa = await ethers.getContractAt( + const dsa = await ethers.getContractAt( InstaAccount.abi, await contracts.instaList.accountAddr(dsaID) ); @@ -406,13 +442,15 @@ class Helper { contracts.dsa = dsa; //#endregion + } + async userOpenDepositBorrowOnMakerVault(address, contracts) { //#region Step 8 User open a Vault, put some ether on it and borrow some dai // User open a maker vault // He deposit 10 Eth on it // He borrow a 1000 DAI - + const dsa = contracts.dsa; const openVault = await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "open", @@ -429,7 +467,7 @@ class Helper { contracts.dssCdpManager.address, dsa.address ); - vaultId = String(cdps.ids[0]); + let vaultId = String(cdps.ids[0]); expect(cdps.ids[0].isZero()).to.be.false; await dsa.cast( @@ -465,6 +503,30 @@ class Helper { //#endregion + return vaultId; + } + + async masterAddETHBOnGemJoinMapping(address, contracts) { + await address.userWallet.sendTransaction({ + to: hre.network.config.InstaMaster, + value: ethers.utils.parseEther("0.1"), + }); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [await contracts.instaMaster.getAddress()], + }); + + const ethBGemJoin = "0x08638eF1A205bE6762A8b935F5da9b700Cf7322c"; + await expect( + contracts.instaMapping + .connect(contracts.instaMaster) + .addGemJoinMapping([ethBGemJoin]) + ).to.emit(contracts.instaMapping, "LogAddGemJoinMapping"); + } + + // Instadapp UI should do the same implementation for submitting debt bridge task + async providerWhiteListTaskForMakerToCompound(address, contracts, vaultId) { //#region Step 9 Provider should whitelist task // By WhiteList task, the provider can constrain the type @@ -608,13 +670,171 @@ class Helper { //#endregion + return spells; + } + + // Instadapp UI should do the same implementation for submitting debt bridge task + async providerWhiteListTaskForMakerETHAToMakerETHB( + address, + contracts, + vaultId + ) { + //#region Step 9 Provider should whitelist task + + // By WhiteList task, the provider can constrain the type + // of task the user can submitting. + + //#region Actions + + let spells = []; + + const debtBridgeCalculationForFullRefinance = new GelatoCoreLib.Action({ + addr: contracts.connectGelatoDebtBridgeFromMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectGelatoDebtBridgeFromMakerABI, + functionname: "saveFullRefinanceDataToMemory", + inputs: [vaultId, 0, 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(debtBridgeCalculationForFullRefinance); + + const flashBorrow = new GelatoCoreLib.Action({ + addr: contracts.connectInstaPool.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectInstaPool.abi, + functionname: "flashBorrow", + inputs: [hre.network.config.DAI, 0, "600", 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(flashBorrow); + + const paybackMaker = new GelatoCoreLib.Action({ + addr: contracts.connectMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "payback", + inputs: [vaultId, ethers.constants.MaxUint256, 0, 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(paybackMaker); + + const withdrawMaker = new GelatoCoreLib.Action({ + addr: contracts.connectMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "withdraw", + inputs: [vaultId, ethers.constants.MaxUint256, 0, 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(withdrawMaker); + + const openVaultB = new GelatoCoreLib.Action({ + addr: contracts.connectMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "open", + inputs: ["ETH-B"], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(openVaultB); + + const depositMakerOnVaultB = new GelatoCoreLib.Action({ + addr: contracts.connectMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [0, 0, "603", 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(depositMakerOnVaultB); + + const borrowOnVaultB = new GelatoCoreLib.Action({ + addr: contracts.connectMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "borrow", + inputs: [0, 0, "604", 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(borrowOnVaultB); + + const flashPayBack = new GelatoCoreLib.Action({ + addr: contracts.connectInstaPool.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectInstaPool.abi, + functionname: "flashPayback", + inputs: [hre.network.config.DAI, 0, 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(flashPayBack); + + const payProvider = new GelatoCoreLib.Action({ + addr: contracts.connectGelatoProviderPayment.address, + data: await hre.run("abi-encode-withselector", { + abi: ConnectGelatoProviderPaymentABI, + functionname: "payProvider", + inputs: [address.providerAddress, ETH, 0, "605", 0], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + }); + + spells.push(payProvider); + + const gasPriceCeil = ethers.constants.MaxUint256; + + const connectGelatoDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( + { + conditions: [contracts.conditionMakerVaultUnsafe.address], + actions: spells, + gasPriceCeil, + } + ); + + await expect( + contracts.gelatoCore + .connect(address.providerWallet) + .provideTaskSpecs([connectGelatoDebtBridgeFromMakerTaskSpec]) + ).to.emit(contracts.gelatoCore, "LogTaskSpecProvided"); + + expect( + await contracts.gelatoCore + .connect(address.providerWallet) + .isTaskSpecProvided( + address.providerAddress, + connectGelatoDebtBridgeFromMakerTaskSpec + ) + ).to.be.equal("OK"); + + expect( + await contracts.gelatoCore + .connect(address.providerWallet) + .taskSpecGasPriceCeil( + address.providerAddress, + await contracts.gelatoCore + .connect(address.providerWallet) + .hashTaskSpec(connectGelatoDebtBridgeFromMakerTaskSpec) + ) + ).to.be.equal(gasPriceCeil); + //#endregion - return { - address: address, - contracts: contracts, - vaultId: vaultId, - spells: spells, - }; + + return spells; } async getABI() {