mirror of
https://github.com/Instadapp/Gelato-automations.git
synced 2024-07-29 22:28:07 +00:00
Add debt ceiling and dust check (#92)
* feat: conditions debt ceiling and dust
This commit is contained in:
parent
2d8581141e
commit
2a2158f89b
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"];
|
|
@ -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"];
|
|
@ -53,6 +53,10 @@ module.exports = {
|
|||
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
|
||||
blockNumber: 11346751,
|
||||
},
|
||||
// Accounts
|
||||
accounts: {
|
||||
accountsBalance: "1000000000000000000000000",
|
||||
},
|
||||
// Custom
|
||||
...mainnetDeployments,
|
||||
},
|
||||
|
|
547
test/unit/conditions/3_ConditionDebtCeilingIsReached.test.js
Normal file
547
test/unit/conditions/3_ConditionDebtCeilingIsReached.test.js
Normal 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");
|
||||
});
|
||||
});
|
175
test/unit/conditions/4_ConditionBorrowAmountIsDust.test.js
Normal file
175
test/unit/conditions/4_ConditionBorrowAmountIsDust.test.js
Normal 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;
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user