Add debt ceiling and dust check (#92)

* feat: conditions debt ceiling and dust
This commit is contained in:
Twin Fish 2020-12-01 12:52:58 +01:00 committed by GitHub
parent 2d8581141e
commit 2a2158f89b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1033 additions and 3 deletions

View File

@ -0,0 +1,77 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {GelatoBytes} from "../../../lib/GelatoBytes.sol";
import {
_debtIsDustNewVault,
_debtIsDust,
_getMakerVaultDebt,
_isVaultOwner
} from "../../../functions/dapps/FMaker.sol";
contract ConditionBorrowAmountIsDust is GelatoConditionsStandard {
using GelatoBytes for bytes;
function getConditionData(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string calldata _destColType
) public pure virtual returns (bytes memory) {
return
abi.encodeWithSelector(
this.isBorrowAmountDust.selector,
_dsa,
_fromVaultId,
_destVaultId,
_destColType
);
}
function ok(
uint256,
bytes calldata _conditionData,
uint256
) public view virtual override returns (string memory) {
(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string memory _destColType
) = abi.decode(_conditionData[4:], (address, uint256, uint256, string));
return
isBorrowAmountDust(_dsa, _fromVaultId, _destVaultId, _destColType);
}
function isBorrowAmountDust(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string memory _destColType
) public view returns (string memory) {
_destVaultId = _isVaultOwner(_destVaultId, _dsa) ? _destVaultId : 0;
uint256 wDaiToBorrow = _getMakerVaultDebt(_fromVaultId);
return
borrowAmountIsDustExplicit(_destVaultId, wDaiToBorrow, _destColType)
? "DebtNotGreaterThanDust"
: OK;
}
function borrowAmountIsDustExplicit(
uint256 _vaultId,
uint256 _wDaiToBorrow,
string memory _colType
) public view returns (bool) {
return
_vaultId == 0
? _debtIsDustNewVault(_colType, _wDaiToBorrow)
: _debtIsDust(_vaultId, _wDaiToBorrow);
}
}

View File

@ -5,9 +5,6 @@ pragma experimental ABIEncoderV2;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {
IGelatoCore
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
import {GelatoBytes} from "../../../lib/GelatoBytes.sol";
import {
_getMakerVaultDebt,

View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {GelatoBytes} from "../../../lib/GelatoBytes.sol";
import {
_debtCeilingIsReachedNewVault,
_debtCeilingIsReached,
_getMakerVaultDebt,
_isVaultOwner
} from "../../../functions/dapps/FMaker.sol";
import {
_getRealisedDebt
} from "../../../functions/gelato/FGelatoDebtBridge.sol";
contract ConditionDebtCeilingIsReached is GelatoConditionsStandard {
using GelatoBytes for bytes;
function getConditionData(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string calldata _destColType
) public pure virtual returns (bytes memory) {
return
abi.encodeWithSelector(
this.isDebtCeilingReached.selector,
_dsa,
_fromVaultId,
_destVaultId,
_destColType
);
}
function ok(
uint256,
bytes calldata _conditionData,
uint256
) public view virtual override returns (string memory) {
(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string memory _destColType
) = abi.decode(_conditionData[4:], (address, uint256, uint256, string));
return
isDebtCeilingReached(
_dsa,
_fromVaultId,
_destVaultId,
_destColType
);
}
function isDebtCeilingReached(
address _dsa,
uint256 _fromVaultId,
uint256 _destVaultId,
string memory _destColType
) public view returns (string memory) {
_destVaultId = _isVaultOwner(_destVaultId, _dsa) ? _destVaultId : 0;
uint256 wDaiToBorrow =
_getRealisedDebt(_getMakerVaultDebt(_fromVaultId));
return
debtCeilingIsReachedExplicit(
_destVaultId,
wDaiToBorrow,
_destColType
)
? "DebtCeilingReached"
: OK;
}
function debtCeilingIsReachedExplicit(
uint256 _vaultId,
uint256 _wDaiToBorrow,
string memory _colType
) public view returns (bool) {
return
_vaultId == 0
? _debtCeilingIsReachedNewVault(_colType, _wDaiToBorrow)
: _debtCeilingIsReached(_vaultId, _wDaiToBorrow);
}
}

View File

@ -107,6 +107,87 @@ function _newVaultWillBeSafe(
return tab <= mul(ink, spot);
}
function _debtCeilingIsReachedNewVault(
string memory _colType,
uint256 _amtToBorrow
) view returns (bool) {
IMcdManager manager = IMcdManager(MCD_MANAGER);
IVat vat = IVat(manager.vat());
bytes32 ilk = _stringToBytes32(_colType);
(uint256 Art, uint256 rate, , uint256 line, ) = vat.ilks(ilk);
uint256 Line = vat.Line();
uint256 debt = vat.debt();
uint256 dart = _getBorrowAmt(_amtToBorrow, 0, rate);
uint256 dtab = mul(rate, dart);
debt = add(debt, dtab);
Art = add(Art, dart);
return mul(Art, rate) > line || debt > Line;
}
function _debtCeilingIsReached(uint256 _vaultId, uint256 _amtToBorrow)
view
returns (bool)
{
IMcdManager manager = IMcdManager(MCD_MANAGER);
IVat vat = IVat(manager.vat());
(bytes32 ilk, address urn) = _getVaultData(manager, _vaultId);
(uint256 Art, uint256 rate, , uint256 line, ) = vat.ilks(ilk);
uint256 dai = vat.dai(urn);
uint256 Line = vat.Line();
uint256 debt = vat.debt();
uint256 dart = _getBorrowAmt(_amtToBorrow, dai, rate);
uint256 dtab = mul(rate, dart);
debt = add(debt, dtab);
Art = add(Art, dart);
return mul(Art, rate) > line || debt > Line;
}
function _debtIsDustNewVault(string memory _colType, uint256 _amtToBorrow)
view
returns (bool)
{
IMcdManager manager = IMcdManager(MCD_MANAGER);
IVat vat = IVat(manager.vat());
bytes32 ilk = _stringToBytes32(_colType);
(, uint256 rate, , , uint256 dust) = vat.ilks(ilk);
uint256 art = _getBorrowAmt(_amtToBorrow, 0, rate);
uint256 tab = mul(rate, art);
return tab < dust;
}
function _debtIsDust(uint256 _vaultId, uint256 _amtToBorrow)
view
returns (bool)
{
IMcdManager manager = IMcdManager(MCD_MANAGER);
IVat vat = IVat(manager.vat());
(bytes32 ilk, address urn) = _getVaultData(manager, _vaultId);
(, uint256 art) = vat.urns(ilk, urn);
(, uint256 rate, , , uint256 dust) = vat.ilks(ilk);
uint256 dai = vat.dai(urn);
uint256 dart = _getBorrowAmt(_amtToBorrow, dai, rate);
art = add(art, dart);
uint256 tab = mul(rate, art);
return tab < dust;
}
function _getVaultData(IMcdManager manager, uint256 vault)
view
returns (bytes32 ilk, address urn)

View File

@ -16,4 +16,9 @@ interface IVat {
function dai(address) external view returns (uint256);
function urns(bytes32, address) external view returns (uint256, uint256);
function debt() external view returns (uint256);
// solhint-disable-next-line
function Line() external view returns (uint256);
}

View File

@ -0,0 +1,27 @@
const { sleep } = require("@gelatonetwork/core");
module.exports = async (hre) => {
if (hre.network.name === "mainnet") {
console.log(
"\n\n Deploying ConditionBorrowAmountIsDust to mainnet. Hit ctrl + c to abort"
);
await sleep(10000);
}
const { deployments } = hre;
const { deploy } = deployments;
const { deployer } = await hre.getNamedAccounts();
// the following will only deploy "ConditionBorrowAmountIsDust"
// if the contract was never deployed or if the code changed since last deployment
await deploy("ConditionBorrowAmountIsDust", {
from: deployer,
gasPrice: hre.network.config.gasPrice,
log: hre.network.name === "mainnet" ? true : false,
});
};
module.exports.skip = async (hre) => {
return hre.network.name === "mainnet" ? true : false;
};
module.exports.tags = ["ConditionBorrowAmountIsDust"];

View File

@ -0,0 +1,27 @@
const { sleep } = require("@gelatonetwork/core");
module.exports = async (hre) => {
if (hre.network.name === "mainnet") {
console.log(
"\n\n Deploying ConditionDebtCeilingIsReached to mainnet. Hit ctrl + c to abort"
);
await sleep(10000);
}
const { deployments } = hre;
const { deploy } = deployments;
const { deployer } = await hre.getNamedAccounts();
// the following will only deploy "ConditionDebtCeilingIsReached"
// if the contract was never deployed or if the code changed since last deployment
await deploy("ConditionDebtCeilingIsReached", {
from: deployer,
gasPrice: hre.network.config.gasPrice,
log: hre.network.name === "mainnet" ? true : false,
});
};
module.exports.skip = async (hre) => {
return hre.network.name === "mainnet" ? true : false;
};
module.exports.tags = ["ConditionDebtCeilingIsReached"];

View File

@ -53,6 +53,10 @@ module.exports = {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
blockNumber: 11346751,
},
// Accounts
accounts: {
accountsBalance: "1000000000000000000000000",
},
// Custom
...mainnetDeployments,
},

View File

@ -0,0 +1,547 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { deployments, ethers } = hre;
// #region Contracts ABI
const ConnectMaker = require("../../../pre-compiles/ConnectMaker.json");
const GetCdps = require("../../../pre-compiles/GetCdps.json");
const DssCdpManager = require("../../../pre-compiles/DssCdpManager.json");
const InstaList = require("../../../pre-compiles/InstaList.json");
const InstaAccount = require("../../../pre-compiles/InstaAccount.json");
const InstaIndex = require("../../../pre-compiles/InstaIndex.json");
const IERC20 = require("../../../pre-compiles/IERC20.json");
// #endregion
describe("ConditionDebtCeilingIsReached Unit Test", 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 userWallet;
let userAddress;
let getCdps;
let dssCdpManager;
let instaList;
let instaIndex;
let DAI;
let conditionDebtCeilingIsReached;
let dsa;
let cdpAId;
let cdpBId;
let amountToBorrow;
beforeEach(async function () {
// Deploy contract dependencies
await deployments.fixture();
// Get Test Wallet for local testnet
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("900000") // amount needed to test debt ceiling.
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
hre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
hre.network.config.InstaList
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
hre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
hre.network.config.DssCdpManager
);
DAI = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
// ========== Test Setup ============
conditionDebtCeilingIsReached = await ethers.getContract(
"ConditionDebtCeilingIsReached"
);
// Create DeFi Smart Account
const dsaAccountCount = await instaList.accounts();
await expect(instaIndex.build(userAddress, 1, userAddress)).to.emit(
instaIndex,
"LogAccountCreated"
);
const dsaID = dsaAccountCount.add(1);
await expect(await instaList.accounts()).to.be.equal(dsaID);
// Instantiate the DSA
dsa = await ethers.getContractAt(
InstaAccount.abi,
await instaList.accountAddr(dsaID)
);
// Create/Deposit/Borrow a Vault for ETH-A
let openVault = await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
});
await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpAId = String(cdps.ids[0]);
expect(cdps.ids[0].isZero()).to.be.false;
// Create/Deposit/Borrow a Vault for ETH-B
openVault = await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-B"],
});
await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress);
cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpBId = String(cdps.ids[1]);
expect(cdps.ids[1].isZero()).to.be.false;
});
it("#1: ok should return Ok if for new vault (to open) the borrow amnount is less than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("500", 18);
const amountToDeposit = ethers.utils.parseUnits("2", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
0,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("OK");
});
it("#2: ok should return DebtCeilingReached if for new vault (to open) the borrow amnount is greater than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("1000000", 18);
const amountToDeposit = ethers.utils.parseUnits("200000", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
0,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("DebtCeilingReached");
});
it("#3: ok should return Ok if for old vault the borrow amnount is less than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("500", 18);
const amountToDeposit = ethers.utils.parseUnits("2", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
cdpBId,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("OK");
});
it("#4: ok should return DebtCeilingReached if for old vault the borrow amnount is greater than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("1000000", 18);
const amountToDeposit = ethers.utils.parseUnits("200000", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
cdpBId,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("DebtCeilingReached");
});
it("#5: ok should return Ok if for old vault (with debt) the borrow amnount is less than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("500", 18);
const amountToDeposit = ethers.utils.parseUnits("2", 18);
const amountToBorrowForVaultB = ethers.utils.parseUnits("500", 18);
const amountToDepositForVaultB = ethers.utils.parseUnits("2", 18);
//#region Deposit vault ETH-A
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit vault ETH-A
//#region Borrow vault ETH-A
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow vault ETH-A
//#region Deposit vault ETH-B
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpBId, amountToDepositForVaultB, 0, 0],
}),
],
userAddress,
{
value: amountToDepositForVaultB,
}
);
//#endregion Deposit vault ETH-B
//#region Borrow vault ETH-B
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpBId, amountToBorrowForVaultB, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(
amountToBorrowForVaultB.add(amountToBorrow)
);
//#endregion Borrow vault ETH-B
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
cdpBId,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("OK");
});
it("#6: ok should return DebtCeilingReached if for old vault (with debt) the borrow amnount is greater than the remaining debt that can be borrowed", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not be able to borrow due to debt ceiling.
amountToBorrow = ethers.utils.parseUnits("1000000", 18);
const amountToDeposit = ethers.utils.parseUnits("200000", 18);
const amountToBorrowForVaultB = ethers.utils.parseUnits("500", 18);
const amountToDepositForVaultB = ethers.utils.parseUnits("2", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
//#region Deposit vault ETH-B
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpBId, amountToDepositForVaultB, 0, 0],
}),
],
userAddress,
{
value: amountToDepositForVaultB,
}
);
//#endregion Deposit vault ETH-B
//#region Borrow vault ETH-B
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpBId, amountToBorrowForVaultB, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(
amountToBorrowForVaultB.add(amountToBorrow)
);
//#endregion Borrow vault ETH-B
const conditionData = await conditionDebtCeilingIsReached.getConditionData(
dsa.address,
cdpAId,
cdpBId,
"ETH-B"
);
expect(
await conditionDebtCeilingIsReached.ok(0, conditionData, 0)
).to.be.equal("DebtCeilingReached");
});
});

