Gelato-automations/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js

265 lines
8.8 KiB
JavaScript
Raw Normal View History

2020-10-26 17:33:36 +00:00
const {expect} = require("chai");
const hre = require("hardhat");
2020-10-27 08:24:15 +00:00
const {ethers} = hre;
2020-10-26 17:33:36 +00:00
const GelatoCoreLib = require("@gelatonetwork/core");
2020-11-02 10:51:49 +00:00
const makerETHAToMakerETHBSetup = require("./helpers/Full-Refinance-Maker-To-Maker.helper");
2020-10-26 17:33:36 +00:00
// 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;
2020-10-27 08:24:15 +00:00
let wallets;
2020-10-26 17:33:36 +00:00
let constants;
let ABI;
// Payload Params for ConnectGelatoFullDebtBridgeFromMaker and ConditionMakerVaultUnsafe
2020-10-26 17:33:36 +00:00
let vaultAId;
// For TaskSpec and for Task
2020-10-27 08:24:15 +00:00
let gelatoDebtBridgeSpells = [];
2020-10-26 17:33:36 +00:00
// Cross test var
let taskReceipt;
before(async function () {
// Reset back to a fresh forked state during runtime
2020-10-27 08:24:15 +00:00
await hre.run("hardhatReset");
2020-10-30 17:35:11 +00:00
const result = await makerETHAToMakerETHBSetup();
2020-10-26 17:33:36 +00:00
2020-10-27 08:24:15 +00:00
wallets = result.wallets;
2020-10-26 17:33:36 +00:00
contracts = result.contracts;
vaultAId = result.vaultAId;
2020-10-27 08:24:15 +00:00
gelatoDebtBridgeSpells = result.spells;
2020-10-26 17:33:36 +00:00
2020-10-30 17:35:11 +00:00
ABI = result.ABI;
constants = result.constants;
2020-10-26 17:33:36 +00:00
});
2020-10-27 08:24:15 +00:00
it("#1: DSA authorizes Gelato to cast spells.", async function () {
2020-10-26 17:33:36 +00:00
//#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],
}),
],
2020-10-27 08:24:15 +00:00
wallets.userAddress
2020-10-26 17:33:36 +00:00
);
expect(await contracts.dsa.isAuth(contracts.gelatoCore.address)).to.be.true;
//#endregion
});
2020-10-27 08:24:15 +00:00
it("#2: User submits automated Debt Bridge task to Gelato via DSA", async function () {
2020-10-26 17:33:36 +00:00
//#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",
2020-10-27 08:24:15 +00:00
inputs: [wallets.userAddress],
2020-10-26 17:33:36 +00:00
}),
constants.MIN_COL_RATIO_MAKER
),
});
// ======= GELATO TASK SETUP ======
2020-10-27 08:24:15 +00:00
const refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({
2020-10-26 17:33:36 +00:00
conditions: [conditionMakerVaultUnsafeObj],
2020-10-27 08:24:15 +00:00
actions: gelatoDebtBridgeSpells,
2020-10-26 17:33:36 +00:00
});
const gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({
2020-10-27 08:24:15 +00:00
addr: wallets.providerAddress, // Gelato Provider Address
2020-10-26 17:33:36 +00:00
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,
2020-10-27 08:24:15 +00:00
refinanceFromEthAToBIfVaultUnsafe,
2020-10-26 17:33:36 +00:00
expiryDate,
],
}),
], // datas
2020-10-27 08:24:15 +00:00
wallets.userAddress, // origin
2020-10-26 17:33:36 +00:00
{
gasLimit: 5000000,
}
)
).to.emit(contracts.gelatoCore, "LogTaskSubmitted");
taskReceipt = new GelatoCoreLib.TaskReceipt({
id: await contracts.gelatoCore.currentTaskReceiptId(),
userProxy: contracts.dsa.address,
provider: gelatoExternalProvider,
2020-10-27 08:24:15 +00:00
tasks: [refinanceFromEthAToBIfVaultUnsafe],
2020-10-26 17:33:36 +00:00
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
2020-10-27 08:24:15 +00:00
it("#3: Auto-refinance from ETH-A to ETH-B, if the Maker vault became unsafe.", async function () {
2020-10-26 17:33:36 +00:00
// 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
2020-10-27 08:24:15 +00:00
.connect(wallets.executorWallet)
2020-10-26 17:33:36 +00:00
.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
2020-10-27 08:24:15 +00:00
.connect(wallets.executorWallet)
2020-10-26 17:33:36 +00:00
.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
2020-10-30 17:35:11 +00:00
.parseUnits(String(1490779 + 14908 * 2), 0)
2020-10-26 17:33:36 +00:00
.mul(gelatoGasPrice);
2020-11-02 10:51:49 +00:00
const debtOnMakerBefore = await contracts.makerResolver.getMakerVaultDebt(
2020-10-26 17:33:36 +00:00
vaultAId
);
const pricedCollateral = (
2020-11-02 10:51:49 +00:00
await contracts.makerResolver.getMakerVaultCollateralBalance(vaultAId)
2020-10-26 17:33:36 +00:00
).sub(gasFeesPaidFromCol);
//#endregion
2020-10-27 08:24:15 +00:00
const providerBalanceBeforeExecution = await wallets.providerWallet.getBalance();
2020-10-26 17:33:36 +00:00
await expect(
2020-10-27 08:24:15 +00:00
contracts.gelatoCore.connect(wallets.executorWallet).exec(taskReceipt, {
2020-10-26 17:33:36 +00:00
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
2020-10-30 17:35:11 +00:00
// .connect(wallets.executorWallet)
2020-10-26 17:33:36 +00:00
// .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;
2020-11-02 10:51:49 +00:00
const debtOnMakerVaultB = await contracts.makerResolver.getMakerVaultDebt(
2020-10-26 17:33:36 +00:00
vaultBId
);
2020-11-02 10:51:49 +00:00
const pricedCollateralOnVaultB = await contracts.makerResolver.getMakerVaultCollateralBalance(
2020-10-26 17:33:36 +00:00
vaultBId
);
2020-10-27 08:24:15 +00:00
expect(await wallets.providerWallet.getBalance()).to.be.gt(
2020-10-26 17:33:36 +00:00
providerBalanceBeforeExecution
);
// Estimated amount to borrowed token should be equal to the actual one read on compound contracts
expect(debtOnMakerBefore).to.be.equal(debtOnMakerVaultB);
2020-10-26 17:33:36 +00:00
// Estimated amount of collateral should be equal to the actual one read on compound contracts
2020-10-27 08:24:15 +00:00
expect(pricedCollateral).to.be.equal(pricedCollateralOnVaultB);
2020-10-26 17:33:36 +00:00
2020-11-02 10:51:49 +00:00
const debtOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultDebt(
2020-10-26 17:33:36 +00:00
vaultAId
);
2020-11-02 10:51:49 +00:00
const collateralOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultCollateralBalance(
2020-10-26 17:33:36 +00:00
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);
2020-10-27 08:24:15 +00:00
// DSA has maximum 2 wei DAI in it due to maths inaccuracies
2020-11-02 10:51:49 +00:00
expect(await contracts.DAI.balanceOf(contracts.dsa.address)).to.be.equal(
constants.MAKER_INITIAL_DEBT
);
2020-10-26 17:33:36 +00:00
//#endregion
});
});