Skip to content

Commit

Permalink
test(hardhat): simplify gas tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Rubilmax committed Nov 16, 2023
1 parent 0ba17fa commit 5f2a9e6
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 177 deletions.
19 changes: 14 additions & 5 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import "@nomicfoundation/hardhat-foundry";
import "@nomicfoundation/hardhat-network-helpers";
import "@typechain/hardhat";

import blueConfig from "./lib/morpho-blue/hardhat.config";

dotenv.config();

Expand All @@ -23,12 +22,22 @@ const config: HardhatUserConfig = {
gasPrice: 0,
initialBaseFeePerGas: 0,
allowBlocksWithSameTimestamp: true,
accounts: {
count: 201, // must be odd
},
},
},
solidity: blueConfig.solidity,
solidity: {
compilers: [
{
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
viaIR: true,
},
},
],
},
mocha: {
timeout: 3000000,
},
Expand Down
13 changes: 0 additions & 13 deletions src/mocks/ERC20Mock.sol

This file was deleted.

17 changes: 0 additions & 17 deletions src/mocks/MorphoMock.sol

This file was deleted.

12 changes: 0 additions & 12 deletions src/mocks/OracleMock.sol

This file was deleted.

File renamed without changes.
156 changes: 33 additions & 123 deletions test/hardhat/irm/Irm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { AbiCoder, MaxUint256, keccak256, toBigInt } from "ethers";
import { AbiCoder, keccak256, toBigInt } from "ethers";
import hre from "hardhat";
import _range from "lodash/range";
import { ERC20Mock, AdaptativeCurveIrm, MorphoMock, OracleMock } from "types";
import { MarketParamsStruct } from "types/src/mocks/MorphoMock";
import { AdaptativeCurveIrm } from "types";
import { MarketParamsStruct } from "types/lib/morpho-blue/src/interfaces/IIrm";

import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time";

// Without the division it overflows.
const initBalance = MaxUint256 / 10000000000000000n;
const oraclePriceScale = 1000000000000000000000000000000000000n;

