Unit testing new smart contract.

This commit is contained in:
Shivva 2020-10-20 01:16:23 +02:00 committed by Luis Schliesske
parent e6a37987b8
commit d04f327159
14 changed files with 1391 additions and 39 deletions

View File

@ -25,6 +25,7 @@ module.exports = {
unlocked_accounts: [INSTA_MASTER],
// Custom
GelatoCore: "0x1d681d76ce96E4d70a88A00EBbcfc1E47808d0b8",
GelatoGasPriceOracle: "0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C",
InstaMaster: INSTA_MASTER,
InstaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723",
InstaList: "0x4c8a1BEb8a87765788946D6B19C6C6355194AbEb",
@ -37,9 +38,11 @@ module.exports = {
ConnectCompound: "0x07F81230d73a78f63F0c2A3403AD281b067d28F8",
ConnectInstaPool: "0xCeF5f3c402d4fef76A038e89a4357176963e1464",
MakerResolver: "0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5",
CompoundResolver: "0x1f22D77365d8BFE3b901C33C83C01B584F946617",
DAI: "0x6b175474e89094c44da98b954eedeac495271d0f",
DAI_UNISWAP: "0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667",
CDAI: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",
CETH: "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
DssCdpManager: "0x5ef30b9986345249bc32d8928B7ee64DE9435E39",
GetCdps: "0x36a724Bd100c39f0Ea4D3A20F7097eE01A8Ff573",
ProviderModuleDSA: "0x0C25452d20cdFeEd2983fa9b9b9Cf4E81D6f2fE2",

View File

@ -293,7 +293,7 @@ contract ConnectGelatoDebtBridge is ConnectGelatoDebtBridgeResolver {
// Constant name must be in capitalized SNAKE_CASE
// solhint-disable-next-line
string public constant override name = "GelatoDebtBridge-v1.0";
uint256 public constant GASLIMIT = 2000000; // To Define
uint256 public constant GASLIMIT = 1933090 + (19331 * 2); // 1933080 + ~2% (Estimated Value)
address public immutable oracleAggregator;
constructor(uint256 _iD, address _oracleAggregator) public {
@ -315,7 +315,7 @@ contract ConnectGelatoDebtBridge is ConnectGelatoDebtBridgeResolver {
string memory _pair,
uint256 _getID,
uint256 _setID
) external {
) external payable {
(
uint256 paybackAmount,
uint256 collateralToWithdraw,

View File

@ -59,7 +59,7 @@ contract ConnectGelatoProviderPayment is ConnectGelatoProviderPaymentHelper {
uint256 _amt,
uint256 _getID,
uint256 _setID
) public {
) public payable {
// Desable linter for too long require statement
// solhint-disable-next-line
require(

View File

@ -0,0 +1,37 @@
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "../ConnectGelatoDEbtBridge.sol";
contract ConnectGelatoDebtBridgeMock is ConnectGelatoDebtBridge {
constructor(uint256 _iD, address _oracleAggregator)
public
ConnectGelatoDebtBridge(_iD, _oracleAggregator)
{}
function wcollateralToWithdraw(
uint256 _p1LiqRatio,
uint256 _p2LiqRatio,
uint256 _col,
uint256 _bor,
uint256 _colPrice
) public pure returns (uint256) {
return
_wcollateralToWithdraw(
_p1LiqRatio,
_p2LiqRatio,
_col,
_bor,
_colPrice
);
}
function wborrowedTokenToPayback(
uint256 _p1LiqRatio,
uint256 _p2LiqRatio,
uint256 _col,
uint256 _bor
) public pure returns (uint256) {
return _wborrowedTokenToPayback(_p1LiqRatio, _p2LiqRatio, _col, _bor);
}
}

View File

@ -10,9 +10,9 @@ interface IMakerPriceFeed {
}
contract OracleAggregatorStorage {
mapping(string => address) internal _makerOracle;
mapping(string => address) internal _compoundOracle;
mapping(string => address) internal _chainlinkOracle;
mapping(string => address) public makerOracle;
mapping(string => address) public compoundOracle;
mapping(string => address) public chainlinkOracle;
}
// 0x729D19f657BD0614b4985Cf1D82531c67569197B for ETH/USD medianizer it return value in wad standard.
@ -37,10 +37,10 @@ contract OracleAggregator is OracleAggregatorStorage, Ownable, DSMath {
// Desable linter for too long require statement
// solhint-disable-next-line
require(
_makerOracle[_pair] == address(0x0),
makerOracle[_pair] == address(0x0),
"OracleAggregator.Maker: Oracle already set."
);
_makerOracle[_pair] = _oracleAddress;
makerOracle[_pair] = _oracleAddress;
}
function getMakerTokenPrice(string memory _pair)
@ -48,9 +48,15 @@ contract OracleAggregator is OracleAggregatorStorage, Ownable, DSMath {
view
returns (uint256)
{
// Desable linter for too long require statement
// solhint-disable-next-line
require(
makerOracle[_pair] != address(0x0),
"OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported."
);
if (mockMode) {
return mockValue;
}
return uint256(IMakerPriceFeed(_makerOracle[_pair]).read());
return uint256(IMakerPriceFeed(makerOracle[_pair]).read());
}
}

View File

@ -3,26 +3,7 @@
"abi": [
{
"inputs": [],
"name": "_acceptAdmin",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "newPendingAdmin",
"type": "address"
}
],
"name": "_setPendingAdmin",
"name": "accrueInterest",
"outputs": [
{
"internalType": "uint256",
@ -285,6 +266,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalBorrows",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalBorrowsCurrent",
@ -298,6 +292,32 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "totalReserves",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{

View File

@ -0,0 +1,22 @@
{
"contractName": "GelatoGasPriceOracle",
"abi": [
{
"inputs": [],
"name": "latestAnswer",
"outputs": [
{
"internalType": "int256",
"name": "",
"type": "int256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x",
"deployedBytecode": "0x",
"linkReferences": {},
"deployedLinkReferences": {}
}

File diff suppressed because one or more lines are too long

View File

@ -21,13 +21,86 @@ const DssCdpManager = require("../pre-compiles/DssCdpManager.json");
const GetCdps = require("../pre-compiles/GetCdps.json");
const IERC20 = require("../pre-compiles/IERC20.json");
const CTokenInterface = require("../pre-compiles/CTokenInterface.json");
const GelatoGasPriceOracle = require("../pre-compiles/GelatoGasPriceOracle.json");
const CompoundResolver = require("../pre-compiles/InstaCompoundResolver.json");
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
const GAS_LIMIT = "4000000";
const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei");
const WAD = ethers.utils.parseUnits("1", 18);
// #endregion
//#region Mock Math Function
let wdiv = (x, y) => {
return x.mul(WAD).add(y.div(2)).div(y);
};
let wmul = (x, y) => {
return x.mul(y).add(WAD.div(2)).div(WAD);
};
let wcollateralToWithdraw = (
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
borrowedToken,
collateralPrice
) => {
//#region CALCULATION REPLICATION
let expectedColToWithdraw = wmul(
wmul(wantedLiquidationRatioOnProtocol1, wantedLiquidationRatioOnProtocol2),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedColToWithdraw = expectedColToWithdraw.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedColToWithdraw = wdiv(
expectedColToWithdraw,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedColToWithdraw = collateral.sub(expectedColToWithdraw); // doc ref : e_2 - ((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
// Extra step to convert back to col type
expectedColToWithdraw = wdiv(expectedColToWithdraw, collateralPrice);
//#endregion
return expectedColToWithdraw;
};
let wborrowedTokenToPayback = (
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
borrowedToken
) => {
//#region CALCULATION REPLICATION
let expectedBorToPayBack = wmul(
wmul(wantedLiquidationRatioOnProtocol1, wantedLiquidationRatioOnProtocol2),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedBorToPayBack = expectedBorToPayBack.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedBorToPayBack = wdiv(
expectedBorToPayBack,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedBorToPayBack = wmul(
wdiv(ethers.utils.parseUnits("1", 18), wantedLiquidationRatioOnProtocol1),
expectedBorToPayBack
); // doc ref : (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
expectedBorToPayBack = borrowedToken.sub(expectedBorToPayBack); // doc ref : d_2 - (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
//#endregion
return expectedBorToPayBack;
};
//#endregion
describe("Debt Bridge with External Provider", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
@ -53,8 +126,11 @@ describe("Debt Bridge with External Provider", function () {
let daiToken;
let gelatoCore;
let cDaiToken;
let cEthToken;
let instaMaster;
let instaConnectors;
let gelatoGasPriceOracle;
let compoundResolver;
// Contracts to deploy and use for local testing
let conditionMakerVaultIsSafe;
@ -126,10 +202,22 @@ describe("Debt Bridge with External Provider", function () {
CTokenInterface.abi,
bre.network.config.CDAI
);
cEthToken = await ethers.getContractAt(
CTokenInterface.abi,
bre.network.config.CETH
);
instaConnectors = await ethers.getContractAt(
InstaConnector.abi,
bre.network.config.InstaConnectors
);
gelatoGasPriceOracle = await ethers.getContractAt(
GelatoGasPriceOracle.abi,
bre.network.config.GelatoGasPriceOracle
);
compoundResolver = await ethers.getContractAt(
CompoundResolver.abi,
bre.network.config.CompoundResolver
);
// instaEvent = await ethers.getContractAt(
// InstaEvent.abi,
// bre.network.config.InstaEvent
@ -449,7 +537,7 @@ describe("Debt Bridge with External Provider", function () {
//#endregion
});
it("Use Maker Compound refinancing if the maker vault become unsafe after a market move.", async function () {
it("#1: Use Maker Compound refinancing if the maker vault become unsafe after a market move.", async function () {
// User Actions
// Step 1 : User create a DeFi Smart Account
// Step 2 : User open a Vault, put some ether on it and borrow some dai
@ -566,6 +654,10 @@ describe("Debt Bridge with External Provider", function () {
// It will be done through a algorithm that will optimize the
// total borrow rate.
let wantedLiquidationRatioOnProtocol1 = ethers.utils.parseUnits("3", 18);
let wantedLiquidationRatioOnProtocol2 = ethers.utils.parseUnits("19", 17);
let currencyPair = "ETH/USD";
const debtBridgeCondition = new GelatoCoreLib.Condition({
inst: conditionMakerVaultIsSafe.address,
data: await conditionMakerVaultIsSafe.getConditionData(
@ -585,9 +677,9 @@ describe("Debt Bridge with External Provider", function () {
functionname: "debtBridgeMakerToCompound",
inputs: [
cdpId,
ethers.utils.parseUnits("3", 18),
ethers.utils.parseUnits("18", 17),
"ETH/USD",
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
currencyPair,
0,
0,
],
@ -748,7 +840,37 @@ describe("Debt Bridge with External Provider", function () {
// will execute the user's task to make the user position safe
// by a debt refinancing in compound.
//#region EXPECTED OUTCOME
let latestPrice = await oracleAggregator.getMakerTokenPrice(currencyPair);
let fees = ethers.utils
.parseUnits("2000000", 0)
.mul(await gelatoGasPriceOracle.latestAnswer());
let debt = await connectGelatoDebtBridge.getMakerVaultDebt(cdpId);
let collateral = wmul(
await connectGelatoDebtBridge.getMakerVaultCollateralBalance(cdpId),
latestPrice
).sub(fees);
let expectedColWithdrawAmount = wcollateralToWithdraw(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
debt,
latestPrice
);
let expectedBorAmountToPayBack = wborrowedTokenToPayback(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
debt
);
//#endregion
let providerBalanceBeforeExecution = await providerWallet.getBalance();
await expect(
connectedGelatoCore.exec(taskReceipt, {
gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei)
@ -757,15 +879,47 @@ describe("Debt Bridge with External Provider", function () {
).to.emit(gelatoCore, "LogExecSuccess");
let providerBalanceAfterExecution = await providerWallet.getBalance();
expect(providerBalanceAfterExecution).to.be.gt(
providerBalanceBeforeExecution
);
const amtOfBorrowedDAIOnCompound = (
await cDaiToken.getAccountSnapshot(dsa.address)
)[2];
expect(amtOfBorrowedDAIOnCompound).to.be.lt(
ethers.utils.parseUnits("1000", 18)
); // Check the borrow amount
// compound position of DSA on cDai and cEth
let compoundPosition = await compoundResolver.getCompoundData(dsa.address, [
cDaiToken.address,
cEthToken.address,
]);
// https://compound.finance/docs/ctokens#exchange-rate
// calculate cEth/ETH rate to convert back cEth to ETH
// for comparing with the withdrew Ether to the deposited one.
let exchangeRateCethToEth = (await cEthToken.getCash())
.add(await cEthToken.totalBorrows())
.sub(await cEthToken.totalReserves())
.div(await cEthToken.totalSupply());
expect(
expectedBorAmountToPayBack.sub(
compoundPosition[0].borrowBalanceStoredUser
)
).to.be.gt(ethers.utils.parseUnits("1", 0));
expect(
expectedColWithdrawAmount.sub(
compoundPosition[1].balanceOfUser.mul(exchangeRateCethToEth)
)
).to.be.gt(ethers.utils.parseUnits("1", 0));
expect(
expectedBorAmountToPayBack.sub(
compoundPosition[0].borrowBalanceStoredUser
)
).to.be.lt(ethers.utils.parseUnits("1", 16));
expect(
expectedColWithdrawAmount.sub(
compoundPosition[1].balanceOfUser.mul(exchangeRateCethToEth)
)
).to.be.lt(ethers.utils.parseUnits("1", 14));
// DSA contain 1000 DAI
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
ethers.utils.parseUnits("1000", 18)
);

View File

@ -0,0 +1,182 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
// #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("ConditionMakerVaultIsSafe gelato condition contract unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let userWallet;
let userAddress;
let getCdps;
let dssCdpManager;
let instaList;
let instaIndex;
let daiToken;
let conditionMakerVaultIsSafe;
let oracleAggregator;
let cdpId;
let dsa;
before(async function () {
// Get Test Wallet for local testnet
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
// Ganache default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
bre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
bre.network.config.DssCdpManager
);
daiToken = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
// ========== Test Setup ============
const OracleAggregator = await ethers.getContractFactory(
"OracleAggregator"
);
oracleAggregator = await OracleAggregator.deploy();
await oracleAggregator.deployed();
const ConditionMakerVaultIsSafe = await ethers.getContractFactory(
"ConditionMakerVaultIsSafe"
);
conditionMakerVaultIsSafe = await ConditionMakerVaultIsSafe.deploy(
oracleAggregator.address
);
await conditionMakerVaultIsSafe.deployed();
// 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
const openVault = await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
});
await dsa.cast([bre.network.config.ConnectMaker], [openVault], userAddress);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpId = String(cdps.ids[0]);
expect(cdps.ids[0].isZero()).to.be.false;
await dsa.cast(
[bre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0],
}),
],
userAddress,
{
value: ethers.utils.parseEther("10"),
}
);
await dsa.cast(
[bre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpId, ethers.utils.parseUnits("1000", 18), 0, 0],
}),
],
userAddress
);
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
ethers.utils.parseEther("1000")
);
// Add ETH/USD Maker Medianizer in the Oracle Aggregator
await oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
);
});
it("#1: ok should return NotOKMakerVaultIsSafe when the ETH/USD price is above the defined limit", async function () {
let data = await conditionMakerVaultIsSafe.getConditionData(
cdpId,
"ETH/USD",
ethers.utils.parseUnits("30", 17)
);
expect(await conditionMakerVaultIsSafe.ok(0, data, 0)).to.be.equal(
"NotOKMakerVaultIsSafe"
);
});
it("#2: ok should return OK when the ETH/USD price is lower than the defined limit", async function () {
let data = await conditionMakerVaultIsSafe.getConditionData(
cdpId,
"ETH/USD",
ethers.utils.parseUnits("30", 17)
);
//#region Mock Part
oracleAggregator.mock(true, ethers.utils.parseUnits("299", 18));
//#endregion
expect(await conditionMakerVaultIsSafe.ok(0, data, 0)).to.be.equal(
"NotOKMakerVaultIsSafe"
);
});
});

View File

@ -0,0 +1,281 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
// #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 ConnectBasic = require("../pre-compiles/ConnectBasic.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");
const InstaConnector = require("../pre-compiles/InstaConnectors.json");
const ConnectGelatoProviderPaymentABI = require("../artifacts/ConnectGelatoProviderPayment.json");
// #endregion
describe("Gelato Provider Payment Connector unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let userWallet;
let userAddress;
let providerWallet;
let providerAddress;
let instaList;
let instaIndex;
let daiToken;
let instaConnectors;
let instaMaster;
let connectBasic;
let getCdps;
let dssCdpManager;
let connectGelatoProviderPayment;
let dsa;
let cdpId;
before(async function () {
// Get Test Wallet for local testnet
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
[, providerWallet] = await ethers.getSigners();
providerAddress = await providerWallet.getAddress();
instaMaster = await ethers.provider.getSigner(
bre.network.config.InstaMaster
);
// Ganache default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
);
connectBasic = await ethers.getContractAt(
ConnectBasic.abi,
bre.network.config.ConnectBasic
);
instaConnectors = await ethers.getContractAt(
InstaConnector.abi,
bre.network.config.InstaConnectors
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
bre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
bre.network.config.DssCdpManager
);
daiToken = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
// ========== Test Setup ============
const connectorLength = await instaConnectors.connectorLength();
const connectorId = connectorLength.add(1);
const ConnectGelatoProviderPayment = await ethers.getContractFactory(
"ConnectGelatoProviderPayment"
);
connectGelatoProviderPayment = await ConnectGelatoProviderPayment.deploy(
connectorId
);
connectGelatoProviderPayment.deployed();
await instaConnectors
.connect(instaMaster)
.enable(connectGelatoProviderPayment.address);
await userWallet.sendTransaction({
to: bre.network.config.InstaMaster,
value: ethers.utils.parseEther("0.1"),
});
expect(
await instaConnectors.isConnector([connectGelatoProviderPayment.address])
).to.be.true;
// ========== Create DeFi Smart Account for User 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)
);
});
it("#1: payProvider should pay to Provider 300x10^18 token dai", async function () {
let providerDAIBalanceBefore = await daiToken.balanceOf(providerAddress);
await dsa.cast(
[bre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
}),
],
userAddress
);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpId = String(cdps.ids[0]);
expect(cdps.ids[0].isZero()).to.be.false;
await dsa.cast(
[bre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0],
}),
],
userAddress,
{
value: ethers.utils.parseEther("10"),
}
);
await dsa.cast(
[bre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpId, ethers.utils.parseUnits("1000", 18), 0, 0],
}),
],
userAddress
);
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
ethers.utils.parseEther("1000")
);
await dsa.cast(
[connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
functionname: "payProvider",
inputs: [
providerAddress,
daiToken.address,
ethers.utils.parseUnits("300", 18),
0,
0,
],
}),
],
userAddress
);
expect(await daiToken.balanceOf(providerAddress)).to.be.equal(
providerDAIBalanceBefore.add(ethers.utils.parseUnits("300", 18))
);
});
it("#2: payProvider should pay to Provider 1 ether", async function () {
let providerBalanceBefore = await providerWallet.getBalance();
await dsa.cast(
[connectBasic.address, connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "deposit",
inputs: [
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
ethers.utils.parseEther("1"),
0,
"105",
],
}),
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
functionname: "payProvider",
inputs: [
providerAddress,
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
0,
"105",
0,
],
}),
],
userAddress,
{
value: ethers.utils.parseEther("1"),
}
);
expect(await providerWallet.getBalance()).to.be.equal(
providerBalanceBefore.add(ethers.utils.parseEther("1"))
);
});
it("#3: payProvider should return error message ConnectGelatoProviderPayment.payProvider:INVALIDADDESS when provider is Zero Address", async function () {
expect(
dsa.cast(
[connectBasic.address, connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "deposit",
inputs: [
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
ethers.utils.parseEther("1"),
0,
"105",
],
}),
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
functionname: "payProvider",
inputs: [
ethers.constants.AddressZero,
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
0,
"105",
0,
],
}),
],
userAddress,
{
value: ethers.utils.parseEther("1"),
}
)
).to.be.revertedWith(
"ConnectGelatoProviderPayment.payProvider:INVALIDADDESS."
);
});
});

View File

@ -0,0 +1,51 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
describe("Oracle Aggregator unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let oracleAggregator;
before(async function () {
const OracleAggregator = await ethers.getContractFactory(
"OracleAggregator"
);
oracleAggregator = await OracleAggregator.deploy();
oracleAggregator.deployed();
});
it("#1: addOracle should add a maker medianizer for a currencyPair", async function () {
await oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
);
expect(await oracleAggregator.makerOracle("ETH/USD")).to.be.equal(
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
);
});
it("#2: addOracle should revert when adding a maker medianizer and for this currency pair it was been already added", async function () {
expect(
oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
)
).to.be.revertedWith("OracleAggregator.Maker: Oracle already set.");
});
it("#3: getMakerTokenPrice should return ETH/USD prize", async function () {
expect((await oracleAggregator.getMakerTokenPrice("ETH/USD")).isZero()).to
.be.false;
});
it("#4: getMakerTokenPrice should return OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported. when currencyPair are not supported / not been added", async function () {
expect(oracleAggregator.getMakerTokenPrice("ETH/DAI")).to.be.revertedWith(
"OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported."
);
});
});

