Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions simulations/vip-664/bscmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { forking, testVip } from "src/vip-framework";

import vip664 from "../../vips/vip-664/bscmainnet";
import { BNB_BTC, BNB_CORE, BNB_DEFI, BNB_GAMEFI, BNB_MEME } from "../../vips/vip-664/phase4Markets";
import { checkPhase4PostVip, checkPhase4PreVip } from "./utils/checkPhase4";

// BSC mainnet block (after the BNB push-out IRMs were deployed).
const FORK_BLOCK = 104562598;

const BNB_POOLS = [BNB_CORE, BNB_BTC, BNB_DEFI, BNB_GAMEFI, BNB_MEME];

forking(FORK_BLOCK, async () => {
checkPhase4PreVip(BNB_POOLS);

testVip("VIP-664 Market Deprecation Phase 4 (Part 1) — BNB Chain", await vip664());

checkPhase4PostVip(BNB_POOLS);
});
16 changes: 16 additions & 0 deletions simulations/vip-664/ethereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import vip664 from "../../vips/vip-664/bscmainnet";
import { ETH_CORE } from "../../vips/vip-664/phase4Markets";
import { checkPhase4PostVip, checkPhase4PreVip } from "./utils/checkPhase4";

// Ethereum mainnet block (after the Ethereum push-out IRM was deployed).
const FORK_BLOCK = 25329944;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([ETH_CORE]);

testForkedNetworkVipCommands("VIP-664 Ethereum Core", await vip664());

checkPhase4PostVip([ETH_CORE]);
});
16 changes: 16 additions & 0 deletions simulations/vip-664/opbnbmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import vip664 from "../../vips/vip-664/bscmainnet";
import { OPBNB } from "../../vips/vip-664/phase4Markets";
import { checkPhase4PostVip, checkPhase4PreVip } from "./utils/checkPhase4";

// opBNB mainnet block (after the opBNB push-out IRM was deployed).
const FORK_BLOCK = 153977309;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([OPBNB]);

testForkedNetworkVipCommands("VIP-664 opBNB", await vip664());

checkPhase4PostVip([OPBNB]);
});
16 changes: 16 additions & 0 deletions simulations/vip-664/opmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import vip664 from "../../vips/vip-664/bscmainnet";
import { OPTIMISM } from "../../vips/vip-664/phase4Markets";
import { checkPhase4PostVip, checkPhase4PreVip } from "./utils/checkPhase4";

// Optimism mainnet block (after the Optimism push-out IRM was deployed).
const FORK_BLOCK = 153006241;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([OPTIMISM]);

testForkedNetworkVipCommands("VIP-664 Optimism", await vip664());

checkPhase4PostVip([OPTIMISM]);
});
16 changes: 16 additions & 0 deletions simulations/vip-664/unichainmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import vip664 from "../../vips/vip-664/bscmainnet";
import { UNICHAIN } from "../../vips/vip-664/phase4Markets";
import { checkPhase4PostVip, checkPhase4PreVip } from "./utils/checkPhase4";

// Unichain mainnet block (after the Unichain push-out IRM was deployed).
const FORK_BLOCK = 50862900;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([UNICHAIN]);

testForkedNetworkVipCommands("VIP-664 Unichain", await vip664());

checkPhase4PostVip([UNICHAIN]);
});
160 changes: 160 additions & 0 deletions simulations/vip-664/utils/checkPhase4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { expect } from "chai";
import { Contract } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import { ethers } from "hardhat";

import { PUSHOUT_IRM, PoolDef, RF_FULL } from "../../../vips/vip-664/phase4Markets";

// Documented pre-VIP collateral factor on the two BNB Liquid Staked ETH markets (5%).
const CF_GAP = parseUnits("0.05", 18);

const VTOKEN_ABI = [
"function reserveFactorMantissa() view returns (uint256)",
"function interestRateModel() view returns (address)",
];

const COMPTROLLER_ABI = [
"function supplyCaps(address) view returns (uint256)",
"function borrowCaps(address) view returns (uint256)",
"function markets(address) view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa)",
];