let seed = 42;
const random = () => {
seed = (seed * 16807) % 2147483647;
Expand All @@ -35,151 +31,65 @@ const randomForwardTimestamp = async () => {
const block = await hre.ethers.provider.getBlock("latest");
const elapsed = random() < 1 / 2 ? 0 : (1 + Math.floor(random() * 100)) * 12; // 50% of the time, don't go forward in time.

const newTimestamp = block!.timestamp + elapsed;

await setNextBlockTimestamp(block!.timestamp + elapsed);

return newTimestamp;
};

describe("irm", () => {
let admin: SignerWithAddress;
let suppliers: SignerWithAddress[];
let borrowers: SignerWithAddress[];

let morpho: MorphoMock;
let borrowable: ERC20Mock;
let collateral: ERC20Mock;
let oracle: OracleMock;
let irm: AdaptativeCurveIrm;

let marketParams: MarketParamsStruct;
let id: Buffer;

const updateMarket = (newMarket: Partial<MarketParamsStruct>) => {
marketParams = { ...marketParams, ...newMarket };
id = identifier(marketParams);
};

beforeEach(async () => {
const allSigners = await hre.ethers.getSigners();

const users = allSigners.slice(0, -1);

[admin] = allSigners.slice(-1);
suppliers = users.slice(0, users.length / 2);
borrowers = users.slice(users.length / 2);

const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", admin);

borrowable = await ERC20MockFactory.deploy("DAI", "DAI");
collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC");

const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", admin);

oracle = await OracleMockFactory.deploy();

await oracle.setPrice(oraclePriceScale);

const MorphoFactory = await hre.ethers.getContractFactory("MorphoMock", admin);

morpho = await MorphoFactory.deploy(admin.address);

const morphoAddress = await morpho.getAddress();
[admin] = await hre.ethers.getSigners();

const AdaptativeCurveIrmFactory = await hre.ethers.getContractFactory("AdaptativeCurveIrm", admin);

irm = await AdaptativeCurveIrmFactory.deploy(
morphoAddress,
await admin.getAddress(),
4000000000000000000n,
1585489599188n,
900000000000000000n,
317097919n,
);

const borrowableAddress = await borrowable.getAddress();
const collateralAddress = await collateral.getAddress();
const oracleAddress = await oracle.getAddress();
const irmAddress = await irm.getAddress();

updateMarket({
borrowableToken: borrowableAddress,
collateralToken: collateralAddress,
oracle: oracleAddress,
marketParams = {
// Non-zero address to include calldata gas cost.
collateralToken: irmAddress,
loanToken: irmAddress,
oracle: irmAddress,
irm: irmAddress,
lltv: BigInt.WAD / 2n + 1n,
});

await morpho.enableLltv(marketParams.lltv);
await morpho.enableIrm(marketParams.irm);
await morpho.createMarket(marketParams);

for (const user of users) {
await borrowable.setBalance(user.address, initBalance);
await borrowable.connect(user).approve(morphoAddress, MaxUint256);
await collateral.setBalance(user.address, initBalance);
await collateral.connect(user).approve(morphoAddress, MaxUint256);
}
lltv: 0,
};

hre.tracer.nameTags[morphoAddress] = "Morpho";
hre.tracer.nameTags[collateralAddress] = "Collateral";
hre.tracer.nameTags[borrowableAddress] = "Borrowable";
hre.tracer.nameTags[oracleAddress] = "Oracle";
hre.tracer.nameTags[irmAddress] = "IRM";
});

it("should simulate gas cost [main]", async () => {
for (let i = 0; i < suppliers.length; ++i) {
logProgress("main", i, suppliers.length);

const supplier = suppliers[i];

let assets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100));

await randomForwardTimestamp();

await morpho.connect(supplier).supply(marketParams, assets, 0, supplier.address, "0x");

await randomForwardTimestamp();

await morpho.connect(supplier).withdraw(marketParams, assets / 2n, 0, supplier.address, supplier.address);

const borrower = borrowers[i];

const market = await morpho.market(id);
const liquidity = market.totalSupplyAssets - market.totalBorrowAssets;

assets = assets.min(liquidity / 2n);

await randomForwardTimestamp();

await morpho.connect(borrower).supplyCollateral(marketParams, assets, borrower.address, "0x");

await randomForwardTimestamp();

await morpho.connect(borrower).borrow(marketParams, assets / 2n, 0, borrower.address, borrower.address);

await randomForwardTimestamp();

await morpho.connect(borrower).repay(marketParams, assets / 4n, 0, borrower.address, "0x");

await randomForwardTimestamp();

await morpho.connect(borrower).withdrawCollateral(marketParams, assets / 8n, borrower.address, borrower.address);
for (let i = 0; i < 200; ++i) {
logProgress("main", i, 200);

const lastUpdate = await randomForwardTimestamp();

const totalSupplyAssets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100));
const totalBorrowAssets = totalSupplyAssets.min(BigInt.WAD * toBigInt(1 + Math.floor(random() * 50)));

await irm.borrowRate(marketParams, {
fee: 0,
lastUpdate,
totalSupplyAssets: totalSupplyAssets,
totalBorrowAssets: totalBorrowAssets,
// Non-zero shares to include calldata gas cost.
totalSupplyShares: 1000000000000n,
totalBorrowShares: 1000000000000n,
});
}
});

it("should trace borrow rate [borrowRate]", async () => {
const supplier = suppliers[0];

await morpho.connect(supplier).supply(marketParams, BigInt.WAD, 0, supplier.address, "0x");

const borrower = borrowers[0];

await morpho.connect(borrower).supplyCollateral(marketParams, BigInt.WAD, borrower.address, "0x");
await morpho.connect(borrower).borrow(marketParams, BigInt.WAD / 2n, 0, borrower.address, borrower.address);

const block = await hre.ethers.provider.getBlock("latest");
await setNextBlockTimestamp(block!.timestamp + 60 * 60 * 24);

hre.tracer.printNext = true;
hre.tracer.enableAllOpcodes = true;
await morpho.accrueInterest(marketParams);
});
});
14 changes: 7 additions & 7 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"compilerOptions": {
"target": "esnext",
"target": "es2020",
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "dist",
"baseUrl": ".",
"strict": true,
"esModuleInterop": true,
"rootDir": ".",
"baseUrl": ".",
"outDir": "dist",
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true
},
"include": ["src", "types", "test/hardhat"],
"files": ["./hardhat.config.ts"]
"include": ["types", "test/hardhat"],
"files": ["hardhat.config.ts"]
}

0 comments on commit 5f2a9e6

Please sign in to comment.