View File

@ -0,0 +1,195 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const GelatoCoreLib = require("@gelatonetwork/core");
// #region Contracts ABI
const ConnectAuth = require("../pre-compiles/ConnectAuth.json");
const InstaList = require("../pre-compiles/InstaList.json");
const InstaAccount = require("../pre-compiles/InstaAccount.json");
const InstaIndex = require("../pre-compiles/InstaIndex.json");
const InstaConnectors = require("../pre-compiles/InstaConnectors.json");
const ConnectGelatoProviderPaymentABI = require("../artifacts/ConnectGelatoProviderPayment.json");
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
// #endregion
describe("Provider Module unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let providerModuleDSA;
let connectGelatoProviderPayment;
let instaIndex;
let gelatoCore;
let instaConnectors;
let instaList;
let userWallet;
let userAddress;
let providerWallet;
let providerAddress;
let dsa;
before(async function () {
// Get Test Wallet for local testnet
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
[, providerWallet] = await ethers.getSigners();
providerAddress = await providerWallet.getAddress();
// Ganache default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
/////////////////// Get Deployed Contract //////////////////
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
);
instaConnectors = await ethers.getContractAt(
InstaConnectors.abi,
bre.network.config.InstaConnectors
);
gelatoCore = await ethers.getContractAt(
GelatoCoreLib.GelatoCore.abi,
bre.network.config.GelatoCore
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
);
////////////////// Deploy Needed Contracts ////////////////////////
const connectorLength = await instaConnectors.connectorLength();
const connectorId = connectorLength.add(1);
const ConnectGelatoProviderPayment = await ethers.getContractFactory(
"ConnectGelatoProviderPayment"
);
connectGelatoProviderPayment = await ConnectGelatoProviderPayment.deploy(
connectorId
);
await connectGelatoProviderPayment.deployed();
const ProviderModuleDSA = await ethers.getContractFactory(
"ProviderModuleDSA"
);
providerModuleDSA = await ProviderModuleDSA.deploy(
instaIndex.address,
gelatoCore.address,
connectGelatoProviderPayment.address
);
await providerModuleDSA.deployed();
///////////////// Setup /////////////////////////
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)
);
});
// Uncomment when ethers bug will be solved. revertedWith produce the following during call "AssertionError: Expected transaction to be reverted"
// it("isProvided should revert due to none listing of userProxy", async function () {
// expect(
// providerModuleDSA.isProvided(
// ethers.constants.AddressZero,
// ethers.constants.AddressZero,
// [[], [], 0, 0]
// )
// ).to.be.revertedWith("ProviderModuleDSA.isProvided:InvalidUserProxy");
// });
// it("isProvided should revert due to lack of right of gelatoCore to act on the dsa behalf", async function () {
// expect(
// providerModuleDSA.isProvided(
// dsa.address,
// ethers.constants.AddressZero,
// [[], [], 0, 0]
// )
// ).to.be.revertedWith("ProviderModuleDSA.isProvided:GelatoCoreNotAuth");
// });
it("#1: isProvided should return OK", async function () {
// Give authorization to Gelato on user DSA
await dsa.cast(
[bre.network.config.ConnectAuth],
[
await bre.run("abi-encode-withselector", {
abi: ConnectAuth.abi,
functionname: "add",
inputs: [gelatoCore.address],
}),
],
userAddress
);
expect(await dsa.isAuth(gelatoCore.address)).to.be.true;
expect(
await providerModuleDSA.isProvided(
dsa.address,
ethers.constants.AddressZero,
[[], [], 0, 0]
)
).to.be.equal("OK");
});
it("#2: execPayload should return payload with the right provider", async function () {
// Task creation for sending to execPayload
let payProvider = new GelatoCoreLib.Action({
addr: connectGelatoProviderPayment.address,
data: await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
functionname: "payProvider",
inputs: [ethers.constants.AddressZero, ETH, 0, "105", 0],
}),
operation: GelatoCoreLib.Operation.Delegatecall,
});
const task = new GelatoCoreLib.Task({
conditions: [],
actions: [payProvider],
});
let result = await providerModuleDSA.execPayload(
0,
ethers.constants.AddressZero,
providerAddress,
task,
0
);
//#region retrieving the replaced provider address from the payload
let abi = new ethers.utils.AbiCoder();
let datas = abi.decode(
["address[]", "bytes[]", "address"],
ethers.utils.hexDataSlice(result.payload, 4)
)[1];
let paymentReceivingAddress = abi.decode(
["address", "address", "uint256", "uint256", "uint256"],
ethers.utils.hexDataSlice(datas[0], 4)
)[0];
//#endregion
expect(paymentReceivingAddress).to.be.equal(providerAddress);
});
});