// Asserts the pre-VIP state for the given pools, proving the VIP is not a no-op.
// Every assertion mirrors exactly one action emitted by generatePoolCommands, and a
// test is only registered when that action touches at least one market in the pool —
// so no check asserts state for a market the VIP does not modify, and an empty filter
// never shows up as a vacuously-passing test:
// - reserve factor below 100% on markets being raised (skips already-100% markets)
// - interest rate model not yet the push-out IRM (skips PLANET / stkBNB)
// - leftover supply / borrow cap gaps still open (> 0)
// - the two collateral-factor gaps still read 5%
export const checkPhase4PreVip = (pools: PoolDef[]) => {
const provider = ethers.provider;

describe("Pre-VIP behavior", () => {
for (const pool of pools) {
describe(pool.label, () => {
const comptroller = new Contract(pool.comptroller, COMPTROLLER_ABI, provider);
const rfMarkets = pool.markets.filter(m => !m.rfAlready100);
const irmMarkets = pool.markets.filter(m => !m.skipIrm);
const supplyGap = pool.markets.filter(m => m.supplyCapGap);
const borrowGap = pool.markets.filter(m => m.borrowCapGap);
const cfGap = pool.markets.filter(m => m.cfGapLiqThreshold !== undefined);

if (rfMarkets.length > 0) {
it("reserve factor below 100% on markets being raised", async () => {
for (const m of rfMarkets) {
const vToken = new Contract(m.vToken, VTOKEN_ABI, provider);
expect((await vToken.reserveFactorMantissa()).lt(RF_FULL), m.symbol).to.be.true;
}
});
}

if (irmMarkets.length > 0) {
it("interest rate model is not yet the push-out IRM", async () => {
const pushout = PUSHOUT_IRM[pool.irmKey].toLowerCase();
for (const m of irmMarkets) {
const vToken = new Contract(m.vToken, VTOKEN_ABI, provider);
expect((await vToken.interestRateModel()).toLowerCase(), m.symbol).to.not.equal(pushout);
}
});
}

if (supplyGap.length > 0) {
it("outstanding supply caps still open (> 0)", async () => {
for (const m of supplyGap) {
expect((await comptroller.supplyCaps(m.vToken)).gt(0), m.symbol).to.be.true;
}
});
}

if (borrowGap.length > 0) {
it("outstanding borrow caps still open (> 0)", async () => {
for (const m of borrowGap) {
expect((await comptroller.borrowCaps(m.vToken)).gt(0), m.symbol).to.be.true;
}
});
}

if (cfGap.length > 0) {
it("collateral-factor gaps still read 5% (liquidation threshold matches)", async () => {
for (const m of cfGap) {
const data = await comptroller.markets(m.vToken);
expect(data.collateralFactorMantissa.toString(), m.symbol).to.equal(CF_GAP.toString());
expect(data.liquidationThresholdMantissa.toString(), m.symbol).to.equal(m.cfGapLiqThreshold);
}
});
}
});
}
});
};

// Asserts the Phase-4 end state for the given pools. Every assertion mirrors exactly
// one action emitted by generatePoolCommands and is only registered when that action
// touches at least one market in the pool:
// - reserve factor raised to 100% (skips already-100% markets)
// - interest rate model repointed to the push-out IRM (skips PLANET / stkBNB)
// - leftover supply / borrow cap gaps closed to 0
// - collateral-factor gaps closed to 0 with the liquidation threshold preserved
export const checkPhase4PostVip = (pools: PoolDef[]) => {
const provider = ethers.provider;

describe("Post-VIP behavior", () => {
for (const pool of pools) {
describe(pool.label, () => {
const comptroller = new Contract(pool.comptroller, COMPTROLLER_ABI, provider);
const rfMarkets = pool.markets.filter(m => !m.rfAlready100);
const irmMarkets = pool.markets.filter(m => !m.skipIrm);
const supplyGap = pool.markets.filter(m => m.supplyCapGap);
const borrowGap = pool.markets.filter(m => m.borrowCapGap);
const cfGap = pool.markets.filter(m => m.cfGapLiqThreshold !== undefined);

if (rfMarkets.length > 0) {
it("reserve factor raised to 100%", async () => {
for (const m of rfMarkets) {
const vToken = new Contract(m.vToken, VTOKEN_ABI, provider);
expect((await vToken.reserveFactorMantissa()).toString(), m.symbol).to.equal(RF_FULL.toString());
}
});
}

if (irmMarkets.length > 0) {
it("interest rate model repointed to the push-out IRM", async () => {
const expected = PUSHOUT_IRM[pool.irmKey].toLowerCase();
for (const m of irmMarkets) {
const vToken = new Contract(m.vToken, VTOKEN_ABI, provider);
expect((await vToken.interestRateModel()).toLowerCase(), m.symbol).to.equal(expected);
}
});
}

if (supplyGap.length > 0) {
it("outstanding supply caps closed to 0", async () => {
for (const m of supplyGap) {
expect((await comptroller.supplyCaps(m.vToken)).toString(), m.symbol).to.equal("0");
}
});
}

if (borrowGap.length > 0) {
it("outstanding borrow caps closed to 0", async () => {
for (const m of borrowGap) {
expect((await comptroller.borrowCaps(m.vToken)).toString(), m.symbol).to.equal("0");
}
});
}

if (cfGap.length > 0) {
it("outstanding collateral factors closed to 0 (liquidation threshold preserved)", async () => {
for (const m of cfGap) {
const data = await comptroller.markets(m.vToken);
expect(data.collateralFactorMantissa.toString(), m.symbol).to.equal("0");
expect(data.liquidationThresholdMantissa.toString(), m.symbol).to.equal(m.cfGapLiqThreshold);
}
});
}
});
}
});
};
16 changes: 16 additions & 0 deletions simulations/vip-665/arbitrumone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import { ARBITRUM_LIQUID_STAKED_ETH } from "../../vips/vip-664/phase4Markets";
import vip665 from "../../vips/vip-665/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-664/utils/checkPhase4";