View File

@ -0,0 +1,175 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { deployments, ethers } = hre;
// #region Contracts ABI
const ConnectMaker = require("../../../pre-compiles/ConnectMaker.json");
const GetCdps = require("../../../pre-compiles/GetCdps.json");
const DssCdpManager = require("../../../pre-compiles/DssCdpManager.json");
const InstaList = require("../../../pre-compiles/InstaList.json");
const InstaAccount = require("../../../pre-compiles/InstaAccount.json");
const InstaIndex = require("../../../pre-compiles/InstaIndex.json");
const IERC20 = require("../../../pre-compiles/IERC20.json");
// #endregion
describe("ConditionBorrowAmountIsDust Unit Test", 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 userWallet;
let userAddress;
let getCdps;
let dssCdpManager;
let instaList;
let instaIndex;
let DAI;
let conditionBorrowAmountIsDust;
let dsa;
let cdpAId;
let amountToBorrow;
beforeEach(async function () {
// Deploy contract dependencies
await deployments.fixture();
// Get Test Wallet for local testnet
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
hre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
hre.network.config.InstaList
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
hre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
hre.network.config.DssCdpManager
);
DAI = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
// ========== Test Setup ============
conditionBorrowAmountIsDust = await ethers.getContract(
"ConditionBorrowAmountIsDust"
);
// Create DeFi Smart Account
const dsaAccountCount = await instaList.accounts();
await expect(instaIndex.build(userAddress, 1, userAddress)).to.emit(
instaIndex,
"LogAccountCreated"
);
const dsaID = dsaAccountCount.add(1);
await expect(await instaList.accounts()).to.be.equal(dsaID);
// Instantiate the DSA
dsa = await ethers.getContractAt(
InstaAccount.abi,
await instaList.accountAddr(dsaID)
);
// Create/Deposit/Borrow a Vault for ETH-A
let openVault = await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
});
await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpAId = String(cdps.ids[0]);
expect(cdps.ids[0].isZero()).to.be.false;
});
it("#1: ok should return Ok if we borrow more than the dust limit", async function () {
// Steps :
// 1 - Deposit.
// 2 - Borrow.
// 3 - Test if vault ETH-B will not have dust borrow.
amountToBorrow = ethers.utils.parseUnits("500", 18);
const amountToDeposit = ethers.utils.parseUnits("2", 18);
//#region Deposit
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpAId, amountToDeposit, 0, 0],
}),
],
userAddress,
{
value: amountToDeposit,
}
);
//#endregion Deposit
//#region Borrow
await dsa.cast(
[hre.network.config.ConnectMaker],
[
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpAId, amountToBorrow, 0, 0],
}),
],
userAddress
);
expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow);
//#endregion Borrow
const conditionData = await conditionBorrowAmountIsDust.getConditionData(
dsa.address,
cdpAId,
0,
"ETH-B"
);
expect(
await conditionBorrowAmountIsDust.ok(0, conditionData, 0)
).to.be.equal("OK");
});
it("#2: borrowAmountIsDustExplicit should return false if we borrow less than the dust limit", async function () {
amountToBorrow = ethers.utils.parseUnits("200", 18); // dust limit is 500 DAI (first Dec 2020) for ETH-B
expect(
await conditionBorrowAmountIsDust.borrowAmountIsDustExplicit(
0,
amountToBorrow,
"ETH-B"
)
).to.be.true;
});
});