[VPD-1199]: VIP-620 + VIP-621 TokenBuyback migration + per-tx gas-cap sims#708
Merged
Conversation
add resolvePerTxGasCap (eth_txGasLimitCap RPC, static fallback map in networkConfig) and apply it as tx.gasLimit in testVip, testForkedNetworkVipCommands, pretendExecutingVip so hardhat OOGs at the same boundary mainnet would. catches BSC Maxwell+Osaka and Ethereum Fusaka EIP-7825 caps; future hardforks via EIP-8123 are picked up automatically.
VIP-618's bundled execute() needed ~17.5M gas, exceeding BSC's Maxwell+ Osaka per-tx cap of 2^24 = 16,777,216. Replace with vip-800 part-1 (non-drain migration: grants, ownership, routers, ACM perms, pause, PSR rewire, Prime allocation, RiskFundV2 upgrade) and part-2 (converter drain + handback). Each fits comfortably under the cap.
- Refresh MIGRATION_HELPER_V2 to the 0x296a3E redeploy (commit 746fe99) and update the 10 buyback proxy addresses to the latest redeploy. - Drop the USDC -> USDT swap leg: helper.executeSwap() now runs a single USDC -> USDT -> U multihop. USDC sweep tuned to 10k (4k stays on PLP for unclaimed user rewards); U_MIN_OUT raised to 9,900e18 under the 9,996.60 U QuoterV2 read. - Pull the May 2026 Prime allocation (Prime.addMarket(vU), PLP.initializeTokens/setMax/setSpeed, PLP.sweepToken) out of the helper and drive it from the VIP itself; helper now exposes three one-shot entrypoints — execute1 / executeSwap / execute2. - Move router allowlisting out of execute1 into execute2 to fit BSC's per-tx gas cap. Part 2 hands back ownership of all 16 contracts (10 buybacks + 6 converters) instead of just the 6 converters. - Sims: replace the artifact-bytecode etch with a local ABI under simulations/vip-800/abi/, bump FORK_BLOCK to 98041000 (past the helper and buyback redeploys), and add executedSwap / router- allowlist assertions for the new flow.
fred-venus
reviewed
May 13, 2026
| export const USDT_TREASURY_BUYBACK = "0xBF858c95D778022b48E6Ad343D3d644017fb0ca7"; | ||
| export const USDC_TREASURY_BUYBACK = "0xFB5FA544dBf39983198BDD01e2c26E3AB597e22A"; | ||
| export const XVS_TREASURY_BUYBACK = "0x01D0f07D389692D386EB8D09Da3bbCa5C83be551"; | ||
| export const RISK_FUND_BUYBACK = "0x0c71EFabD00329E839745ef23aB946d3ed24A805"; |
Contributor
There was a problem hiding this comment.
pending owner needs to be point to new helper contract
Contributor
Author
There was a problem hiding this comment.
yeah ownership transferred to new helper contract only
pretendExecutingVip runs each cmd as its own tx, so cap applies per-cmd not to sum. Log per-cmd max + warn on over-cap cmds. Fix EIP-8123 → EIP-7825. Drop dead re-export (no prior callers).
- Restore vip-618's original 10 buyback addresses, USDC_TO_SWEEP=14,986 and U_MIN_OUT=7,418 — the VIP is queued on-chain and must match what governance audited. - Define the redeployed buyback addresses (PR #162) and retuned single-multihop budget (USDC_TO_SWEEP=10,000, U_MIN_OUT=9,900 under the 9,996.60 U QuoterV2 read) locally in vip-800/part-1. - BUYBACKS index order preserved so PSR-row → buyback mapping survives. - vip-800/part-1 keeps re-exporting the untouched vip-618 constants so sims have a single import surface.
Run part-1 through full governance flow (propose/vote/execute) instead of direct timelock impersonation via pretendExecutingVip, so part-2 sees the same post-part-1 state mainnet will. Each testVip uses its own loadFixture function, so part-2's first snapshot captures post-part-1 state. Balances now captured in callbackAfterExecution.
proposalCount is 619; the two-part TokenBuyback migration now lands as VIP-620 (part 1) and VIP-621 (part 2). Directory stays as vips/vip-620/ to keep both parts together with the shared simulation ABI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces VIP-618 (queued but unexecutable on-chain — its bundled
helper.execute()needs ~17.5M gas, exceeding BSC's Maxwell+Osaka per-tx gas cap of 2²⁴ = 16,777,216; EIP-7825 ships the same cap on Ethereum/Fusaka). The migration is split into two REGULAR proposals — VIP-620 and VIP-621 — that each fit comfortably under the cap, and folds in the May 2026 Prime Rewards Allocation (USDT + U) that was originally bundled in VIP-618.Also adds a per-tx gas-cap guard to
vip-frameworkso future violations fail in CI instead of at execute time on chain.Why split
execute1()(VIP-620)execute2()(VIP-621)The drain alone iterates 6 converters × 47 core-pool tokens; the router allowlist iterates 10 buybacks × 9 routers. Moving both out of
execute1and into a separateexecute2is the smallest split that fits.VIP-620 —
vips/vip-620/bscmainnet-part-1.tsDEFAULT_ADMIN_ROLEon the ACM toMIGRATION_HELPER_V2 = 0x296a3E00c07E306FB26976FdCa201b14933AffAD. Helper renounces it at the end ofexecute1().pendingOwner = helper, so no on-chain transfer is needed for them.)helper.execute1(): accepts all 16 ownerships (10 buybacks + 6 converters), pauses every legacy converter + Shortfall auctions, repoints PSR (18 new rows added, 12 stale zeroed, respectingmaxLoopsLimit=20and the per-schema 1e4 invariant at every checkpoint), grants the cron operator persistentexecuteBuyback+forwardBaseAssetperms on every buyback, renounces ACM admin. Ownership of all 16 contracts is retained by the helper untilexecute2()(VIP-621).Prime.addMarket(coreComptroller, vU, supplyMultiplier=2e18, borrowMultiplier=0)PLP.initializeTokens([U])+setMaxTokensDistributionSpeed([U], [1e18])PLP.sweepToken(USDC, helper, 10,000e18)— ~4k of PLP's ~14.9k USDC is reserved for unclaimed user rewards.helper.executeSwap()— single soft-failing USDC → USDT → U PancakeSwap V3 multihop (direct USDC/U pool is too thin; the deep USDT/U pool is required). Min-out =9,900e18 U(~1% buffer under the9,996.60 UQuoterV2 read at 2026-05-13). USDT leg dropped because PLP already holds ~25k USDT for the May distribution. Output to PLP; leftover USDC forwarded back to NormalTimelock.PLP.setTokensDistributionSpeed([USDT, U], […])at the $12,250/month per-market target.RiskFundV2to the new implementation. Safe becauseRiskFundConverteris paused insideexecute1(), so no in-flightconvertExactTokenscallback can hit the removedupdatePoolStateselector — even though the converter still holds balance until VIP-621.VIP-621 —
vips/vip-620/bscmainnet-part-2.tshelper.execute2(): allowlists 9 swap routers (PancakeSwap V2 / V3 / Smart / Universal, Uniswap V2 SwapRouter02 / V3 SwapRouter02 / V4 / Universal, 1inch v5) on every buyback, drains every non-zero core-pool ERC20 balance from each timelock-owned converter into the corresponding new buyback (RiskFundConverter→RISK_FUND_BUYBACK; 4×*PrimeConverter→U_PRIME_BUYBACK;XVSVaultConverter→XVS_BUYBACK), transfers ownership of all 16 contracts back to NormalTimelock.acceptOwnership()on all 16 contracts (10 buybacks + 6 converters).execute1/executeSwap/execute2) revertAlreadyExecutedon any subsequent call.vip-framework changes (
src/)The original VIP-618 passed simulation but is stuck on chain because Hardhat fork doesn't enforce consensus per-tx caps. This PR closes that gap:
src/networkConfig.ts:PER_TX_GAS_CAP_BY_NETWORKmap (bscmainnet/bsctestnet/ethereum/sepolia→2²⁴; L2s left unset until a concrete protocol per-tx rule lands).src/utils.ts:resolvePerTxGasCap(forkedNetwork)— prefers the EIP-7825eth_txGasLimitCapRPC (forward-compatible with future hardforks) and falls back to the static map; logs the chosen source once per network.src/vip-framework/index.ts:testVipbuildsgovernor.execute(proposalId)viapopulateTransactionand setsgasLimit = capso an over-cap proposal OOGs in sim the way it would on chain.testForkedNetworkVipCommandsapplies the same gasLimit cap on the destination-chainexecutor.execute(proposalId).pretendExecutingVipreports per-command max gas + total gas and warns when any individual command exceeds the cap.On-chain pre-conditions (verified at
bscmainnetblock98045598)pendingOwner = 0x296a3E00c07E306FB26976FdCa201b14933AffAD✓References
execute1/executeSwap/execute2split)Test plan
npx hardhat test simulations/vip-620/bscmainnet-part-1.ts --fork bscmainnetnpx hardhat test simulations/vip-620/bscmainnet-part-2.ts --fork bscmainnet