// Arbitrum One block (after the Arbitrum push-out IRM was deployed).
const FORK_BLOCK = 474085204;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([ARBITRUM_LIQUID_STAKED_ETH]);

testForkedNetworkVipCommands("VIP-665 Arbitrum", await vip665());

checkPhase4PostVip([ARBITRUM_LIQUID_STAKED_ETH]);
});
16 changes: 16 additions & 0 deletions simulations/vip-665/basemainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import { BASE_CORE } from "../../vips/vip-664/phase4Markets";
import vip665 from "../../vips/vip-665/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-664/utils/checkPhase4";

// Base mainnet block (after the Base push-out IRM was deployed).
const FORK_BLOCK = 47410957;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([BASE_CORE]);

testForkedNetworkVipCommands("VIP-665 Base", await vip665());

checkPhase4PostVip([BASE_CORE]);
});
23 changes: 23 additions & 0 deletions simulations/vip-665/bscmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { forking, testVip } from "src/vip-framework";

import {
BNB_LIQUID_STAKED_BNB,
BNB_LIQUID_STAKED_ETH,
BNB_STABLECOINS,
BNB_TRON,
} from "../../vips/vip-664/phase4Markets";
import vip665 from "../../vips/vip-665/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-664/utils/checkPhase4";

// BSC mainnet block (after the BNB push-out IRM was deployed).
const FORK_BLOCK = 104562598;

const BNB_POOLS = [BNB_LIQUID_STAKED_BNB, BNB_LIQUID_STAKED_ETH, BNB_STABLECOINS, BNB_TRON];

forking(FORK_BLOCK, async () => {
checkPhase4PreVip(BNB_POOLS);

testVip("VIP-665 Market Deprecation Phase 4 (Part 2) — BNB Chain", await vip665());

checkPhase4PostVip(BNB_POOLS);
});
16 changes: 16 additions & 0 deletions simulations/vip-665/ethereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import { ETH_CURVE, ETH_LIQUID_STAKED_ETH } from "../../vips/vip-664/phase4Markets";
import vip665 from "../../vips/vip-665/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-664/utils/checkPhase4";

// Ethereum mainnet block (after the Ethereum push-out IRM was deployed).
const FORK_BLOCK = 25329944;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([ETH_CURVE, ETH_LIQUID_STAKED_ETH]);

testForkedNetworkVipCommands("VIP-665 Ethereum Curve + Liquid Staked ETH", await vip665());

checkPhase4PostVip([ETH_CURVE, ETH_LIQUID_STAKED_ETH]);
});
16 changes: 16 additions & 0 deletions simulations/vip-665/zksyncmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { forking, testForkedNetworkVipCommands } from "src/vip-framework";

import { ZKSYNC_CORE } from "../../vips/vip-664/phase4Markets";
import vip665 from "../../vips/vip-665/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-664/utils/checkPhase4";

// zkSync Era mainnet block (after the zkSync push-out IRM was deployed).
const FORK_BLOCK = 70814289;

forking(FORK_BLOCK, async () => {
checkPhase4PreVip([ZKSYNC_CORE]);

testForkedNetworkVipCommands("VIP-665 zkSync Era", await vip665());

checkPhase4PostVip([ZKSYNC_CORE]);
});
Loading
Loading