Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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-634/bscmainnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { forking, testVip } from "src/vip-framework";

import vip634 from "../../vips/vip-634/bscmainnet";
import { BNB_BTC, BNB_CORE, BNB_DEFI, BNB_GAMEFI } from "../../vips/vip-634/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];

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

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

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

import vip634 from "../../vips/vip-634/bscmainnet";
import { ETH_CORE } from "../../vips/vip-634/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-634 Ethereum Core", await vip634());

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

import vip634 from "../../vips/vip-634/bscmainnet";
import { OPBNB } from "../../vips/vip-634/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-634 opBNB", await vip634());

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

import vip634 from "../../vips/vip-634/bscmainnet";
import { OPTIMISM } from "../../vips/vip-634/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-634 Optimism", await vip634());

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

import vip634 from "../../vips/vip-634/bscmainnet";
import { UNICHAIN } from "../../vips/vip-634/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-634 Unichain", await vip634());

checkPhase4PostVip([UNICHAIN]);
});
187 changes: 187 additions & 0 deletions simulations/vip-634/utils/checkPhase4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
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-634/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)",
"function poolMarkets(uint96, address) view returns (bool isListed, uint256 collateralFactorMantissa, uint256 unused, uint256 liquidationThresholdMantissa, bool isBorrowAllowed)",
];

// 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);
const emodeCfGap = pool.markets.filter(m => m.emodeCfGap !== 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);
}
});
}

if (emodeCfGap.length > 0) {
it("e-mode collateral-factor gaps still open (> 0, liquidation threshold matches)", async () => {
for (const m of emodeCfGap) {
const g = m.emodeCfGap;
if (!g) continue;
const data = await comptroller.poolMarkets(g.poolId, m.vToken);
expect(data.collateralFactorMantissa.gt(0), m.symbol).to.be.true;
expect(data.liquidationThresholdMantissa.toString(), m.symbol).to.equal(g.liquidationThreshold);
}
});
}
});
}
});
};

// 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);
const emodeCfGap = pool.markets.filter(m => m.emodeCfGap !== 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);
}
});
}

if (emodeCfGap.length > 0) {
it("e-mode collateral factors closed to 0 (liquidation threshold preserved)", async () => {
for (const m of emodeCfGap) {
const g = m.emodeCfGap;
if (!g) continue;
const data = await comptroller.poolMarkets(g.poolId, m.vToken);
expect(data.collateralFactorMantissa.toString(), m.symbol).to.equal("0");
expect(data.liquidationThresholdMantissa.toString(), m.symbol).to.equal(g.liquidationThreshold);
}
});
}
});
}
});
};
16 changes: 16 additions & 0 deletions simulations/vip-635/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-634/phase4Markets";
import vip635 from "../../vips/vip-635/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-634/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-635 Arbitrum", await vip635());

checkPhase4PostVip([ARBITRUM_LIQUID_STAKED_ETH]);
});
16 changes: 16 additions & 0 deletions simulations/vip-635/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-634/phase4Markets";
import vip635 from "../../vips/vip-635/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-634/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-635 Base", await vip635());

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

import {
BNB_LIQUID_STAKED_BNB,
BNB_LIQUID_STAKED_ETH,
BNB_MEME,
BNB_STABLECOINS,
BNB_TRON,
} from "../../vips/vip-634/phase4Markets";
import vip635 from "../../vips/vip-635/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-634/utils/checkPhase4";

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

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

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

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

checkPhase4PostVip(BNB_POOLS);
});
16 changes: 16 additions & 0 deletions simulations/vip-635/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-634/phase4Markets";
import vip635 from "../../vips/vip-635/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-634/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-635 Ethereum Curve + Liquid Staked ETH", await vip635());

checkPhase4PostVip([ETH_CURVE, ETH_LIQUID_STAKED_ETH]);
});
16 changes: 16 additions & 0 deletions simulations/vip-635/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-634/phase4Markets";
import vip635 from "../../vips/vip-635/bscmainnet";
import { checkPhase4PostVip, checkPhase4PreVip } from "../vip-634/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-635 zkSync Era", await vip635());

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