From 2e783f0ec45efb447f1b9d2aa4d7e1e1b972f16a Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Tue, 17 Mar 2026 19:32:51 +0530 Subject: [PATCH 01/12] feat: add VIP-608 to upgrade VBep20Delegate for 43 core pool markets Upgrade all VBep20Delegator proxies to new implementation with internalCash tracking. Each market atomically sets the new implementation and calls sweepTokenAndSync to initialize internalCash. --- simulations/vip-608/abi/VBep20Delegate.json | 902 ++++++++++++++++++++ simulations/vip-608/bscmainnet.ts | 171 ++++ vips/vip-608/bscmainnet.ts | 137 +++ 3 files changed, 1210 insertions(+) create mode 100644 simulations/vip-608/abi/VBep20Delegate.json create mode 100644 simulations/vip-608/bscmainnet.ts create mode 100644 vips/vip-608/bscmainnet.ts diff --git a/simulations/vip-608/abi/VBep20Delegate.json b/simulations/vip-608/abi/VBep20Delegate.json new file mode 100644 index 000000000..a3e02c234 --- /dev/null +++ b/simulations/vip-608/abi/VBep20Delegate.json @@ -0,0 +1,902 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "FlashLoanAlreadyActive", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { "internalType": "uint256", "name": "maxFee", "type": "uint256" } + ], + "name": "FlashLoanFeeTooHigh", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { "internalType": "uint256", "name": "maxFee", "type": "uint256" } + ], + "name": "FlashLoanProtocolShareTooHigh", + "type": "error" + }, + { "inputs": [], "name": "InsufficientCash", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "actualAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "requiredTotalFee", "type": "uint256" } + ], + "name": "InsufficientRepayment", + "type": "error" + }, + { "inputs": [], "name": "InvalidComptroller", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "cashPrior", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "interestAccumulated", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "borrowIndex", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "borrowAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "accountBorrows", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldInternalCash", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newInternalCash", "type": "uint256" } + ], + "name": "CashSynced", + "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": false, "internalType": "uint256", "name": "oldFlashLoanFeeMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newFlashLoanFeeMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "oldFlashLoanProtocolShare", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newFlashLoanProtocolShare", "type": "uint256" } + ], + "name": "FlashLoanFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bool", "name": "previousStatus", "type": "bool" }, + { "indexed": false, "internalType": "bool", "name": "newStatus", "type": "bool" } + ], + "name": "FlashLoanStatusChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "liquidator", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "vTokenCollateral", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "seizeTokens", "type": "uint256" } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "minter", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "mintAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "mintTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "payer", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "receiver", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "mintAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "mintTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "MintBehalf", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlAddress", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlAddress", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAdmin", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "NewAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract ComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { "indexed": false, "internalType": "contract ComptrollerInterface", "name": "newComptroller", "type": "address" } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract InterestRateModelV8", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract InterestRateModelV8", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldPendingAdmin", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newPendingAdmin", "type": "address" } + ], + "name": "NewPendingAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldProtocolShareReserve", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newProtocolShareReserve", "type": "address" } + ], + "name": "NewProtocolShareReserve", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldReduceReservesBlockDelta", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newReduceReservesBlockDelta", "type": "uint256" } + ], + "name": "NewReduceReservesBlockDelta", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldReserveFactorMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newReserveFactorMantissa", "type": "uint256" } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "redeemer", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "redeemAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "redeemTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "redeemer", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "feeAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "redeemTokens", "type": "uint256" } + ], + "name": "RedeemFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "payer", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "accountBorrows", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "benefactor", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "addAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalReserves", "type": "uint256" } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "protocolShareReserve", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "reduceAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalReserves", "type": "uint256" } + ], + "name": "ReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "recipient", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "TokenSwept", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalFee", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "protocolFee", "type": "uint256" } + ], + "name": "TransferInUnderlyingFlashLoan", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "receiver", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "TransferOutUnderlyingFlashLoan", + "type": "event" + }, + { + "inputs": [], + "name": "_acceptAdmin", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "addAmount", "type": "uint256" }], + "name": "_addReserves", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], + "name": "_becomeImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "reduceAmount_", "type": "uint256" }], + "name": "_reduceReserves", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "_resignImplementation", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "contract ComptrollerInterface", "name": "newComptroller", "type": "address" }], + "name": "_setComptroller", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "contract InterestRateModelV8", "name": "newInterestRateModel_", "type": "address" }], + "name": "_setInterestRateModel", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address payable", "name": "newPendingAdmin", "type": "address" }], + "name": "_setPendingAdmin", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newReserveFactorMantissa_", "type": "uint256" }], + "name": "_setReserveFactor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrueInterest", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOfUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "borrowAmount", "type": "uint256" }], + "name": "borrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "borrowBalanceCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "borrowBalanceStored", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "borrowAmount", "type": "uint256" } + ], + "name": "borrowBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "borrowIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }], + "name": "calculateFlashLoanFee", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [{ "internalType": "contract ComptrollerInterface", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateStored", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanAmount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "borrowAmount", "type": "uint256" } + ], + "name": "flashLoanDebtPosition", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanFeeMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanProtocolShareMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "getAccountSnapshot", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "underlying_", "type": "address" }, + { "internalType": "contract ComptrollerInterface", "name": "comptroller_", "type": "address" }, + { "internalType": "contract InterestRateModelV8", "name": "interestRateModel_", "type": "address" }, + { "internalType": "uint256", "name": "initialExchangeRateMantissa_", "type": "uint256" }, + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" }, + { "internalType": "uint8", "name": "decimals_", "type": "uint8" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "contract ComptrollerInterface", "name": "comptroller_", "type": "address" }, + { "internalType": "contract InterestRateModelV8", "name": "interestRateModel_", "type": "address" }, + { "internalType": "uint256", "name": "initialExchangeRateMantissa_", "type": "uint256" }, + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" }, + { "internalType": "uint8", "name": "decimals_", "type": "uint8" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRateModel", + "outputs": [{ "internalType": "contract InterestRateModelV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "internalCash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isFlashLoanEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVToken", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "internalType": "contract VTokenInterface", "name": "vTokenCollateral", "type": "address" } + ], + "name": "liquidateBorrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "mintAmount", "type": "uint256" }], + "name": "mint", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "uint256", "name": "mintAmount", "type": "uint256" } + ], + "name": "mintBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAdmin", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "redeemTokens", "type": "uint256" }], + "name": "redeem", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "redeemer", "type": "address" }, + { "internalType": "uint256", "name": "redeemTokens", "type": "uint256" } + ], + "name": "redeemBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "redeemAmount", "type": "uint256" }], + "name": "redeemUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "redeemer", "type": "address" }, + { "internalType": "uint256", "name": "redeemAmount", "type": "uint256" } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockDelta", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockNumber", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "repayAmount", "type": "uint256" }], + "name": "repayBorrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "repayAmount", "type": "uint256" } + ], + "name": "repayBorrowBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "liquidator", "type": "address" }, + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "seizeTokens", "type": "uint256" } + ], + "name": "seize", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newAccessControlManagerAddress", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enabled", "type": "bool" }], + "name": "setFlashLoanEnabled", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "flashLoanFeeMantissa_", "type": "uint256" }, + { "internalType": "uint256", "name": "flashLoanProtocolShare_", "type": "uint256" } + ], + "name": "setFlashLoanFeeMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address payable", "name": "protcolShareReserve_", "type": "address" }], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newReduceReservesBlockDelta_", "type": "uint256" }], + "name": "setReduceReservesBlockDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "transferAmount", "type": "uint256" }], + "name": "sweepTokenAndSync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrows", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "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": [ + { "internalType": "address", "name": "dst", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "src", "type": "address" }, + { "internalType": "address", "name": "dst", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "repaymentAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "totalFee", "type": "uint256" }, + { "internalType": "uint256", "name": "protocolFee", "type": "uint256" } + ], + "name": "transferInUnderlyingFlashLoan", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferOutUnderlyingFlashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-608/bscmainnet.ts b/simulations/vip-608/bscmainnet.ts new file mode 100644 index 000000000..19d595439 --- /dev/null +++ b/simulations/vip-608/bscmainnet.ts @@ -0,0 +1,171 @@ +import { expect } from "chai"; +import { BigNumber, Contract } from "ethers"; +import { ethers } from "hardhat"; +import { forking, testVip } from "src/vip-framework"; + +import vip608 from "../../vips/vip-608/bscmainnet"; +import VBEP20_DELEGATE_ABI from "./abi/VBep20Delegate.json"; + +const BLOCK_NUMBER = 87126359; + +const NEW_VBEP20_DELEGATE_IMPL = "0xb25b57599BA969c4829699F7E4Fc4076D14745E1"; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +// All 43 VBep20Delegator proxy addresses +const MARKET_ADDRESSES = [ + "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8", // vUSDC + "0xfD5840Cd36d94D7229439859C0112a4185BC0255", // vUSDT + "0x95c78222B3D6e262426483D42CfA53685A67Ab9D", // vBUSD + "0x2fF3d0F6990a40261c66E1ff2017aCBc282EB6d0", // vSXP + "0x151B1e2635A717bcDc836ECd6FbB62B674FE3E1D", // vXVS + "0x882C173bC7Ff3b7786CA16dfeD3DFFfb9Ee7847B", // vBTC + "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8", // vETH + "0x57A5297F2cB2c0AaC9D554660acd6D385Ab50c6B", // vLTC + "0xB248a295732e0225acd3337607cc01068e3b9c10", // vXRP + "0x5F0388EBc2B94FA8E123F404b79cCF5f40b29176", // vBCH + "0x1610bc33319e9398de5f57B33a5b184c806aD217", // vDOT + "0x650b940a1033B8A1b1873f78730FcFC73ec11f1f", // vLINK + "0x334b3eCB4DCa3593BCCC3c7EBD1A1C1d1780FBF1", // vDAI + "0xf91d58b5aE142DAcC749f58A49FCBac340Cb0343", // vFIL + "0x972207A639CC1B374B893cc33Fa251b55CEB7c07", // vBETH + "0x9A0AF7FDb2065Ce470D72664DE73cAE409dA28Ec", // vADA + "0xec3422Ef92B2fb59e84c8B02Ba73F1fE84Ed8D71", // vDOGE + "0x5c9476FcD6a4F9a3654139721c949c2233bBbBc8", // vMATIC + "0x86aC3974e2BD0d60825230fa6F355fF11409df5c", // vCAKE + "0x26DA28954763B92139ED49283625ceCAf52C6f94", // vAAVE + "0x08CEB3F4a7ed3500cA0982bcd0FC7816688084c3", // vTUSDOLD + "0x61eDcFe8Dd6bA3c891CB9bEc2dc7657B3B422E93", // vTRXOLD + "0xC5D3466aA484B040eE977073fcF337f2c00071c1", // vTRX + "0x6CFdEc747f37DAf3b87a35a1D9c8AD3063A1A8A0", // vWBETH + "0xBf762cd5991cA1DCdDaC9ae5C638F5B5Dc3Bee6E", // vTUSD + "0x27FF564707786720C71A2e5c1490A63266683612", // vUNI + "0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba", // vFDUSD + "0x4d41a36D04D97785bcEA57b057C412b278e6Edcc", // vTWT + "0xf841cb62c19fCd4fF5CD0AaB5939f3140BaaC3Ea", // vSolvBTC + "0x86e06EAfa6A1eA631Eab51DE500E3D474933739f", // vTHE + "0xBf515bA4D1b52FFdCeaBF20d31D705Ce789F2cEC", // vSOL + "0x689E0daB47Ab16bcae87Ec18491692BF621Dc6Ab", // vlisUSD + "0x9e4E5fed5Ac5B9F732d0D850A615206330Bf1866", // vPT-sUSDE-26JUN2025 + "0x699658323d58eE25c69F1a29d476946ab011bD18", // vsUSDe + "0x74ca6930108F775CC667894EEa33843e691680d7", // vUSDe + "0x0C1DA220D301155b87318B90692Da8dc43B67340", // vUSD1 + "0xd804dE60aFD05EE6B89aab5D152258fD461B07D5", // vxSolvBTC + "0xCC1dB43a06d97f736C7B045AedD03C6707c09BDF", // vasBNB + "0x6bCa74586218dB34cdB402295796b79663d816e9", // vWBNB + "0x89c910Eb8c90df818b4649b508Ba22130Dc73Adc", // vslisBNB + "0x3d5E269787d562b74aCC55F18Bd26C5D09Fa245E", // vU + "0x6d3BD68E90B42615cb5abF4B8DE92b154ADc435e", // vPT-clisBNB-25JUN2026 + "0x92e6Ea74a1A3047DabF4186405a21c7D63a0612A", // vXAUM +]; + +const MARKET_NAMES = [ + "vUSDC", + "vUSDT", + "vBUSD", + "vSXP", + "vXVS", + "vBTC", + "vETH", + "vLTC", + "vXRP", + "vBCH", + "vDOT", + "vLINK", + "vDAI", + "vFIL", + "vBETH", + "vADA", + "vDOGE", + "vMATIC", + "vCAKE", + "vAAVE", + "vTUSDOLD", + "vTRXOLD", + "vTRX", + "vWBETH", + "vTUSD", + "vUNI", + "vFDUSD", + "vTWT", + "vSolvBTC", + "vTHE", + "vSOL", + "vlisUSD", + "vPT-sUSDE-26JUN2025", + "vsUSDe", + "vUSDe", + "vUSD1", + "vxSolvBTC", + "vasBNB", + "vWBNB", + "vslisBNB", + "vU", + "vPT-clisBNB-25JUN2026", + "vXAUM", +]; + +interface MarketSnapshot { + totalBorrows: BigNumber; + totalReserves: BigNumber; + totalSupply: BigNumber; + exchangeRate: BigNumber; +} + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const preVipSnapshots: Map = new Map(); + + before(async () => { + // Capture pre-VIP state for all markets + for (let i = 0; i < MARKET_ADDRESSES.length; i++) { + const vToken = new ethers.Contract(MARKET_ADDRESSES[i], VBEP20_DELEGATE_ABI, provider); + const [totalBorrows, totalReserves, totalSupply, exchangeRate] = await Promise.all([ + vToken.totalBorrows(), + vToken.totalReserves(), + vToken.totalSupply(), + vToken.exchangeRateStored(), + ]); + preVipSnapshots.set(MARKET_ADDRESSES[i], { totalBorrows, totalReserves, totalSupply, exchangeRate }); + } + }); + + testVip("VIP-608 Upgrade VBep20Delegate for Core Pool", await vip608()); + + describe("Post-VIP behavior", async () => { + for (let i = 0; i < MARKET_ADDRESSES.length; i++) { + const marketAddress = MARKET_ADDRESSES[i]; + const marketName = MARKET_NAMES[i]; + + describe(`${marketName} (${marketAddress})`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(marketAddress, VBEP20_DELEGATE_ABI, provider); + }); + + it("should have the new implementation", async () => { + expect(await vToken.implementation()).to.equal(NEW_VBEP20_DELEGATE_IMPL); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(marketAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should preserve totalSupply", async () => { + const snapshot = preVipSnapshots.get(marketAddress)!; + expect(await vToken.totalSupply()).to.equal(snapshot.totalSupply); + }); + + it("should allow accrueInterest to succeed", async () => { + // accrueInterest should work correctly with synced internalCash + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/vips/vip-608/bscmainnet.ts b/vips/vip-608/bscmainnet.ts new file mode 100644 index 000000000..ebbc3b331 --- /dev/null +++ b/vips/vip-608/bscmainnet.ts @@ -0,0 +1,137 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +// TODO: Replace with the deployed VBep20Delegate implementation address +const NEW_VBEP20_DELEGATE_IMPL = "0xb25b57599BA969c4829699F7E4Fc4076D14745E1"; + +// VBep20Delegator proxy addresses (43 markets, excludes vBNB) +const vUSDC = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; +const vUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; +const vBUSD = "0x95c78222B3D6e262426483D42CfA53685A67Ab9D"; +const vSXP = "0x2fF3d0F6990a40261c66E1ff2017aCBc282EB6d0"; +const vXVS = "0x151B1e2635A717bcDc836ECd6FbB62B674FE3E1D"; +const vBTC = "0x882C173bC7Ff3b7786CA16dfeD3DFFfb9Ee7847B"; +const vETH = "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8"; +const vLTC = "0x57A5297F2cB2c0AaC9D554660acd6D385Ab50c6B"; +const vXRP = "0xB248a295732e0225acd3337607cc01068e3b9c10"; +const vBCH = "0x5F0388EBc2B94FA8E123F404b79cCF5f40b29176"; +const vDOT = "0x1610bc33319e9398de5f57B33a5b184c806aD217"; +const vLINK = "0x650b940a1033B8A1b1873f78730FcFC73ec11f1f"; +const vDAI = "0x334b3eCB4DCa3593BCCC3c7EBD1A1C1d1780FBF1"; +const vFIL = "0xf91d58b5aE142DAcC749f58A49FCBac340Cb0343"; +const vBETH = "0x972207A639CC1B374B893cc33Fa251b55CEB7c07"; +const vADA = "0x9A0AF7FDb2065Ce470D72664DE73cAE409dA28Ec"; +const vDOGE = "0xec3422Ef92B2fb59e84c8B02Ba73F1fE84Ed8D71"; +const vMATIC = "0x5c9476FcD6a4F9a3654139721c949c2233bBbBc8"; +const vCAKE = "0x86aC3974e2BD0d60825230fa6F355fF11409df5c"; +const vAAVE = "0x26DA28954763B92139ED49283625ceCAf52C6f94"; +const vTUSDOLD = "0x08CEB3F4a7ed3500cA0982bcd0FC7816688084c3"; +const vTRXOLD = "0x61eDcFe8Dd6bA3c891CB9bEc2dc7657B3B422E93"; +const vTRX = "0xC5D3466aA484B040eE977073fcF337f2c00071c1"; +const vWBETH = "0x6CFdEc747f37DAf3b87a35a1D9c8AD3063A1A8A0"; +const vTUSD = "0xBf762cd5991cA1DCdDaC9ae5C638F5B5Dc3Bee6E"; +const vUNI = "0x27FF564707786720C71A2e5c1490A63266683612"; +const vFDUSD = "0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba"; +const vTWT = "0x4d41a36D04D97785bcEA57b057C412b278e6Edcc"; +const vSolvBTC = "0xf841cb62c19fCd4fF5CD0AaB5939f3140BaaC3Ea"; +const vTHE = "0x86e06EAfa6A1eA631Eab51DE500E3D474933739f"; +const vSOL = "0xBf515bA4D1b52FFdCeaBF20d31D705Ce789F2cEC"; +const vlisUSD = "0x689E0daB47Ab16bcae87Ec18491692BF621Dc6Ab"; +const vPT_sUSDE_26JUN2025 = "0x9e4E5fed5Ac5B9F732d0D850A615206330Bf1866"; +const vsUSDe = "0x699658323d58eE25c69F1a29d476946ab011bD18"; +const vUSDe = "0x74ca6930108F775CC667894EEa33843e691680d7"; +const vUSD1 = "0x0C1DA220D301155b87318B90692Da8dc43B67340"; +const vxSolvBTC = "0xd804dE60aFD05EE6B89aab5D152258fD461B07D5"; +const vasBNB = "0xCC1dB43a06d97f736C7B045AedD03C6707c09BDF"; +const vWBNB = "0x6bCa74586218dB34cdB402295796b79663d816e9"; +const vslisBNB = "0x89c910Eb8c90df818b4649b508Ba22130Dc73Adc"; +const vU = "0x3d5E269787d562b74aCC55F18Bd26C5D09Fa245E"; +const vPT_clisBNB_25JUN2026 = "0x6d3BD68E90B42615cb5abF4B8DE92b154ADc435e"; +const vXAUM = "0x92e6Ea74a1A3047DabF4186405a21c7D63a0612A"; + +// Markets and their excess token amounts to sweep before syncing +// TODO: Verify excess amounts on-chain before proposal. Use 0 for markets with no excess. +interface MarketConfig { + vToken: string; + name: string; + excessAmount: string; +} + +const MARKETS: MarketConfig[] = [ + { vToken: vUSDC, name: "vUSDC", excessAmount: "0" }, + { vToken: vUSDT, name: "vUSDT", excessAmount: "0" }, + { vToken: vBUSD, name: "vBUSD", excessAmount: "0" }, + { vToken: vSXP, name: "vSXP", excessAmount: "0" }, + { vToken: vXVS, name: "vXVS", excessAmount: "0" }, + { vToken: vBTC, name: "vBTC", excessAmount: "0" }, + { vToken: vETH, name: "vETH", excessAmount: "0" }, + { vToken: vLTC, name: "vLTC", excessAmount: "0" }, + { vToken: vXRP, name: "vXRP", excessAmount: "0" }, + { vToken: vBCH, name: "vBCH", excessAmount: "0" }, + { vToken: vDOT, name: "vDOT", excessAmount: "0" }, + { vToken: vLINK, name: "vLINK", excessAmount: "0" }, + { vToken: vDAI, name: "vDAI", excessAmount: "0" }, + { vToken: vFIL, name: "vFIL", excessAmount: "0" }, + { vToken: vBETH, name: "vBETH", excessAmount: "0" }, + { vToken: vADA, name: "vADA", excessAmount: "0" }, + { vToken: vDOGE, name: "vDOGE", excessAmount: "0" }, + { vToken: vMATIC, name: "vMATIC", excessAmount: "0" }, + { vToken: vCAKE, name: "vCAKE", excessAmount: "0" }, + { vToken: vAAVE, name: "vAAVE", excessAmount: "0" }, + { vToken: vTUSDOLD, name: "vTUSDOLD", excessAmount: "0" }, + { vToken: vTRXOLD, name: "vTRXOLD", excessAmount: "0" }, + { vToken: vTRX, name: "vTRX", excessAmount: "0" }, + { vToken: vWBETH, name: "vWBETH", excessAmount: "0" }, + { vToken: vTUSD, name: "vTUSD", excessAmount: "0" }, + { vToken: vUNI, name: "vUNI", excessAmount: "0" }, + { vToken: vFDUSD, name: "vFDUSD", excessAmount: "0" }, + { vToken: vTWT, name: "vTWT", excessAmount: "0" }, + { vToken: vSolvBTC, name: "vSolvBTC", excessAmount: "0" }, + { vToken: vTHE, name: "vTHE", excessAmount: "0" }, + { vToken: vSOL, name: "vSOL", excessAmount: "0" }, + { vToken: vlisUSD, name: "vlisUSD", excessAmount: "0" }, + { vToken: vPT_sUSDE_26JUN2025, name: "vPT-sUSDE-26JUN2025", excessAmount: "0" }, + { vToken: vsUSDe, name: "vsUSDe", excessAmount: "0" }, + { vToken: vUSDe, name: "vUSDe", excessAmount: "0" }, + { vToken: vUSD1, name: "vUSD1", excessAmount: "0" }, + { vToken: vxSolvBTC, name: "vxSolvBTC", excessAmount: "0" }, + { vToken: vasBNB, name: "vasBNB", excessAmount: "0" }, + { vToken: vWBNB, name: "vWBNB", excessAmount: "0" }, + { vToken: vslisBNB, name: "vslisBNB", excessAmount: "0" }, + { vToken: vU, name: "vU", excessAmount: "0" }, + { vToken: vPT_clisBNB_25JUN2026, name: "vPT-clisBNB-25JUN2026", excessAmount: "0" }, + { vToken: vXAUM, name: "vXAUM", excessAmount: "0" }, +]; + +export const vip608 = () => { + const meta = { + version: "v2", + title: "VIP-608 Upgrade VBep20Delegate implementation for Core Pool markets", + description: + "Upgrade all 43 VBep20Delegator core pool markets on BSC mainnet to the new VBep20Delegate implementation. " + + "For each market: (1) set the new implementation, (2) call sweepTokenAndSync to initialize internalCash. " + + "Markets with excess tokens have their excess swept to the Timelock before syncing.", + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + MARKETS.flatMap(({ vToken, excessAmount }) => [ + { + target: vToken, + signature: "_setImplementation(address,bool,bytes)", + params: [NEW_VBEP20_DELEGATE_IMPL, false, "0x"], + }, + { + target: vToken, + signature: "sweepTokenAndSync(uint256)", + params: [excessAmount], + }, + ]), + meta, + ProposalType.REGULAR, + ); +}; + +export default vip608; From 98adfa14f3a5322eb9a77ba2f54d63226835e7e7 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Tue, 17 Mar 2026 23:33:09 +0530 Subject: [PATCH 02/12] feat: add per-network address constants for VIP-608 - Isolated pool VToken upgrade across 7 networks: Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, ZkSync - Each file exports: - Core comptroller address (queried on-chain via getAllMarkets()) - VTokenBeacon proxy address - New VToken implementation placeholder (TODO, not yet deployed) - LayerZero destination chain ID - Core pool VToken addresses array with symbol comments - Total: 23 (ETH) + 7 (ARB) + 5 (OP) + 5 (Base) + 5 (opBNB) + 7 (Unichain) + 9 (ZkSync) = 61 Core VTokens --- vips/vip-608/addresses/arbitrumone.ts | 16 ++++++++++++ vips/vip-608/addresses/basemainnet.ts | 14 ++++++++++ vips/vip-608/addresses/ethereum.ts | 32 +++++++++++++++++++++++ vips/vip-608/addresses/opbnbmainnet.ts | 14 ++++++++++ vips/vip-608/addresses/opmainnet.ts | 14 ++++++++++ vips/vip-608/addresses/unichainmainnet.ts | 16 ++++++++++++ vips/vip-608/addresses/zksyncmainnet.ts | 18 +++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 vips/vip-608/addresses/arbitrumone.ts create mode 100644 vips/vip-608/addresses/basemainnet.ts create mode 100644 vips/vip-608/addresses/ethereum.ts create mode 100644 vips/vip-608/addresses/opbnbmainnet.ts create mode 100644 vips/vip-608/addresses/opmainnet.ts create mode 100644 vips/vip-608/addresses/unichainmainnet.ts create mode 100644 vips/vip-608/addresses/zksyncmainnet.ts diff --git a/vips/vip-608/addresses/arbitrumone.ts b/vips/vip-608/addresses/arbitrumone.ts new file mode 100644 index 000000000..854fecb62 --- /dev/null +++ b/vips/vip-608/addresses/arbitrumone.ts @@ -0,0 +1,16 @@ +import { LzChainId } from "src/types"; + +export const ARBITRUMONE_CORE_COMPTROLLER = "0x317c1A5739F39046E20b08ac9BeEa3f10fD43326"; +export const ARBITRUMONE_VTOKEN_BEACON = "0xE9381D8CA7006c12Ae9eB97890575E705996fa66"; +export const ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ARBITRUMONE_DST_CHAIN_ID = LzChainId.arbitrumone; + +export const ARBITRUMONE_CORE_VTOKENS = [ + "0xada57840b372d4c28623e87fc175de8490792811", // vWBTC_Core + "0x68a34332983f4bf866768dd6d6e638b02ef5e1f0", // vWETH_Core + "0x7d8609f8da70ff9027e9bc5229af4f6727662707", // vUSDC_Core + "0xb9f9117d4200dc296f9acd1e8be1937df834a2fd", // vUSDT_Core + "0xaeb0fed69354f34831fe1d16475d9a83ddacada6", // vARB_Core + "0x9bb8cec9c0d46f53b4f2173bb2a0221f66c353cc", // vgmWETH-USDC_Core + "0x4f3a73f318c5ea67a86eaace24309f29f89900df", // vgmBTC-USDC_Core +]; diff --git a/vips/vip-608/addresses/basemainnet.ts b/vips/vip-608/addresses/basemainnet.ts new file mode 100644 index 000000000..bbb2b3be1 --- /dev/null +++ b/vips/vip-608/addresses/basemainnet.ts @@ -0,0 +1,14 @@ +import { LzChainId } from "src/types"; + +export const BASEMAINNET_CORE_COMPTROLLER = "0x0C7973F9598AA62f9e03B94E92C967fD5437426C"; +export const BASEMAINNET_VTOKEN_BEACON = "0x87a6476510368c4Bfb70d04A3B0e5a881eC7f0d1"; +export const BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const BASEMAINNET_DST_CHAIN_ID = LzChainId.basemainnet; + +export const BASEMAINNET_CORE_VTOKENS = [ + "0x7bBd1005bB24Ec84705b04e1f2DfcCad533b6D72", // vcbBTC_Core + "0xEB8A79BD44cF4500943bf94a2b4434c95C008599", // vWETH_Core + "0x3cb752d175740043Ec463673094e06ACDa2F9a2e", // vUSDC_Core + "0x75201d81b3B0b9D17b179118837BE37F64fc4930", // vwsuperOETHb_Core + "0x133d3bcd77158d125b75a17cb517ffd4b4be64c5", // vwstETH_Core +]; diff --git a/vips/vip-608/addresses/ethereum.ts b/vips/vip-608/addresses/ethereum.ts new file mode 100644 index 000000000..1edd5b3ed --- /dev/null +++ b/vips/vip-608/addresses/ethereum.ts @@ -0,0 +1,32 @@ +import { LzChainId } from "src/types"; + +export const ETHEREUM_CORE_COMPTROLLER = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B"; +export const ETHEREUM_VTOKEN_BEACON = "0xfc08aADC7a1A93857f6296C3fb78aBA1d286533a"; +export const ETHEREUM_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ETHEREUM_DST_CHAIN_ID = LzChainId.ethereum; + +export const ETHEREUM_CORE_VTOKENS = [ + "0x8716554364f20bca783cb2baa744d39361fd1d8d", // vWBTC_Core + "0x7c8ff7d2a1372433726f879bd945ffb250b94c65", // vWETH_Core + "0x17c07e0c232f2f80dfdbd7a95b942d893a4c5acb", // vUSDC_Core + "0x8c3e3821259b82ffb32b2450a95d2dcbf161c24e", // vUSDT_Core + "0x672208c10aaaa2f9a6719f449c4c8227bc0bc202", // vcrvUSD_Core + "0xd8add9b41d4e1cd64edad8722ab0ba8d35536657", // vDAI_Core + "0x13eb80fdbe5c5f4a7039728e258a6f05fb3b912b", // vTUSD_Core + "0x4fafbdc4f2a9876bd1764827b26fb8dc4fd1db95", // vFRAX_Core + "0x17142a05fe678e9584fa1d88efac1bf181bf7abe", // vsFRAX_Core + "0x256addbe0a387c98f487e44b85c29eb983413c5e", // vEIGEN + "0x325ceb02fe1c2ff816a83a5770ea0e88e2faecf2", // veBTC + "0x25c20e6e110a1ce3febacc8b7e48368c7b2f0c91", // vLBTC_Core + "0x0c6b19287999f1e31a5c0a44393b24b62d2c0468", // vUSDS_Core + "0xe36ae842dbbd7ae372eba02c8239cd431cc063d6", // vsUSDS_Core + "0x0ec5488e4f8f319213a14cab188e01fb8517faa8", // vBAL_Core + "0xf87c0a64dc3a8622d6c63265fa29137788163879", // vyvUSDC-1_Core + "0x475d0c68a8cd275c15d1f01f4f291804e445f677", // vyvUSDT-1_Core + "0x520d67226bc904ac122dce66ed2f8f61aa1ed764", // vyvUSDS-1_Core + "0xba3916302cba4abcb51a01e706fc6051aaf272a0", // vyvWETH-1_Core + "0xc42e4bfb996ed35235bda505430cbe404eb49f77", // vweETHs_Core + "0xa836ce315b7a6bb19397ee996551659b1d92298e", // vsUSDe_Core + "0xa0ee2baa024cc3aa1bc9395522d07b7970ca75b3", // vUSDe_Core + "0x5e35c312862d53fd566737892adcf010cb4928f7", // vtBTC_Core +]; diff --git a/vips/vip-608/addresses/opbnbmainnet.ts b/vips/vip-608/addresses/opbnbmainnet.ts new file mode 100644 index 000000000..d7a8b7a6b --- /dev/null +++ b/vips/vip-608/addresses/opbnbmainnet.ts @@ -0,0 +1,14 @@ +import { LzChainId } from "src/types"; + +export const OPBNBMAINNET_CORE_COMPTROLLER = "0xD6e3E2A1d8d95caE355D15b3b9f8E5c2511874dd"; +export const OPBNBMAINNET_VTOKEN_BEACON = "0xfeD1d3a13597c5aBc893Af41ED5cb17e64c847c7"; +export const OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const OPBNBMAINNET_DST_CHAIN_ID = LzChainId.opbnbmainnet; + +export const OPBNBMAINNET_CORE_VTOKENS = [ + "0xed827b80bd838192ea95002c01b5c6da8354219a", // vBTCB_Core + "0x509e81ef638d489936fa85bc58f52df01190d26c", // vETH_Core + "0xb7a01ba126830692238521a1aa7e7a7509410b8e", // vUSDT_Core + "0x53d11cb8a0e5320cd7229c3acc80d1a0707f2672", // vWBNB_Core + "0x13b492b8a03d072bab5c54ac91dba5b830a50917", // vFDUSD_Core +]; diff --git a/vips/vip-608/addresses/opmainnet.ts b/vips/vip-608/addresses/opmainnet.ts new file mode 100644 index 000000000..986d5024f --- /dev/null +++ b/vips/vip-608/addresses/opmainnet.ts @@ -0,0 +1,14 @@ +import { LzChainId } from "src/types"; + +export const OPMAINNET_CORE_COMPTROLLER = "0x5593FF68bE84C966821eEf5F0a988C285D5B7CeC"; +export const OPMAINNET_VTOKEN_BEACON = "0xd550Bdfa9402e215De0BabCb99F7294BE0268367"; +export const OPMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const OPMAINNET_DST_CHAIN_ID = LzChainId.opmainnet; + +export const OPMAINNET_CORE_VTOKENS = [ + "0x9efdcfc2373f81d3df24647b1c46e15268884c46", // vWBTC_Core + "0x66d5ae25731ce99d46770745385e662c8e0b4025", // vWETH_Core + "0x37ac9731b0b02df54975cd0c7240e0977a051721", // vUSDT_Core + "0x6b846e3418455804c1920fa4cc7a31a51c659a2d", // vOP_Core + "0x1c9406ee95b7af55f005996947b19f91b6d55b15", // vUSDC_Core +]; diff --git a/vips/vip-608/addresses/unichainmainnet.ts b/vips/vip-608/addresses/unichainmainnet.ts new file mode 100644 index 000000000..d215afa84 --- /dev/null +++ b/vips/vip-608/addresses/unichainmainnet.ts @@ -0,0 +1,16 @@ +import { LzChainId } from "src/types"; + +export const UNICHAINMAINNET_CORE_COMPTROLLER = "0xe22af1e6b78318e1Fe1053Edbd7209b8Fc62c4Fe"; +export const UNICHAINMAINNET_VTOKEN_BEACON = "0x42c1Efb9Dd9424c5ac8e6EcEa4eb03940c4a15Fc"; +export const UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const UNICHAINMAINNET_DST_CHAIN_ID = LzChainId.unichainmainnet; + +export const UNICHAINMAINNET_CORE_VTOKENS = [ + "0xc219bc179c7cdb37eacb03f993f9fdc2495e3374", // vWETH_Core + "0xb953f92b9f759d97d2f2dec10a8a3cf75fce3a95", // vUSDC_Core + "0x67716d6bf76170af816f5735e14c4d44d0b05ed2", // vUNI_Core + "0x0170398083eb0d0387709523bafca6426146c218", // vweETH_Core + "0xbec19bef402c697a7be315d3e59e5f65b89fa1bb", // vwstETH_Core + "0x68e2a6f7257fac2f5a557b9e83e1fe6d5b408ce5", // vWBTC_Core + "0xda7ce7ba016d266645712e2e4ebc6cc75ea8e4cd", // vUSD₮0_Core +]; diff --git a/vips/vip-608/addresses/zksyncmainnet.ts b/vips/vip-608/addresses/zksyncmainnet.ts new file mode 100644 index 000000000..2c3a21b16 --- /dev/null +++ b/vips/vip-608/addresses/zksyncmainnet.ts @@ -0,0 +1,18 @@ +import { LzChainId } from "src/types"; + +export const ZKSYNCMAINNET_CORE_COMPTROLLER = "0xddE4D098D9995B659724ae6d5E3FB9681Ac941B1"; +export const ZKSYNCMAINNET_VTOKEN_BEACON = "0x53523537aa330640B80400EB8B309fF5896E7eb5"; +export const ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ZKSYNCMAINNET_DST_CHAIN_ID = LzChainId.zksyncmainnet; + +export const ZKSYNCMAINNET_CORE_VTOKENS = [ + "0xaf8fd83cfcbe963211faaf1847f0f217f80b4719", // vWBTC_Core + "0x1fa916c27c7c2c4602124a14c77dbb40a5ff1be8", // vWETH_Core + "0x1af23bd57c62a99c59ad48236553d0dd11e49d2d", // vUSDC.e_Core + "0x69cda960e3b20dfd480866fffd377ebe40bd0a46", // vUSDT_Core + "0x697a70779c1a03ba2bd28b7627a902bff831b616", // vZK_Core + "0x84064c058f2efea4ab648bb6bd7e40f83ffde39a", // vUSDC_Core + "0x183de3c349fcf546aae925e1c7f364ea6fb4033c", // vwUSDM_Core + "0x03cad66259f7f34ee075f8b62d133563d249eda4", // vwstETH_Core + "0xceb7da150d16ace58f090754fef2775c23c8b631", // vzkETH_Core +]; From fe875576157323d8e9480e584dfab9ee9a88cf69 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Tue, 17 Mar 2026 23:36:52 +0530 Subject: [PATCH 03/12] feat: add VIP-608 cross-chain proposal for isolated pool VToken upgrade - Upgrade VTokenBeacon on 7 networks via upgradeTo(address) through LayerZero - Call syncCash() on each Core comptroller VToken to initialize internalCash --- vips/vip-608/bscmainnet-2.ts | 140 +++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 vips/vip-608/bscmainnet-2.ts diff --git a/vips/vip-608/bscmainnet-2.ts b/vips/vip-608/bscmainnet-2.ts new file mode 100644 index 000000000..b199440f1 --- /dev/null +++ b/vips/vip-608/bscmainnet-2.ts @@ -0,0 +1,140 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + ARBITRUMONE_CORE_VTOKENS, + ARBITRUMONE_DST_CHAIN_ID, + ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + ARBITRUMONE_VTOKEN_BEACON, +} from "./addresses/arbitrumone"; +import { + BASEMAINNET_CORE_VTOKENS, + BASEMAINNET_DST_CHAIN_ID, + BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + BASEMAINNET_VTOKEN_BEACON, +} from "./addresses/basemainnet"; +import { + ETHEREUM_CORE_VTOKENS, + ETHEREUM_DST_CHAIN_ID, + ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + ETHEREUM_VTOKEN_BEACON, +} from "./addresses/ethereum"; +import { + OPBNBMAINNET_CORE_VTOKENS, + OPBNBMAINNET_DST_CHAIN_ID, + OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPBNBMAINNET_VTOKEN_BEACON, +} from "./addresses/opbnbmainnet"; +import { + OPMAINNET_CORE_VTOKENS, + OPMAINNET_DST_CHAIN_ID, + OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPMAINNET_VTOKEN_BEACON, +} from "./addresses/opmainnet"; +import { + UNICHAINMAINNET_CORE_VTOKENS, + UNICHAINMAINNET_DST_CHAIN_ID, + UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + UNICHAINMAINNET_VTOKEN_BEACON, +} from "./addresses/unichainmainnet"; +import { + ZKSYNCMAINNET_CORE_VTOKENS, + ZKSYNCMAINNET_DST_CHAIN_ID, + ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + ZKSYNCMAINNET_VTOKEN_BEACON, +} from "./addresses/zksyncmainnet"; + +interface NetworkConfig { + name: string; + vTokenBeacon: string; + newImplementation: string; + dstChainId: number; + coreVTokens: string[]; +} + +const NETWORKS: NetworkConfig[] = [ + { + name: "Ethereum", + vTokenBeacon: ETHEREUM_VTOKEN_BEACON, + newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ETHEREUM_DST_CHAIN_ID, + coreVTokens: ETHEREUM_CORE_VTOKENS, + }, + { + name: "Arbitrum", + vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, + newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ARBITRUMONE_DST_CHAIN_ID, + coreVTokens: ARBITRUMONE_CORE_VTOKENS, + }, + { + name: "Optimism", + vTokenBeacon: OPMAINNET_VTOKEN_BEACON, + newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPMAINNET_DST_CHAIN_ID, + coreVTokens: OPMAINNET_CORE_VTOKENS, + }, + { + name: "Base", + vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, + newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: BASEMAINNET_DST_CHAIN_ID, + coreVTokens: BASEMAINNET_CORE_VTOKENS, + }, + { + name: "opBNB", + vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, + newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPBNBMAINNET_DST_CHAIN_ID, + coreVTokens: OPBNBMAINNET_CORE_VTOKENS, + }, + { + name: "Unichain", + vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, + newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, + coreVTokens: UNICHAINMAINNET_CORE_VTOKENS, + }, + { + name: "ZkSync", + vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, + newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, + coreVTokens: ZKSYNCMAINNET_CORE_VTOKENS, + }, +]; + +export const vip608_2 = () => { + const meta = { + version: "v2", + title: "VIP-608 Upgrade isolated pool VToken implementation across all networks", + description: + "Upgrade VToken beacon proxies on 7 networks (Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, ZkSync). " + + "For each network: (1) call upgradeTo on the VTokenBeacon to upgrade all VToken proxies, " + + "(2) call syncCash on each Core comptroller VToken to initialize internalCash.", + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + NETWORKS.flatMap(({ vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + { + target: vTokenBeacon, + signature: "upgradeTo(address)", + params: [newImplementation], + dstChainId, + }, + ...coreVTokens.map(vToken => ({ + target: vToken, + signature: "syncCash()", + params: [], + dstChainId, + })), + ]), + meta, + ProposalType.REGULAR, + ); +}; + +export default vip608_2; From bdd61a7d55169156805c152a37d299c27027387f Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Tue, 17 Mar 2026 23:39:22 +0530 Subject: [PATCH 04/12] feat: add isolated pool ABIs for VIP-608 simulations - ILVToken (syncCash, internalCash, accrueInterest, underlying, etc.) - ILComptroller (getAllMarkets) - ILPoolRegistry (getAllPools) - vtokenBeacon (upgradeTo, implementation) --- simulations/vip-608/abi/ILComptroller.json | 2121 +++++++++++++++++++ simulations/vip-608/abi/ILPoolRegistry.json | 680 ++++++ simulations/vip-608/abi/ILVToken.json | 2105 ++++++++++++++++++ simulations/vip-608/abi/vtokenBeacon.json | 51 + 4 files changed, 4957 insertions(+) create mode 100644 simulations/vip-608/abi/ILComptroller.json create mode 100644 simulations/vip-608/abi/ILPoolRegistry.json create mode 100644 simulations/vip-608/abi/ILVToken.json create mode 100644 simulations/vip-608/abi/vtokenBeacon.json diff --git a/simulations/vip-608/abi/ILComptroller.json b/simulations/vip-608/abi/ILComptroller.json new file mode 100644 index 000000000..d1801efd8 --- /dev/null +++ b/simulations/vip-608/abi/ILComptroller.json @@ -0,0 +1,2121 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistry_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "ActionPaused", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "BorrowCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowCapIsNotZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expectedLessThanOrEqualTo", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "CollateralExceedsThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "CollateralFactorIsNotZero", + "type": "error" + }, + { + "inputs": [], + "name": "ComptrollerMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "DelegationStatusUnchanged", + "type": "error" + }, + { + "inputs": [], + "name": "EnterMarketActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "ExitMarketActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "collateralToSeize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableCollateral", + "type": "uint256" + } + ], + "name": "InsufficientCollateral", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientShortfall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLiquidationThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketAlreadyListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "MarketNotCollateral", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "loopsLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requiredLoops", + "type": "uint256" + } + ], + "name": "MaxLoopsLimitExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expectedGreaterThan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "MinimalCollateralViolated", + "type": "error" + }, + { + "inputs": [], + "name": "MintActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroBorrowBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "PriceError", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "RepayActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "SeizeActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "SnapshotError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "SupplyCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "SupplyCapIsNotZero", + "type": "error" + }, + { + "inputs": [], + "name": "TooMuchRepay", + "type": "error" + }, + { + "inputs": [], + "name": "TransferActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expectedSender", + "type": "address" + }, + { + "internalType": "address", + "name": "actualSender", + "type": "address" + } + ], + "name": "UnexpectedSender", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum Action", + "name": "action", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "pauseState", + "type": "bool" + } + ], + "name": "ActionPausedMarket", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "approver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "DelegateUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "IsForcedLiquidationEnabledUpdated", + "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": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketUnlisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMaxLoopsLimit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newmaxLoopsLimit", + "type": "uint256" + } + ], + "name": "MaxLoopsLimitUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBorrowCap", + "type": "uint256" + } + ], + "name": "NewBorrowCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldCloseFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCloseFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldCollateralFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCollateralFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationThresholdMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationThreshold", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinLiquidatableCollateral", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "NewMinLiquidatableCollateral", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "oldPriceOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "newPriceOracle", + "type": "address" + } + ], + "name": "NewPriceOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IPrime", + "name": "oldPrimeToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IPrime", + "name": "newPrimeToken", + "type": "address" + } + ], + "name": "NewPrimeToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "rewardsDistributor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "rewardToken", + "type": "address" + } + ], + "name": "NewRewardsDistributor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSupplyCap", + "type": "uint256" + } + ], + "name": "NewSupplyCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "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": [ + { + "internalType": "contract RewardsDistributor", + "name": "_rewardsDistributor", + "type": "address" + } + ], + "name": "addRewardsDistributor", + "outputs": [], + "stateMutability": "nonpayable", + "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": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "checkMembership", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "enterMarkets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllMarkets", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAssetsIn", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getBorrowingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenModify", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "getHypotheticalAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardDistributors", + "outputs": [ + { + "internalType": "contract RewardsDistributor[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getRewardsByMarket", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplySpeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowSpeed", + "type": "uint256" + } + ], + "internalType": "struct ComptrollerStorage.RewardSpeeds[]", + "name": "rewardSpeeds", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "healAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "loopLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "accessControlManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isComptroller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "isMarketListed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract VToken", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "internalType": "struct ComptrollerStorage.LiquidationOrder[]", + "name": "orders", + "type": "tuple[]" + } + ], + "name": "liquidateAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "liquidateBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokensToSeize", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidationIncentiveMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "markets", + "outputs": [ + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLoopsLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minLiquidatableCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualMintAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + } + ], + "name": "mintVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "preBorrowHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "preLiquidateHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "preMintHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "preRedeemHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preRepayHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "seizerContract", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preSeizeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "preTransferHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "prime", + "outputs": [ + { + "internalType": "contract IPrime", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowerIndex", + "type": "uint256" + } + ], + "name": "repayBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seizeVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "marketsList", + "type": "address[]" + }, + { + "internalType": "enum Action[]", + "name": "actionsList", + "type": "uint8[]" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "setActionsPaused", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "setCloseFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "setCollateralFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "setForcedLiquidation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "setLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "name": "setMarketBorrowCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "name": "setMarketSupplyCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "setMaxLoopsLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "setMinLiquidatableCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "newOracle", + "type": "address" + } + ], + "name": "setPriceOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IPrime", + "name": "_prime", + "type": "address" + } + ], + "name": "setPrimeToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "supportMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "transferVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "unlistMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "updateDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-608/abi/ILPoolRegistry.json b/simulations/vip-608/abi/ILPoolRegistry.json new file mode 100644 index 000000000..b637988ae --- /dev/null +++ b/simulations/vip-608/abi/ILPoolRegistry.json @@ -0,0 +1,680 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "MarketAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "oldMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "newMetadata", + "type": "tuple" + } + ], + "name": "PoolMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "oldName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "PoolNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialSupply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vTokenReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCap", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistry.AddMarketInput", + "name": "input", + "type": "tuple" + } + ], + "name": "addMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract Comptroller", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationIncentive", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getPoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getVenusPoolMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "metadata", + "outputs": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setPoolName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "metadata_", + "type": "tuple" + } + ], + "name": "updatePoolMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-608/abi/ILVToken.json b/simulations/vip-608/abi/ILVToken.json new file mode 100644 index 000000000..c039d2ef1 --- /dev/null +++ b/simulations/vip-608/abi/ILVToken.json @@ -0,0 +1,2105 @@ +[ + { + "inputs": [ + { + "internalType": "bool", + "name": "timeBased_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "blocksPerYear_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBorrowRateMantissa_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "actualAddAmount", + "type": "uint256" + } + ], + "name": "AddReservesFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateNotApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ForceLiquidateBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "HealBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidBlocksPerYear", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTimeBasedConfiguration", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "LiquidateAccrueCollateralInterestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsUintMax", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsZero", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCollateralFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateSeizeLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "MintFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolSeizeShareTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemTransferOutNotPossible", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashValidation", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RepayBorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetInterestRateModelFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorBoundsCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "TransferNotAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "cashPrior", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtRecovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldInternalCash", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newInternalCash", + "type": "uint256" + } + ], + "name": "CashSynced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "HealBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldProtocolSeizeShareMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa", + "type": "uint256" + } + ], + "name": "NewProtocolSeizeShare", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldProtocolShareReserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newProtocolShareReserve", + "type": "address" + } + ], + "name": "NewProtocolShareReserve", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReduceReservesBlockOrTimestampDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "NewReduceReservesBlockDelta", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldShortfall", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newShortfall", + "type": "address" + } + ], + "name": "NewShortfallContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolSeize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "benefactor", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolShareReserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "SpreadReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SweepToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "NO_ERROR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrueInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + } + ], + "name": "addReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "badDebt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "recoveredAmount_", + "type": "uint256" + } + ], + "name": "badDebtRecovered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "blocksOrSecondsPerYear", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "borrowIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "internalType": "contract ComptrollerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "forceLiquidateBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vTokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumberOrTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "healBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying_", + "type": "address" + }, + { + "internalType": "contract ComptrollerInterface", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "contract InterestRateModel", + "name": "interestRateModel_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialExchangeRateMantissa_", + "type": "uint256" + }, + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "shortfall", + "type": "address" + }, + { + "internalType": "address payable", + "name": "protocolShareReserve", + "type": "address" + } + ], + "internalType": "struct VTokenInterface.RiskManagementInit", + "name": "riskManagement", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRateModel", + "outputs": [ + { + "internalType": "contract InterestRateModel", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "internalCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTimeBased", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolSeizeShareMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "reduceReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockDelta", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "setInterestRateModel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa_", + "type": "uint256" + } + ], + "name": "setProtocolSeizeShare", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "protocolShareReserve_", + "type": "address" + } + ], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "setReduceReservesBlockDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "setReserveFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "shortfall_", + "type": "address" + } + ], + "name": "setShortfallContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shortfall", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "syncCash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "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": [ + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-608/abi/vtokenBeacon.json b/simulations/vip-608/abi/vtokenBeacon.json new file mode 100644 index 000000000..fe7f3e52f --- /dev/null +++ b/simulations/vip-608/abi/vtokenBeacon.json @@ -0,0 +1,51 @@ +[ + { + "inputs": [{ "internalType": "address", "name": "implementation_", "type": "address" }], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "address", "name": "implementation", "type": "address" }], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newImplementation", "type": "address" }], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] From 7fc3d1942bdd550d31de14054766b9499a58ab53 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Tue, 17 Mar 2026 23:40:40 +0530 Subject: [PATCH 05/12] feat: add VIP-608 simulation tests for isolated pool VToken upgrade - 7 remote network sims using testForkedNetworkVipCommands (Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, ZkSync) - BSC local sim using testVip for cross-chain dispatch verification - Post-VIP assertions: beacon implementation, internalCash == underlying balance, accrueInterest succeeds --- simulations/vip-608/arbitrumone.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/basemainnet.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/bscmainnet-2.ts | 9 ++++ simulations/vip-608/ethereum.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/opbnbmainnet.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/opmainnet.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/unichainmainnet.ts | 65 ++++++++++++++++++++++++++ simulations/vip-608/zksyncmainnet.ts | 65 ++++++++++++++++++++++++++ 8 files changed, 464 insertions(+) create mode 100644 simulations/vip-608/arbitrumone.ts create mode 100644 simulations/vip-608/basemainnet.ts create mode 100644 simulations/vip-608/bscmainnet-2.ts create mode 100644 simulations/vip-608/ethereum.ts create mode 100644 simulations/vip-608/opbnbmainnet.ts create mode 100644 simulations/vip-608/opmainnet.ts create mode 100644 simulations/vip-608/unichainmainnet.ts create mode 100644 simulations/vip-608/zksyncmainnet.ts diff --git a/simulations/vip-608/arbitrumone.ts b/simulations/vip-608/arbitrumone.ts new file mode 100644 index 000000000..400b1fd95 --- /dev/null +++ b/simulations/vip-608/arbitrumone.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + ARBITRUMONE_CORE_VTOKENS, + ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + ARBITRUMONE_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/arbitrumone"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 443434569; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(ARBITRUMONE_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of ARBITRUMONE_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/basemainnet.ts b/simulations/vip-608/basemainnet.ts new file mode 100644 index 000000000..e9461f0cf --- /dev/null +++ b/simulations/vip-608/basemainnet.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + BASEMAINNET_CORE_VTOKENS, + BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + BASEMAINNET_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/basemainnet"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 43514761; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(BASEMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of BASEMAINNET_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/bscmainnet-2.ts b/simulations/vip-608/bscmainnet-2.ts new file mode 100644 index 000000000..aadb97f90 --- /dev/null +++ b/simulations/vip-608/bscmainnet-2.ts @@ -0,0 +1,9 @@ +import { forking, testVip } from "src/vip-framework"; + +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; + +const BLOCK_NUMBER = 87425358; + +forking(BLOCK_NUMBER, async () => { + testVip("VIP-608 Isolated Pool VToken Upgrade (cross-chain dispatch)", await vip608_2()); +}); diff --git a/simulations/vip-608/ethereum.ts b/simulations/vip-608/ethereum.ts new file mode 100644 index 000000000..f25d27060 --- /dev/null +++ b/simulations/vip-608/ethereum.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + ETHEREUM_CORE_VTOKENS, + ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + ETHEREUM_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/ethereum"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 24678795; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(ETHEREUM_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(ETHEREUM_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(ETHEREUM_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of ETHEREUM_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/opbnbmainnet.ts b/simulations/vip-608/opbnbmainnet.ts new file mode 100644 index 000000000..7c48f7bba --- /dev/null +++ b/simulations/vip-608/opbnbmainnet.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + OPBNBMAINNET_CORE_VTOKENS, + OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPBNBMAINNET_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/opbnbmainnet"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 122734048; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(OPBNBMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of OPBNBMAINNET_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/opmainnet.ts b/simulations/vip-608/opmainnet.ts new file mode 100644 index 000000000..8c8f7018b --- /dev/null +++ b/simulations/vip-608/opmainnet.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + OPMAINNET_CORE_VTOKENS, + OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPMAINNET_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/opmainnet"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 149175577; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(OPMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(OPMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(OPMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of OPMAINNET_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/unichainmainnet.ts b/simulations/vip-608/unichainmainnet.ts new file mode 100644 index 000000000..0dabb48bd --- /dev/null +++ b/simulations/vip-608/unichainmainnet.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + UNICHAINMAINNET_CORE_VTOKENS, + UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + UNICHAINMAINNET_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/unichainmainnet"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 43024435; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(UNICHAINMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of UNICHAINMAINNET_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); diff --git a/simulations/vip-608/zksyncmainnet.ts b/simulations/vip-608/zksyncmainnet.ts new file mode 100644 index 000000000..6ff31c233 --- /dev/null +++ b/simulations/vip-608/zksyncmainnet.ts @@ -0,0 +1,65 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; + +import { + ZKSYNCMAINNET_CORE_VTOKENS, + ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + ZKSYNCMAINNET_VTOKEN_BEACON, +} from "../../vips/vip-608/addresses/zksyncmainnet"; +import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import VTOKEN_ABI from "./abi/ILVToken.json"; +import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; + +const BLOCK_NUMBER = 69140884; + +const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; + +forking(BLOCK_NUMBER, async () => { + const provider = ethers.provider; + const vTokenBeacon = new ethers.Contract(ZKSYNCMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); + + describe("Pre-VIP behaviour", () => { + it("VToken beacon should not point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.not.equal(ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + }); + + testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); + }, + }); + + describe("Post-VIP behaviour", () => { + it("VToken beacon should point to new implementation", async () => { + const currentImplementation = await vTokenBeacon.implementation(); + expect(currentImplementation).to.equal(ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION); + }); + + for (const vTokenAddress of ZKSYNCMAINNET_CORE_VTOKENS) { + describe(`VToken ${vTokenAddress}`, () => { + let vToken: Contract; + + before(async () => { + vToken = new ethers.Contract(vTokenAddress, VTOKEN_ABI, provider); + }); + + it("should have internalCash equal to underlying token balance", async () => { + const internalCash = await vToken.internalCash(); + const underlyingAddress = await vToken.underlying(); + const underlyingToken = new ethers.Contract(underlyingAddress, ERC20_ABI, provider); + const underlyingBalance = await underlyingToken.balanceOf(vTokenAddress); + expect(internalCash).to.equal(underlyingBalance); + }); + + it("should allow accrueInterest to succeed", async () => { + await expect(vToken.callStatic.accrueInterest()).to.not.be.reverted; + }); + }); + } + }); +}); From 301b16704c72652e0f301954a83b56535f6442aa Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Mar 2026 16:08:58 +0530 Subject: [PATCH 06/12] feat: add ACM permissions for syncCash in VIP-608 cross-chain upgrade syncCash() requires AccessControlManager authorization. Add ACM and Normal Timelock addresses to all network address files, fix EIP-55 checksums, and add giveCallPermission commands before upgrade+sync. --- vips/vip-608/addresses/arbitrumone.ts | 2 + vips/vip-608/addresses/basemainnet.ts | 10 +++-- vips/vip-608/addresses/ethereum.ts | 16 ++++---- vips/vip-608/addresses/opbnbmainnet.ts | 2 + vips/vip-608/addresses/opmainnet.ts | 14 ++++--- vips/vip-608/addresses/unichainmainnet.ts | 2 + vips/vip-608/addresses/zksyncmainnet.ts | 6 ++- vips/vip-608/bscmainnet-2.ts | 45 +++++++++++++++++++++-- 8 files changed, 75 insertions(+), 22 deletions(-) diff --git a/vips/vip-608/addresses/arbitrumone.ts b/vips/vip-608/addresses/arbitrumone.ts index 854fecb62..12a2853cd 100644 --- a/vips/vip-608/addresses/arbitrumone.ts +++ b/vips/vip-608/addresses/arbitrumone.ts @@ -1,5 +1,7 @@ import { LzChainId } from "src/types"; +export const ARBITRUMONE_ACM = "0xD9dD18EB0cf10CbA837677f28A8F9Bda4bc2b157"; +export const ARBITRUMONE_NORMAL_TIMELOCK = "0x4b94589Cc23F618687790036726f744D602c4017"; export const ARBITRUMONE_CORE_COMPTROLLER = "0x317c1A5739F39046E20b08ac9BeEa3f10fD43326"; export const ARBITRUMONE_VTOKEN_BEACON = "0xE9381D8CA7006c12Ae9eB97890575E705996fa66"; export const ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed diff --git a/vips/vip-608/addresses/basemainnet.ts b/vips/vip-608/addresses/basemainnet.ts index bbb2b3be1..6c6beaa2b 100644 --- a/vips/vip-608/addresses/basemainnet.ts +++ b/vips/vip-608/addresses/basemainnet.ts @@ -1,14 +1,16 @@ import { LzChainId } from "src/types"; +export const BASEMAINNET_ACM = "0x9E6CeEfDC6183e4D0DF8092A9B90cDF659687daB"; +export const BASEMAINNET_NORMAL_TIMELOCK = "0x21c12f2946a1a66cBFf7eb997022a37167eCf517"; export const BASEMAINNET_CORE_COMPTROLLER = "0x0C7973F9598AA62f9e03B94E92C967fD5437426C"; export const BASEMAINNET_VTOKEN_BEACON = "0x87a6476510368c4Bfb70d04A3B0e5a881eC7f0d1"; -export const BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x867050A5eE96Af41D62160F6e1F4724828eD3D6e"; export const BASEMAINNET_DST_CHAIN_ID = LzChainId.basemainnet; export const BASEMAINNET_CORE_VTOKENS = [ "0x7bBd1005bB24Ec84705b04e1f2DfcCad533b6D72", // vcbBTC_Core - "0xEB8A79BD44cF4500943bf94a2b4434c95C008599", // vWETH_Core + "0xEB8A79bD44cF4500943bf94a2b4434c95C008599", // vWETH_Core "0x3cb752d175740043Ec463673094e06ACDa2F9a2e", // vUSDC_Core - "0x75201d81b3B0b9D17b179118837BE37F64fc4930", // vwsuperOETHb_Core - "0x133d3bcd77158d125b75a17cb517ffd4b4be64c5", // vwstETH_Core + "0x75201D81B3B0b9D17b179118837Be37f64fc4930", // vwsuperOETHb_Core + "0x133d3BCD77158D125B75A17Cb517fFD4B4BE64C5", // vwstETH_Core ]; diff --git a/vips/vip-608/addresses/ethereum.ts b/vips/vip-608/addresses/ethereum.ts index 1edd5b3ed..082bea299 100644 --- a/vips/vip-608/addresses/ethereum.ts +++ b/vips/vip-608/addresses/ethereum.ts @@ -1,5 +1,7 @@ import { LzChainId } from "src/types"; +export const ETHEREUM_ACM = "0x230058da2D23eb8836EC5DB7037ef7250c56E25E"; +export const ETHEREUM_NORMAL_TIMELOCK = "0xd969E79406c35E80750aAae061D402Aab9325714"; export const ETHEREUM_CORE_COMPTROLLER = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B"; export const ETHEREUM_VTOKEN_BEACON = "0xfc08aADC7a1A93857f6296C3fb78aBA1d286533a"; export const ETHEREUM_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed @@ -13,18 +15,18 @@ export const ETHEREUM_CORE_VTOKENS = [ "0x672208c10aaaa2f9a6719f449c4c8227bc0bc202", // vcrvUSD_Core "0xd8add9b41d4e1cd64edad8722ab0ba8d35536657", // vDAI_Core "0x13eb80fdbe5c5f4a7039728e258a6f05fb3b912b", // vTUSD_Core - "0x4fafbdc4f2a9876bd1764827b26fb8dc4fd1db95", // vFRAX_Core + "0x4fafbdc4f2a9876bd1764827b26fb8dc4fd1db95", // vFRAX_Core, Paused "0x17142a05fe678e9584fa1d88efac1bf181bf7abe", // vsFRAX_Core - "0x256addbe0a387c98f487e44b85c29eb983413c5e", // vEIGEN - "0x325ceb02fe1c2ff816a83a5770ea0e88e2faecf2", // veBTC + "0x256addbe0a387c98f487e44b85c29eb983413c5e", // vEIGEN, have differenet proxy + "0x325ceb02fe1c2ff816a83a5770ea0e88e2faecf2", // veBTC, have different proxy "0x25c20e6e110a1ce3febacc8b7e48368c7b2f0c91", // vLBTC_Core "0x0c6b19287999f1e31a5c0a44393b24b62d2c0468", // vUSDS_Core "0xe36ae842dbbd7ae372eba02c8239cd431cc063d6", // vsUSDS_Core "0x0ec5488e4f8f319213a14cab188e01fb8517faa8", // vBAL_Core - "0xf87c0a64dc3a8622d6c63265fa29137788163879", // vyvUSDC-1_Core - "0x475d0c68a8cd275c15d1f01f4f291804e445f677", // vyvUSDT-1_Core - "0x520d67226bc904ac122dce66ed2f8f61aa1ed764", // vyvUSDS-1_Core - "0xba3916302cba4abcb51a01e706fc6051aaf272a0", // vyvWETH-1_Core + "0xf87c0a64dc3a8622d6c63265fa29137788163879", // vyvUSDC-1_Core, Paused + "0x475d0c68a8cd275c15d1f01f4f291804e445f677", // vyvUSDT-1_Core, Paused + "0x520d67226bc904ac122dce66ed2f8f61aa1ed764", // vyvUSDS-1_Core, Paused + "0xba3916302cba4abcb51a01e706fc6051aaf272a0", // vyvWETH-1_Core, Paused "0xc42e4bfb996ed35235bda505430cbe404eb49f77", // vweETHs_Core "0xa836ce315b7a6bb19397ee996551659b1d92298e", // vsUSDe_Core "0xa0ee2baa024cc3aa1bc9395522d07b7970ca75b3", // vUSDe_Core diff --git a/vips/vip-608/addresses/opbnbmainnet.ts b/vips/vip-608/addresses/opbnbmainnet.ts index d7a8b7a6b..c08b0a786 100644 --- a/vips/vip-608/addresses/opbnbmainnet.ts +++ b/vips/vip-608/addresses/opbnbmainnet.ts @@ -1,5 +1,7 @@ import { LzChainId } from "src/types"; +export const OPBNBMAINNET_ACM = "0xA60Deae5344F1152426cA440fb6552eA0e3005D6"; +export const OPBNBMAINNET_NORMAL_TIMELOCK = "0x10f504e939b912569Dca611851fDAC9E3Ef86819"; export const OPBNBMAINNET_CORE_COMPTROLLER = "0xD6e3E2A1d8d95caE355D15b3b9f8E5c2511874dd"; export const OPBNBMAINNET_VTOKEN_BEACON = "0xfeD1d3a13597c5aBc893Af41ED5cb17e64c847c7"; export const OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed diff --git a/vips/vip-608/addresses/opmainnet.ts b/vips/vip-608/addresses/opmainnet.ts index 986d5024f..b44717e68 100644 --- a/vips/vip-608/addresses/opmainnet.ts +++ b/vips/vip-608/addresses/opmainnet.ts @@ -1,14 +1,16 @@ import { LzChainId } from "src/types"; +export const OPMAINNET_ACM = "0xD71b1F33f6B0259683f11174EE4Ddc2bb9cE4eD6"; +export const OPMAINNET_NORMAL_TIMELOCK = "0x0C6f1E6B4fDa846f63A0d5a8a73EB811E0e0C04b"; export const OPMAINNET_CORE_COMPTROLLER = "0x5593FF68bE84C966821eEf5F0a988C285D5B7CeC"; export const OPMAINNET_VTOKEN_BEACON = "0xd550Bdfa9402e215De0BabCb99F7294BE0268367"; -export const OPMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const OPMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0xe6a1B42B0Ba8129fcB51bb105F590f29dcAF1599"; export const OPMAINNET_DST_CHAIN_ID = LzChainId.opmainnet; export const OPMAINNET_CORE_VTOKENS = [ - "0x9efdcfc2373f81d3df24647b1c46e15268884c46", // vWBTC_Core - "0x66d5ae25731ce99d46770745385e662c8e0b4025", // vWETH_Core - "0x37ac9731b0b02df54975cd0c7240e0977a051721", // vUSDT_Core - "0x6b846e3418455804c1920fa4cc7a31a51c659a2d", // vOP_Core - "0x1c9406ee95b7af55f005996947b19f91b6d55b15", // vUSDC_Core + "0x9EfdCfC2373f81D3DF24647B1c46e15268884c46", // vWBTC_Core + "0x66d5AE25731Ce99D46770745385e662C8e0B4025", // vWETH_Core + "0x37ac9731B0B02df54975cd0c7240e0977a051721", // vUSDT_Core + "0x6b846E3418455804C1920fA4CC7a31A51C659A2D", // vOP_Core + "0x1C9406ee95B7af55F005996947b19F91B6D55b15", // vUSDC_Core ]; diff --git a/vips/vip-608/addresses/unichainmainnet.ts b/vips/vip-608/addresses/unichainmainnet.ts index d215afa84..8f5ed5f75 100644 --- a/vips/vip-608/addresses/unichainmainnet.ts +++ b/vips/vip-608/addresses/unichainmainnet.ts @@ -1,5 +1,7 @@ import { LzChainId } from "src/types"; +export const UNICHAINMAINNET_ACM = "0x1f12014c497a9d905155eB9BfDD9FaC6885e61d0"; +export const UNICHAINMAINNET_NORMAL_TIMELOCK = "0x918532A78d22419Da4091930d472bDdf532BE89a"; export const UNICHAINMAINNET_CORE_COMPTROLLER = "0xe22af1e6b78318e1Fe1053Edbd7209b8Fc62c4Fe"; export const UNICHAINMAINNET_VTOKEN_BEACON = "0x42c1Efb9Dd9424c5ac8e6EcEa4eb03940c4a15Fc"; export const UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed diff --git a/vips/vip-608/addresses/zksyncmainnet.ts b/vips/vip-608/addresses/zksyncmainnet.ts index 2c3a21b16..d5a975eaf 100644 --- a/vips/vip-608/addresses/zksyncmainnet.ts +++ b/vips/vip-608/addresses/zksyncmainnet.ts @@ -1,5 +1,7 @@ import { LzChainId } from "src/types"; +export const ZKSYNCMAINNET_ACM = "0x526159A92A82afE5327d37Ef446b68FD9a5cA914"; +export const ZKSYNCMAINNET_NORMAL_TIMELOCK = "0x093565Bc20AA326F4209eBaF3a26089272627613"; export const ZKSYNCMAINNET_CORE_COMPTROLLER = "0xddE4D098D9995B659724ae6d5E3FB9681Ac941B1"; export const ZKSYNCMAINNET_VTOKEN_BEACON = "0x53523537aa330640B80400EB8B309fF5896E7eb5"; export const ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed @@ -12,7 +14,7 @@ export const ZKSYNCMAINNET_CORE_VTOKENS = [ "0x69cda960e3b20dfd480866fffd377ebe40bd0a46", // vUSDT_Core "0x697a70779c1a03ba2bd28b7627a902bff831b616", // vZK_Core "0x84064c058f2efea4ab648bb6bd7e40f83ffde39a", // vUSDC_Core - "0x183de3c349fcf546aae925e1c7f364ea6fb4033c", // vwUSDM_Core + "0x183de3c349fcf546aae925e1c7f364ea6fb4033c", // vwUSDM_Core, Paused "0x03cad66259f7f34ee075f8b62d133563d249eda4", // vwstETH_Core - "0xceb7da150d16ace58f090754fef2775c23c8b631", // vzkETH_Core + "0xceb7da150d16ace58f090754fef2775c23c8b631", // vzkETH_Core, Paused ]; diff --git a/vips/vip-608/bscmainnet-2.ts b/vips/vip-608/bscmainnet-2.ts index b199440f1..60ba1203b 100644 --- a/vips/vip-608/bscmainnet-2.ts +++ b/vips/vip-608/bscmainnet-2.ts @@ -2,50 +2,66 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; import { + ARBITRUMONE_ACM, ARBITRUMONE_CORE_VTOKENS, ARBITRUMONE_DST_CHAIN_ID, ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + ARBITRUMONE_NORMAL_TIMELOCK, ARBITRUMONE_VTOKEN_BEACON, } from "./addresses/arbitrumone"; import { + BASEMAINNET_ACM, BASEMAINNET_CORE_VTOKENS, BASEMAINNET_DST_CHAIN_ID, BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + BASEMAINNET_NORMAL_TIMELOCK, BASEMAINNET_VTOKEN_BEACON, } from "./addresses/basemainnet"; import { + ETHEREUM_ACM, ETHEREUM_CORE_VTOKENS, ETHEREUM_DST_CHAIN_ID, ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + ETHEREUM_NORMAL_TIMELOCK, ETHEREUM_VTOKEN_BEACON, } from "./addresses/ethereum"; import { + OPBNBMAINNET_ACM, OPBNBMAINNET_CORE_VTOKENS, OPBNBMAINNET_DST_CHAIN_ID, OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPBNBMAINNET_NORMAL_TIMELOCK, OPBNBMAINNET_VTOKEN_BEACON, } from "./addresses/opbnbmainnet"; import { + OPMAINNET_ACM, OPMAINNET_CORE_VTOKENS, OPMAINNET_DST_CHAIN_ID, OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPMAINNET_NORMAL_TIMELOCK, OPMAINNET_VTOKEN_BEACON, } from "./addresses/opmainnet"; import { + UNICHAINMAINNET_ACM, UNICHAINMAINNET_CORE_VTOKENS, UNICHAINMAINNET_DST_CHAIN_ID, UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + UNICHAINMAINNET_NORMAL_TIMELOCK, UNICHAINMAINNET_VTOKEN_BEACON, } from "./addresses/unichainmainnet"; import { + ZKSYNCMAINNET_ACM, ZKSYNCMAINNET_CORE_VTOKENS, ZKSYNCMAINNET_DST_CHAIN_ID, ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + ZKSYNCMAINNET_NORMAL_TIMELOCK, ZKSYNCMAINNET_VTOKEN_BEACON, } from "./addresses/zksyncmainnet"; interface NetworkConfig { name: string; + acm: string; + normalTimelock: string; vTokenBeacon: string; newImplementation: string; dstChainId: number; @@ -55,6 +71,8 @@ interface NetworkConfig { const NETWORKS: NetworkConfig[] = [ { name: "Ethereum", + acm: ETHEREUM_ACM, + normalTimelock: ETHEREUM_NORMAL_TIMELOCK, vTokenBeacon: ETHEREUM_VTOKEN_BEACON, newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ETHEREUM_DST_CHAIN_ID, @@ -62,6 +80,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "Arbitrum", + acm: ARBITRUMONE_ACM, + normalTimelock: ARBITRUMONE_NORMAL_TIMELOCK, vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ARBITRUMONE_DST_CHAIN_ID, @@ -69,6 +89,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "Optimism", + acm: OPMAINNET_ACM, + normalTimelock: OPMAINNET_NORMAL_TIMELOCK, vTokenBeacon: OPMAINNET_VTOKEN_BEACON, newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: OPMAINNET_DST_CHAIN_ID, @@ -76,6 +98,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "Base", + acm: BASEMAINNET_ACM, + normalTimelock: BASEMAINNET_NORMAL_TIMELOCK, vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: BASEMAINNET_DST_CHAIN_ID, @@ -83,6 +107,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "opBNB", + acm: OPBNBMAINNET_ACM, + normalTimelock: OPBNBMAINNET_NORMAL_TIMELOCK, vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: OPBNBMAINNET_DST_CHAIN_ID, @@ -90,6 +116,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "Unichain", + acm: UNICHAINMAINNET_ACM, + normalTimelock: UNICHAINMAINNET_NORMAL_TIMELOCK, vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, @@ -97,6 +125,8 @@ const NETWORKS: NetworkConfig[] = [ }, { name: "ZkSync", + acm: ZKSYNCMAINNET_ACM, + normalTimelock: ZKSYNCMAINNET_NORMAL_TIMELOCK, vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, @@ -110,21 +140,30 @@ export const vip608_2 = () => { title: "VIP-608 Upgrade isolated pool VToken implementation across all networks", description: "Upgrade VToken beacon proxies on 7 networks (Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, ZkSync). " + - "For each network: (1) call upgradeTo on the VTokenBeacon to upgrade all VToken proxies, " + - "(2) call syncCash on each Core comptroller VToken to initialize internalCash.", + "For each network: (1) grant syncCash permission to the timelock, (2) call upgradeTo on the VTokenBeacon, " + + "(3) call syncCash on each Core comptroller VToken to initialize internalCash.", forDescription: "I agree that Venus Protocol should proceed with this proposal", againstDescription: "I do not think that Venus Protocol should proceed with this proposal", abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", }; return makeProposal( - NETWORKS.flatMap(({ vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + NETWORKS.flatMap(({ acm, normalTimelock, vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + // Grant syncCash() permission to the Normal Timelock for each VToken + ...coreVTokens.map(vToken => ({ + target: acm, + signature: "giveCallPermission(address,string,address)", + params: [vToken, "syncCash()", normalTimelock], + dstChainId, + })), + // Upgrade the beacon to the new implementation { target: vTokenBeacon, signature: "upgradeTo(address)", params: [newImplementation], dstChainId, }, + // Call syncCash on each VToken to initialize internalCash ...coreVTokens.map(vToken => ({ target: vToken, signature: "syncCash()", From 9b434ba29f1d875da0130d042485a706fe34007f Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Mar 2026 16:09:18 +0530 Subject: [PATCH 07/12] test: update VIP-608 simulation block numbers and address checksums --- simulations/vip-608/basemainnet.ts | 2 +- simulations/vip-608/opmainnet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/simulations/vip-608/basemainnet.ts b/simulations/vip-608/basemainnet.ts index e9461f0cf..98ec6a00a 100644 --- a/simulations/vip-608/basemainnet.ts +++ b/simulations/vip-608/basemainnet.ts @@ -13,7 +13,7 @@ import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 43514761; +const BLOCK_NUMBER = 43518812; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; diff --git a/simulations/vip-608/opmainnet.ts b/simulations/vip-608/opmainnet.ts index 8c8f7018b..7afa87e0b 100644 --- a/simulations/vip-608/opmainnet.ts +++ b/simulations/vip-608/opmainnet.ts @@ -13,7 +13,7 @@ import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 149175577; +const BLOCK_NUMBER = 149115427; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; From e92b7ee33f1dc7870ebaa85b591d58520bc9ad46 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Fri, 20 Mar 2026 16:30:45 +0530 Subject: [PATCH 08/12] feat: update VIP-608 NEW_VTOKEN_IMPLEMENTATION addresses from isolated-pools PR #551 --- vips/vip-608/addresses/arbitrumone.ts | 2 +- vips/vip-608/addresses/basemainnet.ts | 2 +- vips/vip-608/addresses/ethereum.ts | 2 +- vips/vip-608/addresses/opbnbmainnet.ts | 2 +- vips/vip-608/addresses/opmainnet.ts | 2 +- vips/vip-608/addresses/unichainmainnet.ts | 2 +- vips/vip-608/addresses/zksyncmainnet.ts | 2 +- vips/vip-608/bscmainnet.ts | 2 -- 8 files changed, 7 insertions(+), 9 deletions(-) diff --git a/vips/vip-608/addresses/arbitrumone.ts b/vips/vip-608/addresses/arbitrumone.ts index 12a2853cd..183705ec6 100644 --- a/vips/vip-608/addresses/arbitrumone.ts +++ b/vips/vip-608/addresses/arbitrumone.ts @@ -4,7 +4,7 @@ export const ARBITRUMONE_ACM = "0xD9dD18EB0cf10CbA837677f28A8F9Bda4bc2b157"; export const ARBITRUMONE_NORMAL_TIMELOCK = "0x4b94589Cc23F618687790036726f744D602c4017"; export const ARBITRUMONE_CORE_COMPTROLLER = "0x317c1A5739F39046E20b08ac9BeEa3f10fD43326"; export const ARBITRUMONE_VTOKEN_BEACON = "0xE9381D8CA7006c12Ae9eB97890575E705996fa66"; -export const ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION = "0x1986Fb53535953711265d5fD329cd7A690411669"; export const ARBITRUMONE_DST_CHAIN_ID = LzChainId.arbitrumone; export const ARBITRUMONE_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/basemainnet.ts b/vips/vip-608/addresses/basemainnet.ts index 6c6beaa2b..055b06a9b 100644 --- a/vips/vip-608/addresses/basemainnet.ts +++ b/vips/vip-608/addresses/basemainnet.ts @@ -4,7 +4,7 @@ export const BASEMAINNET_ACM = "0x9E6CeEfDC6183e4D0DF8092A9B90cDF659687daB"; export const BASEMAINNET_NORMAL_TIMELOCK = "0x21c12f2946a1a66cBFf7eb997022a37167eCf517"; export const BASEMAINNET_CORE_COMPTROLLER = "0x0C7973F9598AA62f9e03B94E92C967fD5437426C"; export const BASEMAINNET_VTOKEN_BEACON = "0x87a6476510368c4Bfb70d04A3B0e5a881eC7f0d1"; -export const BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x867050A5eE96Af41D62160F6e1F4724828eD3D6e"; +export const BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x107bA74c87e75b6cc291510D3A85D0c8EAa73e82"; export const BASEMAINNET_DST_CHAIN_ID = LzChainId.basemainnet; export const BASEMAINNET_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/ethereum.ts b/vips/vip-608/addresses/ethereum.ts index 082bea299..b718e9722 100644 --- a/vips/vip-608/addresses/ethereum.ts +++ b/vips/vip-608/addresses/ethereum.ts @@ -4,7 +4,7 @@ export const ETHEREUM_ACM = "0x230058da2D23eb8836EC5DB7037ef7250c56E25E"; export const ETHEREUM_NORMAL_TIMELOCK = "0xd969E79406c35E80750aAae061D402Aab9325714"; export const ETHEREUM_CORE_COMPTROLLER = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B"; export const ETHEREUM_VTOKEN_BEACON = "0xfc08aADC7a1A93857f6296C3fb78aBA1d286533a"; -export const ETHEREUM_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ETHEREUM_NEW_VTOKEN_IMPLEMENTATION = "0x33bE30B31f07c8a2bfb705FBcE55E983c47ba864"; export const ETHEREUM_DST_CHAIN_ID = LzChainId.ethereum; export const ETHEREUM_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/opbnbmainnet.ts b/vips/vip-608/addresses/opbnbmainnet.ts index c08b0a786..7eaf8071f 100644 --- a/vips/vip-608/addresses/opbnbmainnet.ts +++ b/vips/vip-608/addresses/opbnbmainnet.ts @@ -4,7 +4,7 @@ export const OPBNBMAINNET_ACM = "0xA60Deae5344F1152426cA440fb6552eA0e3005D6"; export const OPBNBMAINNET_NORMAL_TIMELOCK = "0x10f504e939b912569Dca611851fDAC9E3Ef86819"; export const OPBNBMAINNET_CORE_COMPTROLLER = "0xD6e3E2A1d8d95caE355D15b3b9f8E5c2511874dd"; export const OPBNBMAINNET_VTOKEN_BEACON = "0xfeD1d3a13597c5aBc893Af41ED5cb17e64c847c7"; -export const OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x7AA7EB7553DE5f221C06595f3b4021363279e9Fe"; export const OPBNBMAINNET_DST_CHAIN_ID = LzChainId.opbnbmainnet; export const OPBNBMAINNET_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/opmainnet.ts b/vips/vip-608/addresses/opmainnet.ts index b44717e68..b29baf02c 100644 --- a/vips/vip-608/addresses/opmainnet.ts +++ b/vips/vip-608/addresses/opmainnet.ts @@ -4,7 +4,7 @@ export const OPMAINNET_ACM = "0xD71b1F33f6B0259683f11174EE4Ddc2bb9cE4eD6"; export const OPMAINNET_NORMAL_TIMELOCK = "0x0C6f1E6B4fDa846f63A0d5a8a73EB811E0e0C04b"; export const OPMAINNET_CORE_COMPTROLLER = "0x5593FF68bE84C966821eEf5F0a988C285D5B7CeC"; export const OPMAINNET_VTOKEN_BEACON = "0xd550Bdfa9402e215De0BabCb99F7294BE0268367"; -export const OPMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0xe6a1B42B0Ba8129fcB51bb105F590f29dcAF1599"; +export const OPMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0xBEB9eE824a0096c0FB606b070c028cB55b6f21e7"; export const OPMAINNET_DST_CHAIN_ID = LzChainId.opmainnet; export const OPMAINNET_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/unichainmainnet.ts b/vips/vip-608/addresses/unichainmainnet.ts index 8f5ed5f75..d57439b1f 100644 --- a/vips/vip-608/addresses/unichainmainnet.ts +++ b/vips/vip-608/addresses/unichainmainnet.ts @@ -4,7 +4,7 @@ export const UNICHAINMAINNET_ACM = "0x1f12014c497a9d905155eB9BfDD9FaC6885e61d0"; export const UNICHAINMAINNET_NORMAL_TIMELOCK = "0x918532A78d22419Da4091930d472bDdf532BE89a"; export const UNICHAINMAINNET_CORE_COMPTROLLER = "0xe22af1e6b78318e1Fe1053Edbd7209b8Fc62c4Fe"; export const UNICHAINMAINNET_VTOKEN_BEACON = "0x42c1Efb9Dd9424c5ac8e6EcEa4eb03940c4a15Fc"; -export const UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x256E61f4056EC9eaB8c56E3aFa217a12c33b8893"; export const UNICHAINMAINNET_DST_CHAIN_ID = LzChainId.unichainmainnet; export const UNICHAINMAINNET_CORE_VTOKENS = [ diff --git a/vips/vip-608/addresses/zksyncmainnet.ts b/vips/vip-608/addresses/zksyncmainnet.ts index d5a975eaf..9b423d83a 100644 --- a/vips/vip-608/addresses/zksyncmainnet.ts +++ b/vips/vip-608/addresses/zksyncmainnet.ts @@ -4,7 +4,7 @@ export const ZKSYNCMAINNET_ACM = "0x526159A92A82afE5327d37Ef446b68FD9a5cA914"; export const ZKSYNCMAINNET_NORMAL_TIMELOCK = "0x093565Bc20AA326F4209eBaF3a26089272627613"; export const ZKSYNCMAINNET_CORE_COMPTROLLER = "0xddE4D098D9995B659724ae6d5E3FB9681Ac941B1"; export const ZKSYNCMAINNET_VTOKEN_BEACON = "0x53523537aa330640B80400EB8B309fF5896E7eb5"; -export const ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION = "TODO"; // Not yet deployed +export const ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION = "0x3e9cf1eBe018610585D0B519A9956C950E079591"; export const ZKSYNCMAINNET_DST_CHAIN_ID = LzChainId.zksyncmainnet; export const ZKSYNCMAINNET_CORE_VTOKENS = [ diff --git a/vips/vip-608/bscmainnet.ts b/vips/vip-608/bscmainnet.ts index ebbc3b331..9590c82e2 100644 --- a/vips/vip-608/bscmainnet.ts +++ b/vips/vip-608/bscmainnet.ts @@ -1,7 +1,6 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; -// TODO: Replace with the deployed VBep20Delegate implementation address const NEW_VBEP20_DELEGATE_IMPL = "0xb25b57599BA969c4829699F7E4Fc4076D14745E1"; // VBep20Delegator proxy addresses (43 markets, excludes vBNB) @@ -50,7 +49,6 @@ const vPT_clisBNB_25JUN2026 = "0x6d3BD68E90B42615cb5abF4B8DE92b154ADc435e"; const vXAUM = "0x92e6Ea74a1A3047DabF4186405a21c7D63a0612A"; // Markets and their excess token amounts to sweep before syncing -// TODO: Verify excess amounts on-chain before proposal. Use 0 for markets with no excess. interface MarketConfig { vToken: string; name: string; From 554ab6dbf0c277e8411254902607aeee94b6f71a Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Fri, 20 Mar 2026 17:11:37 +0530 Subject: [PATCH 09/12] feat(vip-608): update implementation addresses and split cross-chain proposal - Update NEW_VTOKEN_IMPLEMENTATION addresses for all remote chains - Split bscmainnet-2 cross-chain proposal to avoid LayerZero payload size limit - Add bscmainnet-3 for remaining cross-chain commands - Update simulation block numbers and test structure --- simulations/vip-608/arbitrumone.ts | 7 +- simulations/vip-608/basemainnet.ts | 7 +- simulations/vip-608/bscmainnet-2.ts | 4 +- simulations/vip-608/bscmainnet-3.ts | 9 ++ simulations/vip-608/bscmainnet.ts | 2 +- simulations/vip-608/ethereum.ts | 7 +- simulations/vip-608/opbnbmainnet.ts | 7 +- simulations/vip-608/opmainnet.ts | 7 +- simulations/vip-608/unichainmainnet.ts | 7 +- simulations/vip-608/zksyncmainnet.ts | 7 +- vips/vip-608/bscmainnet-2.ts | 57 ++-------- vips/vip-608/bscmainnet-3.ts | 142 +++++++++++++++++++++++++ 12 files changed, 195 insertions(+), 68 deletions(-) create mode 100644 simulations/vip-608/bscmainnet-3.ts create mode 100644 vips/vip-608/bscmainnet-3.ts diff --git a/simulations/vip-608/arbitrumone.ts b/simulations/vip-608/arbitrumone.ts index 400b1fd95..a4fbf1dcb 100644 --- a/simulations/vip-608/arbitrumone.ts +++ b/simulations/vip-608/arbitrumone.ts @@ -10,10 +10,11 @@ import { ARBITRUMONE_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/arbitrumone"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 443434569; +const BLOCK_NUMBER = 443758428; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/basemainnet.ts b/simulations/vip-608/basemainnet.ts index 98ec6a00a..e7e834961 100644 --- a/simulations/vip-608/basemainnet.ts +++ b/simulations/vip-608/basemainnet.ts @@ -10,10 +10,11 @@ import { BASEMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/basemainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 43518812; +const BLOCK_NUMBER = 43608484; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/bscmainnet-2.ts b/simulations/vip-608/bscmainnet-2.ts index aadb97f90..aef56b15c 100644 --- a/simulations/vip-608/bscmainnet-2.ts +++ b/simulations/vip-608/bscmainnet-2.ts @@ -2,8 +2,8 @@ import { forking, testVip } from "src/vip-framework"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -const BLOCK_NUMBER = 87425358; +const BLOCK_NUMBER = 87684581; forking(BLOCK_NUMBER, async () => { - testVip("VIP-608 Isolated Pool VToken Upgrade (cross-chain dispatch)", await vip608_2()); + testVip("VIP-608 Grant syncCash permissions (all networks)", await vip608_2()); }); diff --git a/simulations/vip-608/bscmainnet-3.ts b/simulations/vip-608/bscmainnet-3.ts new file mode 100644 index 000000000..04b4f790b --- /dev/null +++ b/simulations/vip-608/bscmainnet-3.ts @@ -0,0 +1,9 @@ +import { forking, testVip } from "src/vip-framework"; + +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; + +const BLOCK_NUMBER = 87684581; + +forking(BLOCK_NUMBER, async () => { + testVip("VIP-608 Upgrade VToken beacon and syncCash (all networks)", await vip608_3()); +}); diff --git a/simulations/vip-608/bscmainnet.ts b/simulations/vip-608/bscmainnet.ts index 19d595439..68c6222ff 100644 --- a/simulations/vip-608/bscmainnet.ts +++ b/simulations/vip-608/bscmainnet.ts @@ -6,7 +6,7 @@ import { forking, testVip } from "src/vip-framework"; import vip608 from "../../vips/vip-608/bscmainnet"; import VBEP20_DELEGATE_ABI from "./abi/VBep20Delegate.json"; -const BLOCK_NUMBER = 87126359; +const BLOCK_NUMBER = 87684581; const NEW_VBEP20_DELEGATE_IMPL = "0xb25b57599BA969c4829699F7E4Fc4076D14745E1"; diff --git a/simulations/vip-608/ethereum.ts b/simulations/vip-608/ethereum.ts index f25d27060..edf5ef973 100644 --- a/simulations/vip-608/ethereum.ts +++ b/simulations/vip-608/ethereum.ts @@ -10,10 +10,11 @@ import { ETHEREUM_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/ethereum"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 24678795; +const BLOCK_NUMBER = 24698424; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/opbnbmainnet.ts b/simulations/vip-608/opbnbmainnet.ts index 7c48f7bba..6d0c6a12f 100644 --- a/simulations/vip-608/opbnbmainnet.ts +++ b/simulations/vip-608/opbnbmainnet.ts @@ -10,10 +10,11 @@ import { OPBNBMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/opbnbmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 122734048; +const BLOCK_NUMBER = 123557813; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/opmainnet.ts b/simulations/vip-608/opmainnet.ts index 7afa87e0b..1aca2eb85 100644 --- a/simulations/vip-608/opmainnet.ts +++ b/simulations/vip-608/opmainnet.ts @@ -10,10 +10,11 @@ import { OPMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/opmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 149115427; +const BLOCK_NUMBER = 149203740; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/unichainmainnet.ts b/simulations/vip-608/unichainmainnet.ts index 0dabb48bd..8c4e2e2c7 100644 --- a/simulations/vip-608/unichainmainnet.ts +++ b/simulations/vip-608/unichainmainnet.ts @@ -10,10 +10,11 @@ import { UNICHAINMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/unichainmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 43024435; +const BLOCK_NUMBER = 43258056; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/zksyncmainnet.ts b/simulations/vip-608/zksyncmainnet.ts index 6ff31c233..01c663eb3 100644 --- a/simulations/vip-608/zksyncmainnet.ts +++ b/simulations/vip-608/zksyncmainnet.ts @@ -10,10 +10,11 @@ import { ZKSYNCMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/zksyncmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; +import vip608_3 from "../../vips/vip-608/bscmainnet-3"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; -const BLOCK_NUMBER = 69140884; +const BLOCK_NUMBER = 69170974; const ERC20_ABI = ["function balanceOf(address) view returns (uint256)"]; @@ -28,7 +29,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Isolated Pool VToken Upgrade", await vip608_2(), { + testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + + testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/vips/vip-608/bscmainnet-2.ts b/vips/vip-608/bscmainnet-2.ts index 60ba1203b..4db1e8f10 100644 --- a/vips/vip-608/bscmainnet-2.ts +++ b/vips/vip-608/bscmainnet-2.ts @@ -5,65 +5,49 @@ import { ARBITRUMONE_ACM, ARBITRUMONE_CORE_VTOKENS, ARBITRUMONE_DST_CHAIN_ID, - ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, ARBITRUMONE_NORMAL_TIMELOCK, - ARBITRUMONE_VTOKEN_BEACON, } from "./addresses/arbitrumone"; import { BASEMAINNET_ACM, BASEMAINNET_CORE_VTOKENS, BASEMAINNET_DST_CHAIN_ID, - BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, BASEMAINNET_NORMAL_TIMELOCK, - BASEMAINNET_VTOKEN_BEACON, } from "./addresses/basemainnet"; import { ETHEREUM_ACM, ETHEREUM_CORE_VTOKENS, ETHEREUM_DST_CHAIN_ID, - ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, ETHEREUM_NORMAL_TIMELOCK, - ETHEREUM_VTOKEN_BEACON, } from "./addresses/ethereum"; import { OPBNBMAINNET_ACM, OPBNBMAINNET_CORE_VTOKENS, OPBNBMAINNET_DST_CHAIN_ID, - OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPBNBMAINNET_NORMAL_TIMELOCK, - OPBNBMAINNET_VTOKEN_BEACON, } from "./addresses/opbnbmainnet"; import { OPMAINNET_ACM, OPMAINNET_CORE_VTOKENS, OPMAINNET_DST_CHAIN_ID, - OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPMAINNET_NORMAL_TIMELOCK, - OPMAINNET_VTOKEN_BEACON, } from "./addresses/opmainnet"; import { UNICHAINMAINNET_ACM, UNICHAINMAINNET_CORE_VTOKENS, UNICHAINMAINNET_DST_CHAIN_ID, - UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, UNICHAINMAINNET_NORMAL_TIMELOCK, - UNICHAINMAINNET_VTOKEN_BEACON, } from "./addresses/unichainmainnet"; import { ZKSYNCMAINNET_ACM, ZKSYNCMAINNET_CORE_VTOKENS, ZKSYNCMAINNET_DST_CHAIN_ID, - ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, ZKSYNCMAINNET_NORMAL_TIMELOCK, - ZKSYNCMAINNET_VTOKEN_BEACON, } from "./addresses/zksyncmainnet"; interface NetworkConfig { name: string; acm: string; normalTimelock: string; - vTokenBeacon: string; - newImplementation: string; dstChainId: number; coreVTokens: string[]; } @@ -73,8 +57,6 @@ const NETWORKS: NetworkConfig[] = [ name: "Ethereum", acm: ETHEREUM_ACM, normalTimelock: ETHEREUM_NORMAL_TIMELOCK, - vTokenBeacon: ETHEREUM_VTOKEN_BEACON, - newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ETHEREUM_DST_CHAIN_ID, coreVTokens: ETHEREUM_CORE_VTOKENS, }, @@ -82,8 +64,6 @@ const NETWORKS: NetworkConfig[] = [ name: "Arbitrum", acm: ARBITRUMONE_ACM, normalTimelock: ARBITRUMONE_NORMAL_TIMELOCK, - vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, - newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ARBITRUMONE_DST_CHAIN_ID, coreVTokens: ARBITRUMONE_CORE_VTOKENS, }, @@ -91,8 +71,6 @@ const NETWORKS: NetworkConfig[] = [ name: "Optimism", acm: OPMAINNET_ACM, normalTimelock: OPMAINNET_NORMAL_TIMELOCK, - vTokenBeacon: OPMAINNET_VTOKEN_BEACON, - newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: OPMAINNET_DST_CHAIN_ID, coreVTokens: OPMAINNET_CORE_VTOKENS, }, @@ -100,8 +78,6 @@ const NETWORKS: NetworkConfig[] = [ name: "Base", acm: BASEMAINNET_ACM, normalTimelock: BASEMAINNET_NORMAL_TIMELOCK, - vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, - newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: BASEMAINNET_DST_CHAIN_ID, coreVTokens: BASEMAINNET_CORE_VTOKENS, }, @@ -109,8 +85,6 @@ const NETWORKS: NetworkConfig[] = [ name: "opBNB", acm: OPBNBMAINNET_ACM, normalTimelock: OPBNBMAINNET_NORMAL_TIMELOCK, - vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, - newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: OPBNBMAINNET_DST_CHAIN_ID, coreVTokens: OPBNBMAINNET_CORE_VTOKENS, }, @@ -118,8 +92,6 @@ const NETWORKS: NetworkConfig[] = [ name: "Unichain", acm: UNICHAINMAINNET_ACM, normalTimelock: UNICHAINMAINNET_NORMAL_TIMELOCK, - vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, - newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, coreVTokens: UNICHAINMAINNET_CORE_VTOKENS, }, @@ -127,8 +99,6 @@ const NETWORKS: NetworkConfig[] = [ name: "ZkSync", acm: ZKSYNCMAINNET_ACM, normalTimelock: ZKSYNCMAINNET_NORMAL_TIMELOCK, - vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, - newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, coreVTokens: ZKSYNCMAINNET_CORE_VTOKENS, }, @@ -137,40 +107,25 @@ const NETWORKS: NetworkConfig[] = [ export const vip608_2 = () => { const meta = { version: "v2", - title: "VIP-608 Upgrade isolated pool VToken implementation across all networks", + title: "VIP-608 Grant syncCash permissions for isolated pool VToken upgrade (all networks)", description: - "Upgrade VToken beacon proxies on 7 networks (Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, ZkSync). " + - "For each network: (1) grant syncCash permission to the timelock, (2) call upgradeTo on the VTokenBeacon, " + - "(3) call syncCash on each Core comptroller VToken to initialize internalCash.", + "Grant syncCash() call permission to the Normal Timelock for each VToken on all remote networks: " + + "Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, and ZkSync.", forDescription: "I agree that Venus Protocol should proceed with this proposal", againstDescription: "I do not think that Venus Protocol should proceed with this proposal", abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", }; return makeProposal( - NETWORKS.flatMap(({ acm, normalTimelock, vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + NETWORKS.flatMap(({ acm, normalTimelock, dstChainId, coreVTokens }) => // Grant syncCash() permission to the Normal Timelock for each VToken - ...coreVTokens.map(vToken => ({ + coreVTokens.map(vToken => ({ target: acm, signature: "giveCallPermission(address,string,address)", params: [vToken, "syncCash()", normalTimelock], dstChainId, })), - // Upgrade the beacon to the new implementation - { - target: vTokenBeacon, - signature: "upgradeTo(address)", - params: [newImplementation], - dstChainId, - }, - // Call syncCash on each VToken to initialize internalCash - ...coreVTokens.map(vToken => ({ - target: vToken, - signature: "syncCash()", - params: [], - dstChainId, - })), - ]), + ), meta, ProposalType.REGULAR, ); diff --git a/vips/vip-608/bscmainnet-3.ts b/vips/vip-608/bscmainnet-3.ts new file mode 100644 index 000000000..3b12aac57 --- /dev/null +++ b/vips/vip-608/bscmainnet-3.ts @@ -0,0 +1,142 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + ARBITRUMONE_CORE_VTOKENS, + ARBITRUMONE_DST_CHAIN_ID, + ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + ARBITRUMONE_VTOKEN_BEACON, +} from "./addresses/arbitrumone"; +import { + BASEMAINNET_CORE_VTOKENS, + BASEMAINNET_DST_CHAIN_ID, + BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + BASEMAINNET_VTOKEN_BEACON, +} from "./addresses/basemainnet"; +import { + ETHEREUM_CORE_VTOKENS, + ETHEREUM_DST_CHAIN_ID, + ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + ETHEREUM_VTOKEN_BEACON, +} from "./addresses/ethereum"; +import { + OPBNBMAINNET_CORE_VTOKENS, + OPBNBMAINNET_DST_CHAIN_ID, + OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPBNBMAINNET_VTOKEN_BEACON, +} from "./addresses/opbnbmainnet"; +import { + OPMAINNET_CORE_VTOKENS, + OPMAINNET_DST_CHAIN_ID, + OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPMAINNET_VTOKEN_BEACON, +} from "./addresses/opmainnet"; +import { + UNICHAINMAINNET_CORE_VTOKENS, + UNICHAINMAINNET_DST_CHAIN_ID, + UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + UNICHAINMAINNET_VTOKEN_BEACON, +} from "./addresses/unichainmainnet"; +import { + ZKSYNCMAINNET_CORE_VTOKENS, + ZKSYNCMAINNET_DST_CHAIN_ID, + ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + ZKSYNCMAINNET_VTOKEN_BEACON, +} from "./addresses/zksyncmainnet"; + +interface NetworkConfig { + name: string; + vTokenBeacon: string; + newImplementation: string; + dstChainId: number; + coreVTokens: string[]; +} + +const NETWORKS: NetworkConfig[] = [ + { + name: "Ethereum", + vTokenBeacon: ETHEREUM_VTOKEN_BEACON, + newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ETHEREUM_DST_CHAIN_ID, + coreVTokens: ETHEREUM_CORE_VTOKENS, + }, + { + name: "Arbitrum", + vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, + newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ARBITRUMONE_DST_CHAIN_ID, + coreVTokens: ARBITRUMONE_CORE_VTOKENS, + }, + { + name: "Optimism", + vTokenBeacon: OPMAINNET_VTOKEN_BEACON, + newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPMAINNET_DST_CHAIN_ID, + coreVTokens: OPMAINNET_CORE_VTOKENS, + }, + { + name: "Base", + vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, + newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: BASEMAINNET_DST_CHAIN_ID, + coreVTokens: BASEMAINNET_CORE_VTOKENS, + }, + { + name: "opBNB", + vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, + newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPBNBMAINNET_DST_CHAIN_ID, + coreVTokens: OPBNBMAINNET_CORE_VTOKENS, + }, + { + name: "Unichain", + vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, + newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, + coreVTokens: UNICHAINMAINNET_CORE_VTOKENS, + }, + { + name: "ZkSync", + vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, + newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, + coreVTokens: ZKSYNCMAINNET_CORE_VTOKENS, + }, +]; + +export const vip608_3 = () => { + const meta = { + version: "v2", + title: "VIP-608 Upgrade VToken beacon and syncCash for isolated pools (all networks)", + description: + "Upgrade VToken beacon proxies and call syncCash on each VToken on all remote networks: " + + "Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, and ZkSync. " + + "For each network: (1) call upgradeTo on the VTokenBeacon, (2) call syncCash on each VToken to initialize internalCash.", + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + NETWORKS.flatMap(({ vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + // Upgrade the beacon to the new implementation + { + target: vTokenBeacon, + signature: "upgradeTo(address)", + params: [newImplementation], + dstChainId, + }, + // Call syncCash on each VToken to initialize internalCash + ...coreVTokens.map(vToken => ({ + target: vToken, + signature: "syncCash()", + params: [], + dstChainId, + })), + ]), + meta, + ProposalType.REGULAR, + ); +}; + +export default vip608_3; From 685b52eb3b97c8bd93cdbcebece5f2dcd120b8f3 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Fri, 20 Mar 2026 17:59:45 +0530 Subject: [PATCH 10/12] fix(vip-608): correct proxy comments and update simulations --- simulations/vip-608/arbitrumone.ts | 8 ++++++++ simulations/vip-608/basemainnet.ts | 8 ++++++++ simulations/vip-608/ethereum.ts | 8 ++++++++ simulations/vip-608/opbnbmainnet.ts | 8 ++++++++ simulations/vip-608/opmainnet.ts | 8 ++++++++ simulations/vip-608/unichainmainnet.ts | 11 +++++++++++ simulations/vip-608/zksyncmainnet.ts | 8 ++++++++ vips/vip-608/addresses/ethereum.ts | 4 ++-- 8 files changed, 61 insertions(+), 2 deletions(-) diff --git a/simulations/vip-608/arbitrumone.ts b/simulations/vip-608/arbitrumone.ts index a4fbf1dcb..713c3e08e 100644 --- a/simulations/vip-608/arbitrumone.ts +++ b/simulations/vip-608/arbitrumone.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + ARBITRUMONE_CORE_COMPTROLLER, ARBITRUMONE_CORE_VTOKENS, ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, ARBITRUMONE_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/arbitrumone"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(ARBITRUMONE_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(ARBITRUMONE_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(ARBITRUMONE_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/basemainnet.ts b/simulations/vip-608/basemainnet.ts index e7e834961..0ce3bb9a6 100644 --- a/simulations/vip-608/basemainnet.ts +++ b/simulations/vip-608/basemainnet.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + BASEMAINNET_CORE_COMPTROLLER, BASEMAINNET_CORE_VTOKENS, BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, BASEMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/basemainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(BASEMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(BASEMAINNET_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(BASEMAINNET_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/ethereum.ts b/simulations/vip-608/ethereum.ts index edf5ef973..8a50fb20f 100644 --- a/simulations/vip-608/ethereum.ts +++ b/simulations/vip-608/ethereum.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + ETHEREUM_CORE_COMPTROLLER, ETHEREUM_CORE_VTOKENS, ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, ETHEREUM_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/ethereum"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(ETHEREUM_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(ETHEREUM_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(ETHEREUM_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(ETHEREUM_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/opbnbmainnet.ts b/simulations/vip-608/opbnbmainnet.ts index 6d0c6a12f..20483e3b0 100644 --- a/simulations/vip-608/opbnbmainnet.ts +++ b/simulations/vip-608/opbnbmainnet.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + OPBNBMAINNET_CORE_COMPTROLLER, OPBNBMAINNET_CORE_VTOKENS, OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPBNBMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/opbnbmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(OPBNBMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(OPBNBMAINNET_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(OPBNBMAINNET_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/opmainnet.ts b/simulations/vip-608/opmainnet.ts index 1aca2eb85..7f59d7f47 100644 --- a/simulations/vip-608/opmainnet.ts +++ b/simulations/vip-608/opmainnet.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + OPMAINNET_CORE_COMPTROLLER, OPMAINNET_CORE_VTOKENS, OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/opmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(OPMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(OPMAINNET_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(OPMAINNET_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(OPMAINNET_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/unichainmainnet.ts b/simulations/vip-608/unichainmainnet.ts index 8c4e2e2c7..4767be479 100644 --- a/simulations/vip-608/unichainmainnet.ts +++ b/simulations/vip-608/unichainmainnet.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + UNICHAINMAINNET_CORE_COMPTROLLER, UNICHAINMAINNET_CORE_VTOKENS, UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, UNICHAINMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/unichainmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,15 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(UNICHAINMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(UNICHAINMAINNET_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(UNICHAINMAINNET_CORE_VTOKENS.length).to.equal( + allMarkets.length, + "CORE_VTOKENS does not cover all markets", + ); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION); diff --git a/simulations/vip-608/zksyncmainnet.ts b/simulations/vip-608/zksyncmainnet.ts index 01c663eb3..02b7dae94 100644 --- a/simulations/vip-608/zksyncmainnet.ts +++ b/simulations/vip-608/zksyncmainnet.ts @@ -5,12 +5,14 @@ import { expectEvents } from "src/utils"; import { forking, testForkedNetworkVipCommands } from "src/vip-framework"; import { + ZKSYNCMAINNET_CORE_COMPTROLLER, ZKSYNCMAINNET_CORE_VTOKENS, ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, ZKSYNCMAINNET_VTOKEN_BEACON, } from "../../vips/vip-608/addresses/zksyncmainnet"; import vip608_2 from "../../vips/vip-608/bscmainnet-2"; import vip608_3 from "../../vips/vip-608/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; @@ -23,6 +25,12 @@ forking(BLOCK_NUMBER, async () => { const vTokenBeacon = new ethers.Contract(ZKSYNCMAINNET_VTOKEN_BEACON, VTOKEN_BEACON_ABI, provider); describe("Pre-VIP behaviour", () => { + it("CORE_VTOKENS should cover all on-chain markets", async () => { + const comptroller = new ethers.Contract(ZKSYNCMAINNET_CORE_COMPTROLLER, COMPTROLLER_ABI, provider); + const allMarkets: string[] = await comptroller.getAllMarkets(); + expect(ZKSYNCMAINNET_CORE_VTOKENS.length).to.equal(allMarkets.length, "CORE_VTOKENS does not cover all markets"); + }); + it("VToken beacon should not point to new implementation", async () => { const currentImplementation = await vTokenBeacon.implementation(); expect(currentImplementation).to.not.equal(ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION); diff --git a/vips/vip-608/addresses/ethereum.ts b/vips/vip-608/addresses/ethereum.ts index b718e9722..214f229ec 100644 --- a/vips/vip-608/addresses/ethereum.ts +++ b/vips/vip-608/addresses/ethereum.ts @@ -17,8 +17,8 @@ export const ETHEREUM_CORE_VTOKENS = [ "0x13eb80fdbe5c5f4a7039728e258a6f05fb3b912b", // vTUSD_Core "0x4fafbdc4f2a9876bd1764827b26fb8dc4fd1db95", // vFRAX_Core, Paused "0x17142a05fe678e9584fa1d88efac1bf181bf7abe", // vsFRAX_Core - "0x256addbe0a387c98f487e44b85c29eb983413c5e", // vEIGEN, have differenet proxy - "0x325ceb02fe1c2ff816a83a5770ea0e88e2faecf2", // veBTC, have different proxy + "0x256addbe0a387c98f487e44b85c29eb983413c5e", // vEIGEN_Core + "0x325ceb02fe1c2ff816a83a5770ea0e88e2faecf2", // veBTC_Core "0x25c20e6e110a1ce3febacc8b7e48368c7b2f0c91", // vLBTC_Core "0x0c6b19287999f1e31a5c0a44393b24b62d2c0468", // vUSDS_Core "0xe36ae842dbbd7ae372eba02c8239cd431cc063d6", // vsUSDS_Core From df6f39cef270e5b684bae900d750cd197ae924c7 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 20 Mar 2026 12:50:50 +0000 Subject: [PATCH 11/12] chore: prepare VIP-600/601/602 for proposal (THE attack fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename vip-608 → vip-600 (all mainnet files and simulations) - VIP-600 (bscmainnet.ts): BNB Chain core pool VBep20Delegate upgrade, 43 markets - VIP-601 (bscmainnet-2.ts): grant syncCash() ACM permissions on 7 non-BNB chains - VIP-602 (bscmainnet-3.ts): upgrade VToken beacon + call syncCash() on all non-BNB chains - Update meta titles with VIP numbers and (part1/2/3) suffixes - Update meta descriptions from Notion docs Co-Authored-By: Claude Sonnet 4.6 --- .../abi/ILComptroller.json | 0 .../abi/ILPoolRegistry.json | 0 .../{vip-608 => vip-600}/abi/ILVToken.json | 0 .../abi/VBep20Delegate.json | 0 .../abi/vtokenBeacon.json | 0 .../{vip-608 => vip-600}/arbitrumone.ts | 13 +- .../{vip-608 => vip-600}/basemainnet.ts | 13 +- simulations/vip-600/bscmainnet-2.ts | 9 + simulations/vip-600/bscmainnet-3.ts | 9 + .../{vip-608 => vip-600}/bscmainnet.ts | 4 +- simulations/{vip-608 => vip-600}/ethereum.ts | 13 +- .../{vip-608 => vip-600}/opbnbmainnet.ts | 13 +- simulations/{vip-608 => vip-600}/opmainnet.ts | 13 +- .../{vip-608 => vip-600}/unichainmainnet.ts | 13 +- .../{vip-608 => vip-600}/zksyncmainnet.ts | 13 +- simulations/vip-608/bscmainnet-2.ts | 9 - simulations/vip-608/bscmainnet-3.ts | 9 - .../addresses/arbitrumone.ts | 0 .../addresses/basemainnet.ts | 0 .../addresses/ethereum.ts | 0 .../addresses/opbnbmainnet.ts | 0 .../addresses/opmainnet.ts | 0 .../addresses/unichainmainnet.ts | 0 .../addresses/zksyncmainnet.ts | 0 vips/{vip-608 => vip-600}/bscmainnet-2.ts | 41 +++- vips/vip-600/bscmainnet-3.ts | 195 ++++++++++++++++++ vips/{vip-608 => vip-600}/bscmainnet.ts | 45 +++- vips/vip-608/bscmainnet-3.ts | 142 ------------- 28 files changed, 330 insertions(+), 224 deletions(-) rename simulations/{vip-608 => vip-600}/abi/ILComptroller.json (100%) rename simulations/{vip-608 => vip-600}/abi/ILPoolRegistry.json (100%) rename simulations/{vip-608 => vip-600}/abi/ILVToken.json (100%) rename simulations/{vip-608 => vip-600}/abi/VBep20Delegate.json (100%) rename simulations/{vip-608 => vip-600}/abi/vtokenBeacon.json (100%) rename simulations/{vip-608 => vip-600}/arbitrumone.ts (85%) rename simulations/{vip-608 => vip-600}/basemainnet.ts (85%) create mode 100644 simulations/vip-600/bscmainnet-2.ts create mode 100644 simulations/vip-600/bscmainnet-3.ts rename simulations/{vip-608 => vip-600}/bscmainnet.ts (97%) rename simulations/{vip-608 => vip-600}/ethereum.ts (85%) rename simulations/{vip-608 => vip-600}/opbnbmainnet.ts (85%) rename simulations/{vip-608 => vip-600}/opmainnet.ts (85%) rename simulations/{vip-608 => vip-600}/unichainmainnet.ts (85%) rename simulations/{vip-608 => vip-600}/zksyncmainnet.ts (85%) delete mode 100644 simulations/vip-608/bscmainnet-2.ts delete mode 100644 simulations/vip-608/bscmainnet-3.ts rename vips/{vip-608 => vip-600}/addresses/arbitrumone.ts (100%) rename vips/{vip-608 => vip-600}/addresses/basemainnet.ts (100%) rename vips/{vip-608 => vip-600}/addresses/ethereum.ts (100%) rename vips/{vip-608 => vip-600}/addresses/opbnbmainnet.ts (100%) rename vips/{vip-608 => vip-600}/addresses/opmainnet.ts (100%) rename vips/{vip-608 => vip-600}/addresses/unichainmainnet.ts (100%) rename vips/{vip-608 => vip-600}/addresses/zksyncmainnet.ts (100%) rename vips/{vip-608 => vip-600}/bscmainnet-2.ts (53%) create mode 100644 vips/vip-600/bscmainnet-3.ts rename vips/{vip-608 => vip-600}/bscmainnet.ts (64%) delete mode 100644 vips/vip-608/bscmainnet-3.ts diff --git a/simulations/vip-608/abi/ILComptroller.json b/simulations/vip-600/abi/ILComptroller.json similarity index 100% rename from simulations/vip-608/abi/ILComptroller.json rename to simulations/vip-600/abi/ILComptroller.json diff --git a/simulations/vip-608/abi/ILPoolRegistry.json b/simulations/vip-600/abi/ILPoolRegistry.json similarity index 100% rename from simulations/vip-608/abi/ILPoolRegistry.json rename to simulations/vip-600/abi/ILPoolRegistry.json diff --git a/simulations/vip-608/abi/ILVToken.json b/simulations/vip-600/abi/ILVToken.json similarity index 100% rename from simulations/vip-608/abi/ILVToken.json rename to simulations/vip-600/abi/ILVToken.json diff --git a/simulations/vip-608/abi/VBep20Delegate.json b/simulations/vip-600/abi/VBep20Delegate.json similarity index 100% rename from simulations/vip-608/abi/VBep20Delegate.json rename to simulations/vip-600/abi/VBep20Delegate.json diff --git a/simulations/vip-608/abi/vtokenBeacon.json b/simulations/vip-600/abi/vtokenBeacon.json similarity index 100% rename from simulations/vip-608/abi/vtokenBeacon.json rename to simulations/vip-600/abi/vtokenBeacon.json diff --git a/simulations/vip-608/arbitrumone.ts b/simulations/vip-600/arbitrumone.ts similarity index 85% rename from simulations/vip-608/arbitrumone.ts rename to simulations/vip-600/arbitrumone.ts index 713c3e08e..9081134b1 100644 --- a/simulations/vip-608/arbitrumone.ts +++ b/simulations/vip-600/arbitrumone.ts @@ -9,11 +9,10 @@ import { ARBITRUMONE_CORE_VTOKENS, ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, ARBITRUMONE_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/arbitrumone"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/arbitrumone"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 443758428; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/basemainnet.ts b/simulations/vip-600/basemainnet.ts similarity index 85% rename from simulations/vip-608/basemainnet.ts rename to simulations/vip-600/basemainnet.ts index 0ce3bb9a6..0c0085182 100644 --- a/simulations/vip-608/basemainnet.ts +++ b/simulations/vip-600/basemainnet.ts @@ -9,11 +9,10 @@ import { BASEMAINNET_CORE_VTOKENS, BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, BASEMAINNET_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/basemainnet"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/basemainnet"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 43608484; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-600/bscmainnet-2.ts b/simulations/vip-600/bscmainnet-2.ts new file mode 100644 index 000000000..d456f9fda --- /dev/null +++ b/simulations/vip-600/bscmainnet-2.ts @@ -0,0 +1,9 @@ +import { forking, testVip } from "src/vip-framework"; + +import vip601 from "../../vips/vip-600/bscmainnet-2"; + +const BLOCK_NUMBER = 87684581; + +forking(BLOCK_NUMBER, async () => { + testVip("VIP-601 Grant syncCash permissions (all networks)", await vip601()); +}); diff --git a/simulations/vip-600/bscmainnet-3.ts b/simulations/vip-600/bscmainnet-3.ts new file mode 100644 index 000000000..9a1457d91 --- /dev/null +++ b/simulations/vip-600/bscmainnet-3.ts @@ -0,0 +1,9 @@ +import { forking, testVip } from "src/vip-framework"; + +import vip602 from "../../vips/vip-600/bscmainnet-3"; + +const BLOCK_NUMBER = 87684581; + +forking(BLOCK_NUMBER, async () => { + testVip("VIP-602 Upgrade VToken beacon and syncCash (all networks)", await vip602()); +}); diff --git a/simulations/vip-608/bscmainnet.ts b/simulations/vip-600/bscmainnet.ts similarity index 97% rename from simulations/vip-608/bscmainnet.ts rename to simulations/vip-600/bscmainnet.ts index 68c6222ff..5e04dbe6d 100644 --- a/simulations/vip-608/bscmainnet.ts +++ b/simulations/vip-600/bscmainnet.ts @@ -3,7 +3,7 @@ import { BigNumber, Contract } from "ethers"; import { ethers } from "hardhat"; import { forking, testVip } from "src/vip-framework"; -import vip608 from "../../vips/vip-608/bscmainnet"; +import vip600 from "../../vips/vip-600/bscmainnet"; import VBEP20_DELEGATE_ABI from "./abi/VBep20Delegate.json"; const BLOCK_NUMBER = 87684581; @@ -130,7 +130,7 @@ forking(BLOCK_NUMBER, async () => { } }); - testVip("VIP-608 Upgrade VBep20Delegate for Core Pool", await vip608()); + testVip("VIP-600 Upgrade VBep20Delegate for Core Pool", await vip600()); describe("Post-VIP behavior", async () => { for (let i = 0; i < MARKET_ADDRESSES.length; i++) { diff --git a/simulations/vip-608/ethereum.ts b/simulations/vip-600/ethereum.ts similarity index 85% rename from simulations/vip-608/ethereum.ts rename to simulations/vip-600/ethereum.ts index 8a50fb20f..bc26fb883 100644 --- a/simulations/vip-608/ethereum.ts +++ b/simulations/vip-600/ethereum.ts @@ -9,11 +9,10 @@ import { ETHEREUM_CORE_VTOKENS, ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, ETHEREUM_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/ethereum"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/ethereum"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 24698424; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/opbnbmainnet.ts b/simulations/vip-600/opbnbmainnet.ts similarity index 85% rename from simulations/vip-608/opbnbmainnet.ts rename to simulations/vip-600/opbnbmainnet.ts index 20483e3b0..e520255c0 100644 --- a/simulations/vip-608/opbnbmainnet.ts +++ b/simulations/vip-600/opbnbmainnet.ts @@ -9,11 +9,10 @@ import { OPBNBMAINNET_CORE_VTOKENS, OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPBNBMAINNET_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/opbnbmainnet"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/opbnbmainnet"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 123557813; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/opmainnet.ts b/simulations/vip-600/opmainnet.ts similarity index 85% rename from simulations/vip-608/opmainnet.ts rename to simulations/vip-600/opmainnet.ts index 7f59d7f47..3e09e6e6b 100644 --- a/simulations/vip-608/opmainnet.ts +++ b/simulations/vip-600/opmainnet.ts @@ -9,11 +9,10 @@ import { OPMAINNET_CORE_VTOKENS, OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, OPMAINNET_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/opmainnet"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/opmainnet"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 149203740; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/unichainmainnet.ts b/simulations/vip-600/unichainmainnet.ts similarity index 85% rename from simulations/vip-608/unichainmainnet.ts rename to simulations/vip-600/unichainmainnet.ts index 4767be479..88385132f 100644 --- a/simulations/vip-608/unichainmainnet.ts +++ b/simulations/vip-600/unichainmainnet.ts @@ -9,11 +9,10 @@ import { UNICHAINMAINNET_CORE_VTOKENS, UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, UNICHAINMAINNET_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/unichainmainnet"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/unichainmainnet"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 43258056; @@ -40,9 +39,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/zksyncmainnet.ts b/simulations/vip-600/zksyncmainnet.ts similarity index 85% rename from simulations/vip-608/zksyncmainnet.ts rename to simulations/vip-600/zksyncmainnet.ts index 02b7dae94..2d7f7d1ea 100644 --- a/simulations/vip-608/zksyncmainnet.ts +++ b/simulations/vip-600/zksyncmainnet.ts @@ -9,11 +9,10 @@ import { ZKSYNCMAINNET_CORE_VTOKENS, ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, ZKSYNCMAINNET_VTOKEN_BEACON, -} from "../../vips/vip-608/addresses/zksyncmainnet"; -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json"; -import VTOKEN_ABI from "./abi/ILVToken.json"; +} from "../../vips/vip-600/addresses/zksyncmainnet"; +import vip601 from "../../vips/vip-600/bscmainnet-2"; +import vip602 from "../../vips/vip-600/bscmainnet-3"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 69170974; @@ -37,9 +36,9 @@ forking(BLOCK_NUMBER, async () => { }); }); - testForkedNetworkVipCommands("VIP-608 Grant syncCash permissions", await vip608_2()); + testForkedNetworkVipCommands("VIP-601 Grant syncCash permissions", await vip601()); - testForkedNetworkVipCommands("VIP-608 Upgrade VToken beacon and syncCash", await vip608_3(), { + testForkedNetworkVipCommands("VIP-602 Upgrade VToken beacon and syncCash", await vip602(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [VTOKEN_BEACON_ABI], ["Upgraded"], [1]); }, diff --git a/simulations/vip-608/bscmainnet-2.ts b/simulations/vip-608/bscmainnet-2.ts deleted file mode 100644 index aef56b15c..000000000 --- a/simulations/vip-608/bscmainnet-2.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { forking, testVip } from "src/vip-framework"; - -import vip608_2 from "../../vips/vip-608/bscmainnet-2"; - -const BLOCK_NUMBER = 87684581; - -forking(BLOCK_NUMBER, async () => { - testVip("VIP-608 Grant syncCash permissions (all networks)", await vip608_2()); -}); diff --git a/simulations/vip-608/bscmainnet-3.ts b/simulations/vip-608/bscmainnet-3.ts deleted file mode 100644 index 04b4f790b..000000000 --- a/simulations/vip-608/bscmainnet-3.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { forking, testVip } from "src/vip-framework"; - -import vip608_3 from "../../vips/vip-608/bscmainnet-3"; - -const BLOCK_NUMBER = 87684581; - -forking(BLOCK_NUMBER, async () => { - testVip("VIP-608 Upgrade VToken beacon and syncCash (all networks)", await vip608_3()); -}); diff --git a/vips/vip-608/addresses/arbitrumone.ts b/vips/vip-600/addresses/arbitrumone.ts similarity index 100% rename from vips/vip-608/addresses/arbitrumone.ts rename to vips/vip-600/addresses/arbitrumone.ts diff --git a/vips/vip-608/addresses/basemainnet.ts b/vips/vip-600/addresses/basemainnet.ts similarity index 100% rename from vips/vip-608/addresses/basemainnet.ts rename to vips/vip-600/addresses/basemainnet.ts diff --git a/vips/vip-608/addresses/ethereum.ts b/vips/vip-600/addresses/ethereum.ts similarity index 100% rename from vips/vip-608/addresses/ethereum.ts rename to vips/vip-600/addresses/ethereum.ts diff --git a/vips/vip-608/addresses/opbnbmainnet.ts b/vips/vip-600/addresses/opbnbmainnet.ts similarity index 100% rename from vips/vip-608/addresses/opbnbmainnet.ts rename to vips/vip-600/addresses/opbnbmainnet.ts diff --git a/vips/vip-608/addresses/opmainnet.ts b/vips/vip-600/addresses/opmainnet.ts similarity index 100% rename from vips/vip-608/addresses/opmainnet.ts rename to vips/vip-600/addresses/opmainnet.ts diff --git a/vips/vip-608/addresses/unichainmainnet.ts b/vips/vip-600/addresses/unichainmainnet.ts similarity index 100% rename from vips/vip-608/addresses/unichainmainnet.ts rename to vips/vip-600/addresses/unichainmainnet.ts diff --git a/vips/vip-608/addresses/zksyncmainnet.ts b/vips/vip-600/addresses/zksyncmainnet.ts similarity index 100% rename from vips/vip-608/addresses/zksyncmainnet.ts rename to vips/vip-600/addresses/zksyncmainnet.ts diff --git a/vips/vip-608/bscmainnet-2.ts b/vips/vip-600/bscmainnet-2.ts similarity index 53% rename from vips/vip-608/bscmainnet-2.ts rename to vips/vip-600/bscmainnet-2.ts index 4db1e8f10..a50dd0448 100644 --- a/vips/vip-608/bscmainnet-2.ts +++ b/vips/vip-600/bscmainnet-2.ts @@ -104,13 +104,42 @@ const NETWORKS: NetworkConfig[] = [ }, ]; -export const vip608_2 = () => { +export const vip601 = () => { const meta = { version: "v2", - title: "VIP-608 Grant syncCash permissions for isolated pool VToken upgrade (all networks)", - description: - "Grant syncCash() call permission to the Normal Timelock for each VToken on all remote networks: " + - "Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, and ZkSync.", + title: "VIP-601 [Non-BNB Chain] VToken Inflation Attack Patch and Pause Borrowing Across All Markets (part2)", + description: `This VIP upgrades all VToken market implementations across the seven non-BNB Chain Venus Isolated Pool deployments to patch the donation (exchange rate inflation) attack vulnerability identified following the THE token incident on BNB Chain (March 15, 2026). The companion BNB Chain fix is covered in a separate VIP. + +The root cause is that _getCashPrior() previously returned IERC20(underlying).balanceOf(address(this)), which allowed any party to artificially inflate the reported cash — and therefore the exchange rate — by transferring tokens directly to a VToken contract without minting shares. + +The fix replaces this with an internalCash storage variable that is only updated by _doTransferIn, _doTransferOut, and badDebtRecovered(). A one-time syncCash() function, gated by the AccessControlManager (ACM), initializes internalCash to the real underlying balance after the upgrade. syncCash() remains callable post-migration for future reconciliation (e.g., airdrops or direct transfers). + +A secondary measure, pausing borrowing across all affected markets, was deployed to limit exposure while this patch is prepared. Supply, repay, and withdraw functions were not affected by that pause and continue to operate normally. If this VIP passes, borrowing will be restored across all previously paused non-BNB-chain VToken markets. + +#### Changes + +**1. Grant syncCash() permission to the Normal Timelock for each VToken on all non-BNB Chain Isolated Pool deployments** + +Networks: Arbitrum One, Base Mainnet, Ethereum Mainnet, opBNB Mainnet, OP Mainnet, Unichain Mainnet, zkSync Mainnet + +- **Function**: AccessControlManager.giveCallPermission(vToken, "syncCash()", normalTimelock) +- **Effect**: Authorizes the Normal Timelock to call syncCash() on each VToken market, which is required before the implementation upgrade and cash initialization in VIP-602 + +#### Summary + +If approved, this VIP will: +- Grant syncCash() call permissions to the Normal Timelock for all VToken markets on Arbitrum One, Base Mainnet, Ethereum Mainnet, opBNB Mainnet, OP Mainnet, Unichain Mainnet, and zkSync Mainnet +- Enable the subsequent VIP-602 to upgrade the VToken beacon and initialize internalCash via syncCash() on all affected markets + +#### References + +- GitHub PR (Isolated Pools patch): [https://github.com/VenusProtocol/isolated-pools/pull/551](https://github.com/VenusProtocol/isolated-pools/pull/551) +- Allez Labs Post-Mortem: [https://community.venus.io/t/the-market-incident-post-mortem/5712](https://community.venus.io/t/the-market-incident-post-mortem/5712) + +**Voting options** +- For - I agree that Venus Protocol should proceed with this proposal +- Against - I do not think that Venus Protocol should proceed with this proposal +- Abstain - I am indifferent to whether Venus Protocol proceeds or not`, forDescription: "I agree that Venus Protocol should proceed with this proposal", againstDescription: "I do not think that Venus Protocol should proceed with this proposal", abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", @@ -131,4 +160,4 @@ export const vip608_2 = () => { ); }; -export default vip608_2; +export default vip601; diff --git a/vips/vip-600/bscmainnet-3.ts b/vips/vip-600/bscmainnet-3.ts new file mode 100644 index 000000000..3fab6965f --- /dev/null +++ b/vips/vip-600/bscmainnet-3.ts @@ -0,0 +1,195 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + ARBITRUMONE_CORE_VTOKENS, + ARBITRUMONE_DST_CHAIN_ID, + ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + ARBITRUMONE_VTOKEN_BEACON, +} from "./addresses/arbitrumone"; +import { + BASEMAINNET_CORE_VTOKENS, + BASEMAINNET_DST_CHAIN_ID, + BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + BASEMAINNET_VTOKEN_BEACON, +} from "./addresses/basemainnet"; +import { + ETHEREUM_CORE_VTOKENS, + ETHEREUM_DST_CHAIN_ID, + ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + ETHEREUM_VTOKEN_BEACON, +} from "./addresses/ethereum"; +import { + OPBNBMAINNET_CORE_VTOKENS, + OPBNBMAINNET_DST_CHAIN_ID, + OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPBNBMAINNET_VTOKEN_BEACON, +} from "./addresses/opbnbmainnet"; +import { + OPMAINNET_CORE_VTOKENS, + OPMAINNET_DST_CHAIN_ID, + OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + OPMAINNET_VTOKEN_BEACON, +} from "./addresses/opmainnet"; +import { + UNICHAINMAINNET_CORE_VTOKENS, + UNICHAINMAINNET_DST_CHAIN_ID, + UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + UNICHAINMAINNET_VTOKEN_BEACON, +} from "./addresses/unichainmainnet"; +import { + ZKSYNCMAINNET_CORE_VTOKENS, + ZKSYNCMAINNET_DST_CHAIN_ID, + ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + ZKSYNCMAINNET_VTOKEN_BEACON, +} from "./addresses/zksyncmainnet"; + +interface NetworkConfig { + name: string; + vTokenBeacon: string; + newImplementation: string; + dstChainId: number; + coreVTokens: string[]; +} + +const NETWORKS: NetworkConfig[] = [ + { + name: "Ethereum", + vTokenBeacon: ETHEREUM_VTOKEN_BEACON, + newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ETHEREUM_DST_CHAIN_ID, + coreVTokens: ETHEREUM_CORE_VTOKENS, + }, + { + name: "Arbitrum", + vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, + newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ARBITRUMONE_DST_CHAIN_ID, + coreVTokens: ARBITRUMONE_CORE_VTOKENS, + }, + { + name: "Optimism", + vTokenBeacon: OPMAINNET_VTOKEN_BEACON, + newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPMAINNET_DST_CHAIN_ID, + coreVTokens: OPMAINNET_CORE_VTOKENS, + }, + { + name: "Base", + vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, + newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: BASEMAINNET_DST_CHAIN_ID, + coreVTokens: BASEMAINNET_CORE_VTOKENS, + }, + { + name: "opBNB", + vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, + newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: OPBNBMAINNET_DST_CHAIN_ID, + coreVTokens: OPBNBMAINNET_CORE_VTOKENS, + }, + { + name: "Unichain", + vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, + newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, + coreVTokens: UNICHAINMAINNET_CORE_VTOKENS, + }, + { + name: "ZkSync", + vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, + newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, + dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, + coreVTokens: ZKSYNCMAINNET_CORE_VTOKENS, + }, +]; + +export const vip602 = () => { + const meta = { + version: "v2", + title: "VIP-602 [Non-BNB Chain] VToken Inflation Attack Patch and Pause Borrowing Across All Markets (part3)", + description: `This VIP upgrades all VToken market implementations across the seven non-BNB Chain Venus Isolated Pool deployments to patch the donation (exchange rate inflation) attack vulnerability identified following the THE token incident on BNB Chain (March 15, 2026). The companion BNB Chain fix is covered in a separate VIP (VIP-600). + +The root cause is that _getCashPrior() previously returned IERC20(underlying).balanceOf(address(this)), which allowed any party to artificially inflate the reported cash — and therefore the exchange rate — by transferring tokens directly to a VToken contract without minting shares. + +The fix replaces this with an internalCash storage variable that is only updated by _doTransferIn, _doTransferOut, and badDebtRecovered(). A one-time syncCash() function, gated by the AccessControlManager (ACM), initializes internalCash to the real underlying balance after the upgrade. syncCash() remains callable post-migration for future reconciliation (e.g., airdrops or direct transfers). + +A secondary measure, pausing borrowing across all affected markets, was deployed to limit exposure while this patch is prepared. Supply, repay, and withdraw functions were not affected by that pause and continue to operate normally. If this VIP passes, borrowing will be restored across all previously paused non-BNB-chain VToken markets. + +#### Changes + +**1. Upgrade VToken implementation on all non-BNB Chain Isolated Pool deployments** + +Networks: Arbitrum One, Base Mainnet, Ethereum Mainnet, opBNB Mainnet, OP Mainnet, Unichain Mainnet, zkSync Mainnet + +- **Beacon upgrade**: VTokenBeacon.upgradeTo(newVTokenImpl) per network +- **Effect**: All VToken proxies on each network begin using the new implementation, which tracks internalCash instead of calling balanceOf(address(this)) in _getCashPrior() + +**2. Contract changes in the new VToken implementation** + +- VTokenStorage: adds uint256 public internalCash (storage gap reduced from 48 to 47) +- _getCashPrior(): returns internalCash instead of IERC20(underlying).balanceOf(address(this)) +- _doTransferIn(): increments internalCash by the actual amount received (balance delta, handles fee-on-transfer tokens) +- _doTransferOut(): decrements internalCash before transfer +- badDebtRecovered(uint256 recoveredAmount_): increments internalCash to keep cash accounting correct during bad debt recovery via Shortfall auctions +- syncCash(): ACM-gated, nonReentrant; sets internalCash = IERC20(underlying).balanceOf(address(this)); emits CashSynced(uint256 oldCash, uint256 newCash) +- VTokenInterface: adds CashSynced event and syncCash() signature + +**3. Initialize internalCash via syncCash() on all markets across all seven networks** + +- **Function**: syncCash() +- **Caller**: ACM-authorized governance timelock +- **Per market per network**: one call per VToken market +- **Effect**: Sets internalCash to the current real underlying balance, bootstrapping the internal accounting post-upgrade + +**4. Unpause borrowing across all affected markets** + +- Reverses the precautionary borrow pause applied in the preceding VIP +- Supply, repay, and withdraw were never affected +- Applies per market per network across all seven chains + +#### Summary + +If approved, this VIP will: +- Upgrade all non-BNB Chain VToken implementations to replace balanceOf-based cash reporting with the internalCash variable, making exchange rate calculations immune to direct token donations +- Call syncCash() on every market post-upgrade to initialize internalCash to the real underlying balance +- Restore borrowing across all previously paused VToken markets on Arbitrum One, Base Mainnet, Ethereum Mainnet, opBNB Mainnet, OP Mainnet, Unichain Mainnet, and zkSync Mainnet +- Ensure badDebtRecovered() correctly updates internalCash during Shortfall auction settlements to prevent exchange rate drift + +#### References + +- GitHub PR (Isolated Pools patch): [https://github.com/VenusProtocol/isolated-pools/pull/551](https://github.com/VenusProtocol/isolated-pools/pull/551) +- Allez Labs Post-Mortem: [https://community.venus.io/t/the-market-incident-post-mortem/5712](https://community.venus.io/t/the-market-incident-post-mortem/5712) + +**Voting options** +- For - I agree that Venus Protocol should proceed with this proposal +- Against - I do not think that Venus Protocol should proceed with this proposal +- Abstain - I am indifferent to whether Venus Protocol proceeds or not`, + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + NETWORKS.flatMap(({ vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ + // Upgrade the beacon to the new implementation + { + target: vTokenBeacon, + signature: "upgradeTo(address)", + params: [newImplementation], + dstChainId, + }, + // Call syncCash on each VToken to initialize internalCash + ...coreVTokens.map(vToken => ({ + target: vToken, + signature: "syncCash()", + params: [], + dstChainId, + })), + ]), + meta, + ProposalType.REGULAR, + ); +}; + +export default vip602; diff --git a/vips/vip-608/bscmainnet.ts b/vips/vip-600/bscmainnet.ts similarity index 64% rename from vips/vip-608/bscmainnet.ts rename to vips/vip-600/bscmainnet.ts index 9590c82e2..b6f4238cc 100644 --- a/vips/vip-608/bscmainnet.ts +++ b/vips/vip-600/bscmainnet.ts @@ -101,14 +101,45 @@ const MARKETS: MarketConfig[] = [ { vToken: vXAUM, name: "vXAUM", excessAmount: "0" }, ]; -export const vip608 = () => { +export const vip600 = () => { const meta = { version: "v2", - title: "VIP-608 Upgrade VBep20Delegate implementation for Core Pool markets", - description: - "Upgrade all 43 VBep20Delegator core pool markets on BSC mainnet to the new VBep20Delegate implementation. " + - "For each market: (1) set the new implementation, (2) call sweepTokenAndSync to initialize internalCash. " + - "Markets with excess tokens have their excess swept to the Timelock before syncing.", + title: "VIP-600 [BNB Chain] VToken Inflation Attack Patch (part1)", + description: `This VIP upgrades the VBep20Delegate implementation across all 43 BNB Chain core pool VBep20 markets to patch the donation attack vulnerability identified in the THE market incident. The root cause is in getCashPrior() within VBep20.sol, which previously returned IERC20(underlying).balanceOf(address(this)) — an actual on-chain balance that any external actor can inflate by transferring tokens directly to the vToken contract address. The inflated cash distorts the exchange rate formula ((getCashPrior() + totalBorrows - totalReserves) / totalSupply), enabling exchange rate manipulation and first-depositor attacks. + +The fix replaces balance-based cash accounting with an internalCash storage variable that is only updated through protocol-controlled operations (doTransferIn and doTransferOut). Direct token donations can no longer affect getCashPrior() or the exchange rate. A new admin-gated sweepTokenAndSync() function is called atomically in the same VIP to initialise internalCash after each market's implementation upgrade, and to recover any excess donated tokens to the Timelock. + +The 43 affected markets are all VBep20Delegator core pool proxies on BSC mainnet. vBNB is excluded as it is a native BNB market and not subject to this vulnerability. + +#### Changes + +1. **Upgrade VBep20Delegate implementation for all 43 core pool markets** + - Function: proxy._setImplementation(newImpl, false, "0x") + - Points each VBep20Delegator proxy to the new VBep20Delegate implementation containing internal cash tracking + - Markets covered include major stablecoins (vUSDC, vUSDT, vBUSD, vFDUSD, vTUSD, vDAI, vUSDe, vsUSDe, vUSD1), blue-chip assets (vBTC, vETH, vBNB-variants), DeFi tokens (vXVS, vCAKE, vAAVE, vUNI, vLINK), LST/LRT products (vWBETH, vasBNB, vslisBNB, vSolvBTC, vxSolvBTC), and others including vTHE. +2. **Call sweepTokenAndSync(excessAmount) on each upgraded market** + - Function: proxy.sweepTokenAndSync(uint256 transferAmount) + - Must be executed atomically in the same VIP as the implementation upgrade + - Pass 0 for markets with no excess tokens; pass the excess amount for markets with prior donations (e.g. vTHE) to transfer excess to the Timelock before syncing + - Initialises internalCash to match balanceOf(address(this)) post-upgrade + +#### Summary + +If approved, this VIP will: +- Upgrade the VBep20Delegate implementation for all 43 BNB Chain core pool VBep20 markets to use internal cash tracking +- Close the donation attack vector by making getCashPrior() immune to direct token transfers +- Initialise internalCash on all upgraded markets via sweepTokenAndSync() in the same transaction +- Recover any excess donated tokens (including from the vTHE exploit) to the Timelock + +#### References + +- GitHub PR (Core Pool): [https://github.com/VenusProtocol/venus-protocol/pull/664](https://github.com/VenusProtocol/venus-protocol/pull/664) +- Allez Labs Post-Mortem: [https://community.venus.io/t/the-market-incident-post-mortem/5712](https://community.venus.io/t/the-market-incident-post-mortem/5712) + +**Voting options** +- For - I agree that Venus Protocol should proceed with this proposal +- Against - I do not think that Venus Protocol should proceed with this proposal +- Abstain - I am indifferent to whether Venus Protocol proceeds or not`, forDescription: "I agree that Venus Protocol should proceed with this proposal", againstDescription: "I do not think that Venus Protocol should proceed with this proposal", abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", @@ -132,4 +163,4 @@ export const vip608 = () => { ); }; -export default vip608; +export default vip600; diff --git a/vips/vip-608/bscmainnet-3.ts b/vips/vip-608/bscmainnet-3.ts deleted file mode 100644 index 3b12aac57..000000000 --- a/vips/vip-608/bscmainnet-3.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { ProposalType } from "src/types"; -import { makeProposal } from "src/utils"; - -import { - ARBITRUMONE_CORE_VTOKENS, - ARBITRUMONE_DST_CHAIN_ID, - ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, - ARBITRUMONE_VTOKEN_BEACON, -} from "./addresses/arbitrumone"; -import { - BASEMAINNET_CORE_VTOKENS, - BASEMAINNET_DST_CHAIN_ID, - BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, - BASEMAINNET_VTOKEN_BEACON, -} from "./addresses/basemainnet"; -import { - ETHEREUM_CORE_VTOKENS, - ETHEREUM_DST_CHAIN_ID, - ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, - ETHEREUM_VTOKEN_BEACON, -} from "./addresses/ethereum"; -import { - OPBNBMAINNET_CORE_VTOKENS, - OPBNBMAINNET_DST_CHAIN_ID, - OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, - OPBNBMAINNET_VTOKEN_BEACON, -} from "./addresses/opbnbmainnet"; -import { - OPMAINNET_CORE_VTOKENS, - OPMAINNET_DST_CHAIN_ID, - OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, - OPMAINNET_VTOKEN_BEACON, -} from "./addresses/opmainnet"; -import { - UNICHAINMAINNET_CORE_VTOKENS, - UNICHAINMAINNET_DST_CHAIN_ID, - UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, - UNICHAINMAINNET_VTOKEN_BEACON, -} from "./addresses/unichainmainnet"; -import { - ZKSYNCMAINNET_CORE_VTOKENS, - ZKSYNCMAINNET_DST_CHAIN_ID, - ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, - ZKSYNCMAINNET_VTOKEN_BEACON, -} from "./addresses/zksyncmainnet"; - -interface NetworkConfig { - name: string; - vTokenBeacon: string; - newImplementation: string; - dstChainId: number; - coreVTokens: string[]; -} - -const NETWORKS: NetworkConfig[] = [ - { - name: "Ethereum", - vTokenBeacon: ETHEREUM_VTOKEN_BEACON, - newImplementation: ETHEREUM_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: ETHEREUM_DST_CHAIN_ID, - coreVTokens: ETHEREUM_CORE_VTOKENS, - }, - { - name: "Arbitrum", - vTokenBeacon: ARBITRUMONE_VTOKEN_BEACON, - newImplementation: ARBITRUMONE_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: ARBITRUMONE_DST_CHAIN_ID, - coreVTokens: ARBITRUMONE_CORE_VTOKENS, - }, - { - name: "Optimism", - vTokenBeacon: OPMAINNET_VTOKEN_BEACON, - newImplementation: OPMAINNET_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: OPMAINNET_DST_CHAIN_ID, - coreVTokens: OPMAINNET_CORE_VTOKENS, - }, - { - name: "Base", - vTokenBeacon: BASEMAINNET_VTOKEN_BEACON, - newImplementation: BASEMAINNET_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: BASEMAINNET_DST_CHAIN_ID, - coreVTokens: BASEMAINNET_CORE_VTOKENS, - }, - { - name: "opBNB", - vTokenBeacon: OPBNBMAINNET_VTOKEN_BEACON, - newImplementation: OPBNBMAINNET_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: OPBNBMAINNET_DST_CHAIN_ID, - coreVTokens: OPBNBMAINNET_CORE_VTOKENS, - }, - { - name: "Unichain", - vTokenBeacon: UNICHAINMAINNET_VTOKEN_BEACON, - newImplementation: UNICHAINMAINNET_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: UNICHAINMAINNET_DST_CHAIN_ID, - coreVTokens: UNICHAINMAINNET_CORE_VTOKENS, - }, - { - name: "ZkSync", - vTokenBeacon: ZKSYNCMAINNET_VTOKEN_BEACON, - newImplementation: ZKSYNCMAINNET_NEW_VTOKEN_IMPLEMENTATION, - dstChainId: ZKSYNCMAINNET_DST_CHAIN_ID, - coreVTokens: ZKSYNCMAINNET_CORE_VTOKENS, - }, -]; - -export const vip608_3 = () => { - const meta = { - version: "v2", - title: "VIP-608 Upgrade VToken beacon and syncCash for isolated pools (all networks)", - description: - "Upgrade VToken beacon proxies and call syncCash on each VToken on all remote networks: " + - "Ethereum, Arbitrum, Optimism, Base, opBNB, Unichain, and ZkSync. " + - "For each network: (1) call upgradeTo on the VTokenBeacon, (2) call syncCash on each VToken to initialize internalCash.", - forDescription: "I agree that Venus Protocol should proceed with this proposal", - againstDescription: "I do not think that Venus Protocol should proceed with this proposal", - abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", - }; - - return makeProposal( - NETWORKS.flatMap(({ vTokenBeacon, newImplementation, dstChainId, coreVTokens }) => [ - // Upgrade the beacon to the new implementation - { - target: vTokenBeacon, - signature: "upgradeTo(address)", - params: [newImplementation], - dstChainId, - }, - // Call syncCash on each VToken to initialize internalCash - ...coreVTokens.map(vToken => ({ - target: vToken, - signature: "syncCash()", - params: [], - dstChainId, - })), - ]), - meta, - ProposalType.REGULAR, - ); -}; - -export default vip608_3; From 52a28d791f0b9ad0e396b39a7ed1fb8db52b1b78 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 20 Mar 2026 13:26:31 +0000 Subject: [PATCH 12/12] chore: prettier format simulation files after rebase Co-Authored-By: Claude Sonnet 4.6 --- simulations/vip-600/arbitrumone.ts | 3 ++- simulations/vip-600/basemainnet.ts | 3 ++- simulations/vip-600/ethereum.ts | 3 ++- simulations/vip-600/opbnbmainnet.ts | 3 ++- simulations/vip-600/opmainnet.ts | 3 ++- simulations/vip-600/unichainmainnet.ts | 3 ++- simulations/vip-600/zksyncmainnet.ts | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/simulations/vip-600/arbitrumone.ts b/simulations/vip-600/arbitrumone.ts index 9081134b1..a97dc3db0 100644 --- a/simulations/vip-600/arbitrumone.ts +++ b/simulations/vip-600/arbitrumone.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/arbitrumone"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 443758428; diff --git a/simulations/vip-600/basemainnet.ts b/simulations/vip-600/basemainnet.ts index 0c0085182..f1df4303f 100644 --- a/simulations/vip-600/basemainnet.ts +++ b/simulations/vip-600/basemainnet.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/basemainnet"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 43608484; diff --git a/simulations/vip-600/ethereum.ts b/simulations/vip-600/ethereum.ts index bc26fb883..0f745bf6a 100644 --- a/simulations/vip-600/ethereum.ts +++ b/simulations/vip-600/ethereum.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/ethereum"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 24698424; diff --git a/simulations/vip-600/opbnbmainnet.ts b/simulations/vip-600/opbnbmainnet.ts index e520255c0..19faddb19 100644 --- a/simulations/vip-600/opbnbmainnet.ts +++ b/simulations/vip-600/opbnbmainnet.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/opbnbmainnet"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 123557813; diff --git a/simulations/vip-600/opmainnet.ts b/simulations/vip-600/opmainnet.ts index 3e09e6e6b..7a110f500 100644 --- a/simulations/vip-600/opmainnet.ts +++ b/simulations/vip-600/opmainnet.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/opmainnet"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 149203740; diff --git a/simulations/vip-600/unichainmainnet.ts b/simulations/vip-600/unichainmainnet.ts index 88385132f..bbd1b691b 100644 --- a/simulations/vip-600/unichainmainnet.ts +++ b/simulations/vip-600/unichainmainnet.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/unichainmainnet"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 43258056; diff --git a/simulations/vip-600/zksyncmainnet.ts b/simulations/vip-600/zksyncmainnet.ts index 2d7f7d1ea..7cc01c4b9 100644 --- a/simulations/vip-600/zksyncmainnet.ts +++ b/simulations/vip-600/zksyncmainnet.ts @@ -12,7 +12,8 @@ import { } from "../../vips/vip-600/addresses/zksyncmainnet"; import vip601 from "../../vips/vip-600/bscmainnet-2"; import vip602 from "../../vips/vip-600/bscmainnet-3"; -import COMPTROLLER_ABI from "./abi/ILComptroller.json";import VTOKEN_ABI from "./abi/ILVToken.json"; +import COMPTROLLER_ABI from "./abi/ILComptroller.json"; +import VTOKEN_ABI from "./abi/ILVToken.json"; import VTOKEN_BEACON_ABI from "./abi/vtokenBeacon.json"; const BLOCK_NUMBER = 69170974;