View File

@ -0,0 +1,141 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const WAD = ethers.utils.parseUnits("1", 18);
//#region DSMath function
let wdiv = (x, y) => {
return x.mul(WAD).add(y.div(2)).div(y);
};
let wmul = (x, y) => {
return x.mul(y).add(WAD.div(2)).div(WAD);
};
//#endregion
describe("Gelato Debt Bridge Connector unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let connectGelatoDebtBridge;
before(async function () {
const ConnectGelatoDebtBridge = await ethers.getContractFactory(
"ConnectGelatoDebtBridgeMock"
);
connectGelatoDebtBridge = await ConnectGelatoDebtBridge.deploy(
0,
ethers.constants.AddressZero
);
connectGelatoDebtBridge.deployed();
});
it("#1: _wcollateralToWithdraw should return the amount of collateral to withdraw on protocol 1 and to put on protocol 2", async function () {
// 3 times more collateral than borrowed amount in protocol 1
let wantedLiquidationRatioOnProtocol1 = ethers.utils.parseUnits("3", 18);
// 1.5 times more collateral than borrowed amount in protocol 2
let wantedLiquidationRatioOnProtocol2 = ethers.utils.parseUnits("15", 17);
// The amount of collateral locked
let col = ethers.utils.parseUnits("1", 18);
// The amount of borrowed token
let borrowedToken = ethers.utils.parseUnits("100", 18);
// how much one collateral is worth on borrowed token
let collateralPrice = ethers.utils.parseUnits("250", 18);
// the amount of collateral in borrowed token
let collateral = col
.mul(collateralPrice)
.div(ethers.utils.parseUnits("1", 18)); // div to have everything in wad standard
// Check this document https://drive.google.com/file/d/1OV3ZbJPd2Yr-3l0rst6tK3ycfhhg6Nfh/view?usp=sharing for more details one the used formula
//#region CALCULATION REPLICATION
let expectedColToWithdraw = wmul(
wmul(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2
),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedColToWithdraw = expectedColToWithdraw.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedColToWithdraw = wdiv(
expectedColToWithdraw,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedColToWithdraw = collateral.sub(expectedColToWithdraw); // doc ref : e_2 - ((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
// Extra step to convert back to col type
expectedColToWithdraw = wdiv(expectedColToWithdraw, collateralPrice);
//#endregion
expect(
await connectGelatoDebtBridge.wcollateralToWithdraw(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
borrowedToken,
collateralPrice
)
).to.be.equal(expectedColToWithdraw);
});
it("#2: _wborrowedTokenToPayback should return the amount of borrowed token to pay back on protocol 1", async function () {
// 3 times more collateral than borrowed amount in protocol 1
let wantedLiquidationRatioOnProtocol1 = ethers.utils.parseUnits("3", 18);
// 1.5 times more collateral than borrowed amount in protocol 2
let wantedLiquidationRatioOnProtocol2 = ethers.utils.parseUnits("15", 17);
// The amount of collateral locked
let col = ethers.utils.parseUnits("1", 18);
// The amount of borrowed token
let borrowedToken = ethers.utils.parseUnits("100", 18);
// how much one collateral is worth on borrowed token
let collateralPrice = ethers.utils.parseUnits("250", 18);
// the amount of collateral in borrowed token
let collateral = col
.mul(collateralPrice)
.div(ethers.utils.parseUnits("1", 18)); // div to have everything in wad standard
// Check this document https://drive.google.com/file/d/1OV3ZbJPd2Yr-3l0rst6tK3ycfhhg6Nfh/view?usp=sharing for more details one the used formula
//#region CALCULATION REPLICATION
let expectedBorToPayBack = wmul(
wmul(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2
),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedBorToPayBack = expectedBorToPayBack.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedBorToPayBack = wdiv(
expectedBorToPayBack,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedBorToPayBack = wmul(
wdiv(ethers.utils.parseUnits("1", 18), wantedLiquidationRatioOnProtocol1),
expectedBorToPayBack
); // doc ref : (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
expectedBorToPayBack = borrowedToken.sub(expectedBorToPayBack); // doc ref : d_2 - (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
//#endregion
expect(
await connectGelatoDebtBridge.wborrowedTokenToPayback(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
collateral,
borrowedToken
)
).to.be.equal(expectedBorToPayBack);
});
});