From 51f8650a610fc5ce5be4621ed0a573bff6c99672 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Mon, 22 Jun 2026 15:58:00 +0530 Subject: [PATCH 1/5] feat: add VIP for bStock liquidator flash-loan whitelist - Whitelists the bStock backstop liquidator as a flash-loan account on the Core Pool Comptroller (single setWhiteListFlashLoanAccount call, mirroring VIP-576). - Flash loans and the timelock's whitelist permission are already live on-chain from earlier VIPs, so no setFlashLoanEnabled or ACM grant is included. - The liquidator is not deployed yet: the address is a zero placeholder and the fork simulation intentionally fails until the deployed address is set. --- simulations/vip-636/abi/FlashLoanFacet.json | 1218 +++++++++++++++++++ simulations/vip-636/bscmainnet.ts | 39 + vips/vip-636/bscmainnet.ts | 42 + 3 files changed, 1299 insertions(+) create mode 100644 simulations/vip-636/abi/FlashLoanFacet.json create mode 100644 simulations/vip-636/bscmainnet.ts create mode 100644 vips/vip-636/bscmainnet.ts diff --git a/simulations/vip-636/abi/FlashLoanFacet.json b/simulations/vip-636/abi/FlashLoanFacet.json new file mode 100644 index 000000000..2adc7e8fa --- /dev/null +++ b/simulations/vip-636/abi/FlashLoanFacet.json @@ -0,0 +1,1218 @@ +[ + { + "inputs": [], + "name": "AlreadyInSelectedPool", + "type": "error" + }, + { + "inputs": [], + "name": "ArrayLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowNotAllowedInPool", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyPoolLabel", + "type": "error" + }, + { + "inputs": [], + "name": "ExecuteFlashLoanFailed", + "type": "error" + }, + { + "inputs": [], + "name": "FailedToCreateDebtPosition", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanNotEnabled", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanPausedSystemWide", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + } + ], + "name": "InactivePool", + "type": "error" + }, + { + "inputs": [], + "name": "IncompatibleBorrowedAssets", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFlashLoanParams", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOperationForCorePool", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum WeightFunction", + "name": "strategy", + "type": "uint8" + } + ], + "name": "InvalidWeightingStrategy", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "name": "LiquidityCheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketAlreadyListed", + "type": "error" + }, + { + "inputs": [], + "name": "MarketConfigNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "MarketNotListedInCorePool", + "type": "error" + }, + { + "inputs": [], + "name": "NoAssetsRequested", + "type": "error" + }, + { + "inputs": [], + "name": "NotAnApprovedDelegate", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "repaid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "required", + "type": "uint256" + } + ], + "name": "NotEnoughRepayment", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + } + ], + "name": "PoolDoesNotExist", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "PoolMarketNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotAuthorizedForFlashLoan", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximum", + "type": "uint256" + } + ], + "name": "TooManyAssetsRequested", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DistributedVAIVaultVenus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract VToken[]", + "name": "assets", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "FlashLoanExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "onBehalf", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repaidAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingDebt", + "type": "uint256" + } + ], + "name": "FlashLoanRepaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "bool", + "name": "isWhitelisted", + "type": "bool" + } + ], + "name": "IsAccountFlashLoanWhitelisted", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_FLASHLOAN_ASSETS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "actionPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allMarkets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "approvedDelegates", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "authorizedFlashLoan", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowCapGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptrollerLens", + "outputs": [ + { + "internalType": "contract ComptrollerLensInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "corePoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "onBehalf", + "type": "address" + }, + { + "internalType": "address payable", + "name": "receiver", + "type": "address" + }, + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "underlyingAmounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "param", + "type": "bytes" + } + ], + "name": "executeFlashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getPoolMarketIndex", + "outputs": [ + { + "internalType": "PoolMarketId", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getXVSAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabledForUser", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidatorContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minReleaseAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintVAIGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "mintedVAIs", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauseGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingComptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "name": "pools", + "outputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "bool", + "name": "allowCorePoolFallback", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "prime", + "outputs": [ + { + "internalType": "contract IPrime", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "releaseStartBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "repayVAIGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userPoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiController", + "outputs": [ + { + "internalType": "contract VAIControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiMintRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiVaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusAccrued", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowSpeeds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowState", + "outputs": [ + { + "internalType": "uint224", + "name": "index", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "block", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowerIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "venusInitialIndex", + "outputs": [ + { + "internalType": "uint224", + "name": "", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplierIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplySpeeds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplyState", + "outputs": [ + { + "internalType": "uint224", + "name": "index", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "block", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "venusVAIVaultRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-636/bscmainnet.ts b/simulations/vip-636/bscmainnet.ts new file mode 100644 index 000000000..778c0dcb9 --- /dev/null +++ b/simulations/vip-636/bscmainnet.ts @@ -0,0 +1,39 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import vip636, { BSTOCK_LIQUIDATOR, UNITROLLER } from "../../vips/vip-636/bscmainnet"; +import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; + +const VUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; +const FORK_BLOCK = 104930000; // ToDo: Block after the bStock backstop liquidator is deployed + +const VUSDT_ABI = ["function isFlashLoanEnabled() view returns (bool)"]; + +forking(FORK_BLOCK, async () => { + const comptroller = new ethers.Contract(UNITROLLER, FLASHLOAN_FACET_ABI, ethers.provider); + const vUSDT = new ethers.Contract(VUSDT, VUSDT_ABI, ethers.provider); + + describe("VIP-636 Pre-VIP behavior", () => { + it("BStockLiquidator is not yet whitelisted for flash loans", async () => { + expect(await comptroller.authorizedFlashLoan(BSTOCK_LIQUIDATOR)).to.equal(false); + }); + + it("vUSDT flash loans are already enabled (set by a prior VIP)", async () => { + expect(await vUSDT.isFlashLoanEnabled()).to.equal(true); + }); + }); + + testVip("VIP-636", await vip636(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [FLASHLOAN_FACET_ABI], ["IsAccountFlashLoanWhitelisted"], [1]); + }, + }); + + describe("VIP-636 Post-VIP behavior", () => { + it("BStockLiquidator is whitelisted for flash loans", async () => { + expect(await comptroller.authorizedFlashLoan(BSTOCK_LIQUIDATOR)).to.equal(true); + }); + }); +}); diff --git a/vips/vip-636/bscmainnet.ts b/vips/vip-636/bscmainnet.ts new file mode 100644 index 000000000..443348f6e --- /dev/null +++ b/vips/vip-636/bscmainnet.ts @@ -0,0 +1,42 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +export const UNITROLLER = "0xfD36E2c2a6789Db23113685031d7F16329158384"; + +// TODO: BStockLiquidator is not deployed yet. Replace this placeholder with the deployed address before proposing. +export const BSTOCK_LIQUIDATOR = "0x0000000000000000000000000000000000000000"; + +export const vip636 = () => { + const meta = { + version: "v2", + title: "VIP-636 [BNB Chain] Enable flash loans for the bStock backstop liquidator", + description: `If passed, this VIP will whitelist the bStock backstop liquidator as a flash loan account on the Core Pool Comptroller on BNB Chain. + +**Details** + +The Core Pool flash loan feature is already live (enabled by [VIP-567](https://app.venus.io/#/governance/proposal/567?chainId=56) and activated for all markets). The Comptroller only allows whitelisted accounts to initiate flash loans. This VIP authorizes the bStock backstop liquidator to do so, letting it atomically repay an undercollateralized bStock borrow, seize and redeem the collateral, and sell it for the debt asset within a single transaction, without locking protocol capital. + +This action follows the same pattern as [VIP-576](https://app.venus.io/#/governance/proposal/576?chainId=56), which whitelisted the Leveraged Positions manager. + +**Action** + +- Whitelist the bStock backstop liquidator on the [Core Pool Comptroller](https://bscscan.com/address/0xfD36E2c2a6789Db23113685031d7F16329158384) (\`setWhiteListFlashLoanAccount\`).`, + forDescription: "Execute", + againstDescription: "Do not execute", + abstainDescription: "Abstain", + }; + + return makeProposal( + [ + { + target: UNITROLLER, + signature: "setWhiteListFlashLoanAccount(address,bool)", + params: [BSTOCK_LIQUIDATOR, true], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip636; From a2184ce3b8f13278defaccae2531d5b4d630bba9 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Fri, 3 Jul 2026 16:41:26 +0530 Subject: [PATCH 2/5] chore(vip-636): wire deployed BStockLiquidator address and fork block Replace the zero-address placeholder with the deployed BStockLiquidator (0xF03C90e6...) and pin the sim to a fork block after its deployment, readying VIP-636 for proposal. --- simulations/vip-636/bscmainnet.ts | 2 +- vips/vip-636/bscmainnet.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/simulations/vip-636/bscmainnet.ts b/simulations/vip-636/bscmainnet.ts index 778c0dcb9..e8d7641f3 100644 --- a/simulations/vip-636/bscmainnet.ts +++ b/simulations/vip-636/bscmainnet.ts @@ -7,7 +7,7 @@ import vip636, { BSTOCK_LIQUIDATOR, UNITROLLER } from "../../vips/vip-636/bscmai import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; const VUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; -const FORK_BLOCK = 104930000; // ToDo: Block after the bStock backstop liquidator is deployed +const FORK_BLOCK = 107818021; const VUSDT_ABI = ["function isFlashLoanEnabled() view returns (bool)"]; diff --git a/vips/vip-636/bscmainnet.ts b/vips/vip-636/bscmainnet.ts index 443348f6e..9dbba26ac 100644 --- a/vips/vip-636/bscmainnet.ts +++ b/vips/vip-636/bscmainnet.ts @@ -2,9 +2,7 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; export const UNITROLLER = "0xfD36E2c2a6789Db23113685031d7F16329158384"; - -// TODO: BStockLiquidator is not deployed yet. Replace this placeholder with the deployed address before proposing. -export const BSTOCK_LIQUIDATOR = "0x0000000000000000000000000000000000000000"; +export const BSTOCK_LIQUIDATOR = "0xF03C90e6BF66b43411189Ad848F17723f8B4A3c1"; export const vip636 = () => { const meta = { From 33dbaf1632d0a5bfb4a008b4613c2fa162804bcb Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Fri, 3 Jul 2026 16:44:32 +0530 Subject: [PATCH 3/5] chore(vip-650): rename VIP-636 to VIP-650 --- simulations/{vip-636 => vip-650}/abi/FlashLoanFacet.json | 0 simulations/{vip-636 => vip-650}/bscmainnet.ts | 8 ++++---- vips/{vip-636 => vip-650}/bscmainnet.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename simulations/{vip-636 => vip-650}/abi/FlashLoanFacet.json (100%) rename simulations/{vip-636 => vip-650}/bscmainnet.ts (85%) rename vips/{vip-636 => vip-650}/bscmainnet.ts (93%) diff --git a/simulations/vip-636/abi/FlashLoanFacet.json b/simulations/vip-650/abi/FlashLoanFacet.json similarity index 100% rename from simulations/vip-636/abi/FlashLoanFacet.json rename to simulations/vip-650/abi/FlashLoanFacet.json diff --git a/simulations/vip-636/bscmainnet.ts b/simulations/vip-650/bscmainnet.ts similarity index 85% rename from simulations/vip-636/bscmainnet.ts rename to simulations/vip-650/bscmainnet.ts index e8d7641f3..3b12e42e8 100644 --- a/simulations/vip-636/bscmainnet.ts +++ b/simulations/vip-650/bscmainnet.ts @@ -3,7 +3,7 @@ import { ethers } from "hardhat"; import { expectEvents } from "src/utils"; import { forking, testVip } from "src/vip-framework"; -import vip636, { BSTOCK_LIQUIDATOR, UNITROLLER } from "../../vips/vip-636/bscmainnet"; +import vip650, { BSTOCK_LIQUIDATOR, UNITROLLER } from "../../vips/vip-650/bscmainnet"; import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; const VUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; @@ -15,7 +15,7 @@ forking(FORK_BLOCK, async () => { const comptroller = new ethers.Contract(UNITROLLER, FLASHLOAN_FACET_ABI, ethers.provider); const vUSDT = new ethers.Contract(VUSDT, VUSDT_ABI, ethers.provider); - describe("VIP-636 Pre-VIP behavior", () => { + describe("VIP-650 Pre-VIP behavior", () => { it("BStockLiquidator is not yet whitelisted for flash loans", async () => { expect(await comptroller.authorizedFlashLoan(BSTOCK_LIQUIDATOR)).to.equal(false); }); @@ -25,13 +25,13 @@ forking(FORK_BLOCK, async () => { }); }); - testVip("VIP-636", await vip636(), { + testVip("VIP-650", await vip650(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [FLASHLOAN_FACET_ABI], ["IsAccountFlashLoanWhitelisted"], [1]); }, }); - describe("VIP-636 Post-VIP behavior", () => { + describe("VIP-650 Post-VIP behavior", () => { it("BStockLiquidator is whitelisted for flash loans", async () => { expect(await comptroller.authorizedFlashLoan(BSTOCK_LIQUIDATOR)).to.equal(true); }); diff --git a/vips/vip-636/bscmainnet.ts b/vips/vip-650/bscmainnet.ts similarity index 93% rename from vips/vip-636/bscmainnet.ts rename to vips/vip-650/bscmainnet.ts index 9dbba26ac..b33ac2208 100644 --- a/vips/vip-636/bscmainnet.ts +++ b/vips/vip-650/bscmainnet.ts @@ -4,10 +4,10 @@ import { makeProposal } from "src/utils"; export const UNITROLLER = "0xfD36E2c2a6789Db23113685031d7F16329158384"; export const BSTOCK_LIQUIDATOR = "0xF03C90e6BF66b43411189Ad848F17723f8B4A3c1"; -export const vip636 = () => { +export const vip650 = () => { const meta = { version: "v2", - title: "VIP-636 [BNB Chain] Enable flash loans for the bStock backstop liquidator", + title: "VIP-650 [BNB Chain] Enable flash loans for the bStock backstop liquidator", description: `If passed, this VIP will whitelist the bStock backstop liquidator as a flash loan account on the Core Pool Comptroller on BNB Chain. **Details** @@ -37,4 +37,4 @@ This action follows the same pattern as [VIP-576](https://app.venus.io/#/governa ); }; -export default vip636; +export default vip650; From e29782dde80b4505e150eacda495e3a9ef9ac359 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Mon, 20 Jul 2026 19:01:59 +0530 Subject: [PATCH 4/5] chore(vip-650): repoint at redeployed BStockLiquidator --- simulations/vip-650/bscmainnet.ts | 2 +- vips/vip-650/bscmainnet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/simulations/vip-650/bscmainnet.ts b/simulations/vip-650/bscmainnet.ts index 3b12e42e8..e3f2b3c9e 100644 --- a/simulations/vip-650/bscmainnet.ts +++ b/simulations/vip-650/bscmainnet.ts @@ -7,7 +7,7 @@ import vip650, { BSTOCK_LIQUIDATOR, UNITROLLER } from "../../vips/vip-650/bscmai import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; const VUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; -const FORK_BLOCK = 107818021; +const FORK_BLOCK = 111098546; const VUSDT_ABI = ["function isFlashLoanEnabled() view returns (bool)"]; diff --git a/vips/vip-650/bscmainnet.ts b/vips/vip-650/bscmainnet.ts index b33ac2208..6ca5cd308 100644 --- a/vips/vip-650/bscmainnet.ts +++ b/vips/vip-650/bscmainnet.ts @@ -2,7 +2,7 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; export const UNITROLLER = "0xfD36E2c2a6789Db23113685031d7F16329158384"; -export const BSTOCK_LIQUIDATOR = "0xF03C90e6BF66b43411189Ad848F17723f8B4A3c1"; +export const BSTOCK_LIQUIDATOR = "0x5974Badab6911a78Ba15229045514C2C1bD42343"; export const vip650 = () => { const meta = { From ffdf20209108f212bcb97a6c12fe39a131f019cd Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Mon, 20 Jul 2026 19:19:45 +0530 Subject: [PATCH 5/5] fix(vip-framework): recycle default proposer as supporter for quorum When a fallback voter outbids the default proposer for the proposer slot, the default proposer's stake was dropped entirely rather than reused, leaving the remaining voters short of the raised 1.5M XVS quorum and defeating every bscmainnet proposal at recent fork blocks. --- src/vip-framework/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vip-framework/index.ts b/src/vip-framework/index.ts index c1dfc108b..7fea70a06 100644 --- a/src/vip-framework/index.ts +++ b/src/vip-framework/index.ts @@ -26,8 +26,9 @@ import ENDPOINT_ABI from "./abi/LzEndpoint.json"; import OMNICHAIN_EXECUTOR_ABI from "./abi/OmnichainGovernanceExecutor.json"; import GOVERNOR_BRAVO_DELEGATE_ABI from "./abi/governorBravoDelegateAbi.json"; -// XVS Vault stakes erode over time, so a single default supporter no longer reliably -// clears quorum (600,000 XVS) when combined with the proposer at recent fork blocks. +// XVS Vault stakes erode over time and governance has raised the bar (1,000,000 XVS proposal +// threshold, 1,500,000 XVS quorum as of block ~111,098,000), so a single default supporter no +// longer reliably clears quorum when combined with the proposer at recent fork blocks. // Two supporters give the framework headroom across blocks. Override per-test by passing // `supporter` (single, legacy) or `supporters` (array) to `testVip`. const DEFAULT_SUPPORTER_ADDRESSES = [ @@ -325,9 +326,12 @@ const resolveGovernanceVoters = async ( } } - // Build a supporter set so proposer + supporters clear quorum. + // Build a supporter set so proposer + supporters clear quorum. The default proposer keeps its + // voting power when a fallback outbids it for the proposer slot, so fold it back in as a + // supporter — otherwise its stake is silently dropped and the remaining voters can fall short + // of quorum even though enough power exists. const supporterAddresses = [...defaultSupporters]; - for (const voter of fallbackVoters) { + for (const voter of [defaultProposer, ...fallbackVoters]) { if (voter !== proposerAddress && !supporterAddresses.includes(voter)) supporterAddresses.push(voter); } return { proposerAddress, supporterAddresses };