[VPD-1314] Configure PrimeV2 on BSC#712
Conversation
Bring PrimeV2 + PrimeLeaderboard live on bsctestnet: accept ownership, grant ACM perms (epoch ops to NormalTimelock + Guardian), wire the pair, repoint PrimeLiquidityProvider, add Core pool markets, open the mint window, and pause the legacy Prime. Includes fork simulation.
|
missing command for |
|
also update the Prime address in the Comptroller ? |
Add missing XVSVault.setPrimeToken (-> PrimeLeaderboard) and Comptroller.setPrimeToken (-> PrimeV2); extend sim assertions and fix grant helper comment.
Add pre-VIP assertions for wiring=0 and mint window=0; tighten PLP prime equality check; mention vault/comptroller switches in header.
|
Testnet execution: https://venus-testnet.vercel.app/#/governance/proposal/684?chainId=97 |
Extend Guardian ACM permissions to every remaining ACM-gated function on PrimeV2 / PrimeLeaderboard, and compress the leaderboard multiplier tiers from days (30/60/90d) to hours (1/2/3h) so testnet integration can exercise the tier progression without long waits.
|
Testnet Addendum: https://venus-testnet.vercel.app/#/governance/proposal/685?chainId=97 |
Upgrades the bsctestnet PrimeV2 proxy to the new implementation deployed via venus-protocol PR #677 and grants ACM permission for the new recordCycleSnapshot(uint256) function to NormalTimelock and Guardian, matching the keeper grant pattern from the original VIP-675.
Redeployed PrimeV2/PrimeLeaderboard (fresh proxies + impls), so the old addenda's wiring is stale. Accept ownership, re-grant the full ACM set against the new addresses (plus testnet-only resetCycle), wire the pair, and repoint the shared PLP/XVS Vault/Comptroller hooks old→new. No legacy togglePause — already paused by VIP-675.
|
Testnet Addendum: https://venus-testnet.vercel.app/#/governance/proposal/688?chainId=97 |
Wire deployed PrimeV2 / PrimeLeaderboard: accept ownership, grant cycle ACM perms to NT + Keeper + Guardian (admin NT-only), repoint PLP / XVS Vault / Comptroller from legacy Prime, add 6 markets, pause vault until seeding, decommission legacy.
Add the vWBNB market to PrimeV2 on bsctestnet so FE/BE integration can exercise the new market flow. Initializes WBNB on PrimeLiquidityProvider and sets the distribution speed to mirror mainnet (3.47e12 wei/block) for production-like accrual during testing. Supply/borrow multipliers match the legacy mainnet Prime (2e18 / 0).
|
Testnet addedum to add WBNB market: https://venus-testnet.vercel.app/#/governance/proposal/691?chainId=97 |
Add ACM grants so the team multisig (0xCCa5...2948) can fire pause() on both PrimeV2 and the XVS Vault as an emergency circuit breaker, matching the cross-chain MULTISIG_PAUSER pattern from vip-616.
Remove setMintThreshold from the Keeper's permission set on PrimeV2 — opening/closing the permissionless mint window is sensitive and reserved for governance and the Venus Guardian multisig. Update simulation assertions (RoleGranted 43 -> 42) and add tests verifying NormalTimelock and Guardian hold the role while the Keeper does not.
…issions Pauses the XVS Vault and zeroes the PLP distribution speed for every legacy Prime underlying, so an off-chain claimInterest sweep runs against frozen balances before PrimeV2 goes live. Executes via CriticalTimelock, which already holds both permissions (no ACM grants).
Launch PrimeV2 with only the vUSDT and vWBNB markets instead of all six legacy Prime markets, and re-set the PLP distribution speed for USDT and WBNB (zeroed by the preceding critical VIP) back to their prior live values. Sim now runs that critical VIP via CriticalTimelock in before() so the regular VIP executes against real frozen state.
fred-venus
left a comment
There was a problem hiding this comment.
Should we also consider reallocating the xvs buyback part to prime reward buyback contract ?
…st-freeze Add a post-VIP block to the critical sim proving an arbitrary EOA can sweep vUSDT interest on behalf of four real legacy Prime whale holders after the freeze, confirming the off-chain claimInterest sweep pays holders directly.
As discussed, we will handle this through a separate VIP, where the reserve protocol configurations will be updated and the income from the XVS buyback contract will be allocated to Prime reward distribution. |
…mounts Strengthen the permissionless-claim test: assert the sweeping EOA is not a legacy Prime holder while each whale is, and console the prime status plus the before/after USDT balances so the sweep payout is visible in the run output.
initializeStakers and finalizeInitialization are a one-time off-chain bootstrap, so the NormalTimelock does not need them. Grant only the Keeper and the Guardian multisig; drops the RoleGranted count from 42 to 40.
Exercise the full end-user lifecycle (mint/borrow/accrue/claim/repay/redeem/exit) across PrimeV2 and non-Prime markets, the PrimeV2 score hook for a real holder, liquidation seize math, and the VAI mint gate after the legacy Prime -> PrimeV2 repoint. Repoint stale feeds to Chainlink-only to survive the testVip time-jump. Document each scenario and ACM permission assertion inline.
Summary
Brings the new PrimeV2 and PrimeLeaderboard contracts live and decommissions the legacy Prime, on BSC testnet and mainnet. The contracts are implemented in venus-protocol PR #670 (development) and deployed via venus-protocol PR #677 (deployments). These VIPs perform the on-chain governance setup the deploy scripts intentionally leave to governance.
Why two VIPs on mainnet?
PrimeLiquidityProvider, "PLP"). That vault can be pointed at only one Prime at a time.claimInterestcalls would start failing.This ordering guarantees no user is left holding unclaimable legacy rewards. (Mechanically, repointing the shared PLP strands the legacy Prime's accounting and its next claim reverts — proven in the simulations below — which is exactly why the off-chain sweep must run first.)
Mainnet rollout sequence
Critical VIP — freeze (
vips/vip-675/bscmainnet-critical.ts)XVSVault.pause()— freeze staking for the migration window (Critical Timelock already holds the perm).PLP.setTokensDistributionSpeed = 0for every legacy Prime underlying (ETH, BTCB, USDC, USDT, U, WBNB) — stop new interest accrual so the sweep captures a stable balance. LegacyclaimInterestkeeps working while the vault is paused.BE sweep (off-chain) — call
claimInterest(vToken, user)for every legacy Prime holder across all markets, then verify V1 is clean (every holder's claimable == 0) as a hard gate before the next VIP.Migration VIP — regular (
vips/vip-675/bscmainnet.ts) — ten command groups, in order:PrimeV2.acceptOwnership()andPrimeLeaderboard.acceptOwnership(). The deploy script (PR [VPD-687]: VIP to Update Core Pool vslisBNB Market Risk Parameters #677) initiated the 2-step transfer to NormalTimelock; this completes it.PrimeV2.setPrimeLeaderboard(PrimeLeaderboard)andPrimeLeaderboard.setPrimeV2(PrimeV2).PLP.setPrimeToken(PrimeV2). The shared PrimeLiquidityProvider now serves PrimeV2; legacy Prime is no longer its authorized prime (this is the step that strands legacyunreleasedPLPIncome, hence the pre-VIP sweep).XVSVault.setPrimeToken(PrimeLeaderboard, XVS, 0). Vault deposits/withdrawals now update PrimeLeaderboard (which calls PrimeV2); reward token and pool id unchanged.Comptroller.setPrimeToken(PrimeV2), so market supply/borrow hooks call the new contract.VAIController.setPrimeToken(PrimeV2). VAI minting is gated on Prime-holder status (mintEnabledOnlyForPrimeHolder = true, readsIPrime(prime).isUserPrimeHolder), so this keeps the gate tracking live PrimeV2 membership instead of the decommissioned legacy Prime.PrimeV2.addMarketforvUSDTandvWBNB(2x supply multiplier, 0x borrow multiplier). PrimeV2 intentionally does not carry the other legacy markets.setTokensDistributionSpeedfor the two carried underlyings back to their prior live speeds (USDT2170138888888888, WBNB3472222222222); the other four underlyings stay at0.LegacyPrime.togglePause()to pause it permanently.Not re-set by this VIP (configured in the contracts' initializers): the PrimeLeaderboard multiplier tiers (30/60/90 days → 1.3x/1.6x/2.0x) and the PrimeV2 token limit (500). The permissionless mint window is also left uninitialized on purpose (
mintThreshold = 0,mintDeadline = 0) — governance or the Guardian opens it later viasetMintThreshold.BE seeding (off-chain) — snapshot staker list, seed existing stakers into PrimeLeaderboard via
initializeStakers/finalizeInitialization, mint initial Prime users via the Keeperissuepath (the permissionless mint window is left uninitialized:mintThreshold = 0,mintDeadline = 0).Resume — Guardian calls
XVSVault.resume()(Guardian already holds the perm).The XVS Vault stays paused from step 1 through step 5; users continue to supply/borrow on the Core pool markets throughout — only XVS staking is frozen.
ACM permission matrix
Granted by the migration VIP (
vips/vip-675/bscmainnet.ts, step 2). NT = NormalTimelock, FTT = FastTrackTimelock, CT = CriticalTimelock, GUARDIAN = Venus Guardian (0x1C2CAc6ec528c20800B2fe734820D87b581eAA6B), KEEPER =0xe0237587acA20f9304d30FACC9Afcd5DD9a94899, MULTISIG_PAUSER = Venus team multisig (0xCCa5a587eBDBe80f23c8610F2e53B03158e62948).ACM permission matrix — who can call each gated function, grouped by intent (click to expand)
Read it as: keeper-driven cycle ops go to NT + KEEPER + GUARDIAN; one-off policy levers stay NT-only; pause is split between the timelocks (full control) and the team multisig (one-way circuit breaker).
issue/issueBatch/burn/burnBatchrecordCycleSnapshotsetMintThresholdsetPrimeLeaderboard,addMarket,removeMarket,setLimit,updateAlpha,updateMultipliers,setMaxLoopsLimit,sweepUndistributedpause/unpausepauseinitializeStakers/finalizeInitializationsetMultiplierTiers,setPrimeV2,setMaxLoopsLimitpauseresume()already held by Guardian + NT)Command card — every on-chain command (both mainnet VIPs)
Complete, execution-ordered list of every command in the two mainnet proposals. ACM grant rows use
ACM.giveCallPermission(target, signature, account); each expands to one tx per grantee (seeTxns). Addresses: PrimeV20x4f5f…1628, PrimeLeaderboard0x55e2…2c0b, PLP0x23c4…33F2, LegacyPrime0xBbCD…71FC, VAIController0x0040…FAFE, Comptroller = Unitroller, KEEPER0xe023…4899, MULTISIG_PAUSER0xCCa5…2948, GUARDIAN0x1C2C…aA6B.Critical VIP — 2 txns (freeze staking + stop emissions)
pause()setTokensDistributionSpeed(address[],uint256[])[ETH,BTCB,USDC,USDT,U,WBNB],[0,0,0,0,0,0]Migration VIP · Ownership — 2 txns (complete the 2-step transfer)
acceptOwnership()acceptOwnership()Migration VIP · ACM grants — 40 txns (one
giveCallPermissionper grantee)Each row grants
target.signatureto every listed account;Txns= number of grantees (the literal command count). Target of each command is the ACM.issue(address)issueBatch(address[])burn(address)burnBatch(address[])setMintThreshold(uint256,uint256)recordCycleSnapshot(uint256)setPrimeLeaderboard(address)addMarket(address,uint256,uint256)removeMarket(address)setLimit(uint256)updateAlpha(uint128,uint128)updateMultipliers(address,uint256,uint256)setMaxLoopsLimit(uint256)sweepUndistributed(address,address)pause()unpause()pause()initializeStakers(address[],uint256[],uint64[])finalizeInitialization()setMultiplierTiers(uint256[],uint256[])setPrimeV2(address)setMaxLoopsLimit(uint256)pause()Migration VIP · Wiring, repoint, markets, emissions, decommission — 10 txns
setPrimeLeaderboard(address)setPrimeV2(address)setPrimeToken(address)setPrimeToken(address,address,uint256)0setPrimeToken(address)setPrimeToken(address)addMarket(address,uint256,uint256)2e18,0addMarket(address,uint256,uint256)2e18,0setTokensDistributionSpeed(address[],uint256[])[USDT,WBNB],[2170138888888888, 3472222222222]togglePause()Totals: Critical = 2 txns · Migration = 52 txns (2 ownership + 40 ACM grants + 10 wiring/config) · combined = 54 on-chain txns.
Testnet
vips/vip-675/bsctestnet.tsplus addenda iterated the rollout on testnet:Simulations
simulations/vip-675/bscmainnet-critical.ts— asserts the pre/post state of the freeze (XVS Vault paused, every Prime underlying distribution speed zeroed, legacy Prime still active). Also proves the off-chain sweep is viable: for four real legacy Prime whale holders, an arbitrary EOA (not the holder) callsclaimInterest(vUSDT, whale)after the freeze and the whale's USDT balance increases — confirmingclaimInterestis permissionless and pays the holder directly.simulations/vip-675/bscmainnet.ts— runs the critical VIP first (pretendExecutingVipvia Critical Timelock), then asserts the full migration lifecycle and post-state: ownership, wiring, PLP/Comptroller/vault hook repoint, restored emissions, ACM roles, markets, and legacy Prime decommission. Includes two tests proving the legacy claim path is dead post-migration — one reverting cleanly under pause, one proving the underlyingPanic(0x11)underflow when unpaused after the PLP drain.simulations/vip-675/bsctestnet*.ts— testnet lifecycle + addenda post-state.Test case index —
simulations/vip-675/bscmainnet.tsEvery test case in the mainnet migration simulation, with one line on what each proves (click to expand)
Setup: the suite forks at block
105868057and first runs the critical VIP (pretendExecutingVipvia Critical Timelock) so the migration executes against the real frozen state (XVS Vault paused, PLP speeds zeroed). The core-pool integration block repoints stale feeds to Chainlink-only — test scaffolding, not part of the VIP — so reads survive thetestVip~3-day time-jump that would otherwise stale the live oracles.Pre-VIP behavior — pins the deployed-but-unwired starting state so post-VIP deltas are real:
PrimeV2 ownership pending on NormalTimelock (not accepted)— 2-step transfer initiated by the deploy script, not yet accepted.PrimeLeaderboard ownership pending on NormalTimelock (not accepted)— same for the leaderboard.PLP prime token is still the legacy Prime— rewards vault still serves the old Prime.PrimeV2 <-> PrimeLeaderboard are not yet wired— both cross-references are the zero address.PrimeV2 mint window is uninitialized—mintThreshold/mintDeadlineboth 0.legacy Prime is active (unpaused)— old Prime still live before migration.XVSVault prime hook still points at legacy Prime— stake updates still flow to old Prime.XVSVault is already paused (by the preceding critical VIP)— confirms the freeze landed.Comptroller prime still points at legacy Prime— market hooks still call old Prime.VAIController prime still points at legacy Prime, with the holder mint gate enabled— VAI gate reads old Prime.PLP distribution speeds are zeroed (by the preceding critical VIP)— emissions stopped for every Prime underlying.VIP execution (
testVipcallback) — asserts the proposal's emitted effects:PrimeLeaderboardSet(0 -> PrimeLeaderboard)and oneMarketAddedper configured market (vUSDT, vWBNB).RoleGrantedevents — the full ACM matrix (32 PrimeV2 + 7 PrimeLeaderboard + 1 XVSVault).Post-VIP behavior — every wiring delta the proposal promises:
PrimeV2 owner is the NormalTimelock/PrimeLeaderboard owner is the NormalTimelock— ownership transfers accepted.PLP points at PrimeV2— rewards vault repointed.PrimeV2 <-> PrimeLeaderboard are wired— both cross-references set.PrimeV2 token limit is the initializer default (500)— VIP never callssetLimit, cap stays at init value.PrimeV2 mint window is left uninitialized— window stays closed until a later Guardian/governance action.XVSVault prime hook points at PrimeLeaderboard— stake updates now flow to the leaderboard.XVSVault stays paused (awaiting off-chain staker seeding)— vault frozen so live stake can't shift during seeding.Comptroller prime points at PrimeV2— market hooks now call PrimeV2.VAIController prime points at PrimeV2 (gate now tracks PrimeV2 membership)— VAI gate repointed.PLP distribution speeds restored for USDT and WBNB— emissions back to prior live speeds for the two carried markets.legacy Prime is decommissioned (paused)— old Prime frozen permanently.account <X> holds the cycle permissions on PrimeV2(NT, Keeper, Guardian) — steady-state issue/burn/snapshot automation surface.account <X> holds the seeding permissions on PrimeLeaderboard(Keeper, Guardian) — one-time staker bootstrap.NormalTimelock does NOT hold the one-time seeding permissions on PrimeLeaderboard— seeding is keeper/Guardian-only.setMintThreshold is held by the NormalTimelock and the Guardian multisig only— sensitive mint-window lever.Keeper does NOT hold setMintThreshold on PrimeV2— keeper kept off the mint-window lever.Keeper does NOT hold the admin-only permissions on PrimeV2/...on PrimeLeaderboard— policy levers stay NT-only.Guardian holds the XVSVault resume() permission— Guardian unfreezes the vault after seeding.Venus team multisig holds the circuit-breaker pause() permissions— one-way pause on PrimeV2 + XVSVault.market <vToken> is configured on PrimeV2(vUSDT, vWBNB) — multipliers match the proposal (2x supply, 0x borrow).Core pool integration after migration (no breakage) — proves the repoint didn't break the live money market:
checkCorePoolComptroller(reusable) — supply/borrow/claim/repay/redeem on vETH + vUSDT for a non-Prime user; the Comptroller→PrimeV2 hook must no-op, not revert.supplies underlying and receives vTokensenters the market as collateralreports healthy account liquidity (no shortfall)shows sufficient hypothetical liquidity for the intended borrowborrows against the supplied collateralaccrues borrow interest over timeclaims XVS rewards without revertingrepays the full borrowredeems all supplied collateralexits the market once the position is closedissuing a token to a supplier initializes a non-zero market score— live score path runs on issue.a holder's further supply updates the market score via the live hook— extra supply lifts the score (stake cap kept above supplied capital).liquidateCalculateSeizeTokens returns a positive seize amount— seize math reachable and prices correctly.seize amount scales with the repaid amount— math is monotonic in repaid amount.accrueInterest succeeds for configured market(vUSDT, vWBNB) — accrual works for added markets.accrueInterest reverts for a market not configured on PrimeV2 (vETH)— explicit accrual requires the market to exist (distinct from the no-op Comptroller hook).Keeper can issue a PrimeV2 token, flipping the holder flag onKeeper can burn the PrimeV2 token, flipping the holder flag offa non-PrimeV2 holder is blocked from minting VAI— gate returns 0 mintable for a non-holder.checkVAIController(reusable) — full VAI mint/repay works once the user is issued, proving the gate repoint is correct.Test plan
npx hardhat test simulations/vip-675/bscmainnet.ts --fork bscmainnetnpx hardhat test simulations/vip-675/bscmainnet-critical.ts --fork bscmainnetnpx hardhat test simulations/vip-675/bsctestnet.ts --fork bsctestnetyarn tsc --noEmit,yarn lint→ cleanReferences
feat(prime): add PrimeV2 and PrimeLeaderboard contracts): feat(prime): add PrimeV2 and PrimeLeaderboard contracts venus-protocol#670VPD-1313 PrimeV2 deployments for BSC): [VPD-1313] PrimeV2 deployments for BSC venus-protocol#677