Skip to content

TokenBuybackMigrationHelperV2#164

Closed
Debugger022 wants to merge 26 commits into
developfrom
feat/VPD-1167
Closed

TokenBuybackMigrationHelperV2#164
Debugger022 wants to merge 26 commits into
developfrom
feat/VPD-1167

Conversation

@Debugger022
Copy link
Copy Markdown
Contributor

@Debugger022 Debugger022 commented May 13, 2026

Summary

VIP-618 is unexecutable: single-tx execute() needs ~17.5M gas, BSC Osaka hardfork enforces a per-tx cap of 16,777,216 (2^24). Split into three one-shot entrypoints driven by the wrapping VIP(s).

  • execute1() — accept ownership of 16 contracts (10 buybacks + 6 timelock-owned converters), self-grant transient ACM perms, pause 6 converters + Shortfall auctions, rewire PSR (7-phase, respects maxLoopsLimit and preserves per-schema sum invariant at every checkpoint), revoke transient ACM perms, grant cron operator executeBuyback + forwardBaseAsset perms on all 10 buybacks, renounce DEFAULT_ADMIN_ROLE.
  • executeSwap() — May 2026 Prime allocation swap leg only. VIP feeds USDC to helper via PLP.sweepToken(USDC, helper, …) first; helper approves PCS V3 router, swaps USDC -> USDT (single-hop) and USDC -> USDT -> U (multihop, direct USDC/U pool too thin), forwards leftover USDC back to NormalTimelock. Both legs soft-fail with StepFailed so thin-pool slippage cannot brick the broader VIP.
  • execute2() — allowlist 9 routers × 10 buybacks, drain 6 converters into replacement buybacks (sweepToken is onlyOwner, not pause-gated), hand back ownership of all 16 contracts to NormalTimelock.

Other Prime allocation actions (Prime.addMarket(vU), PLP.initializeTokens([U]), PLP.setMaxTokensDistributionSpeed, PLP.setTokensDistributionSpeed) move out of the helper and run as direct NormalTimelock commands in the wrapping VIP — they don't justify on-chain glue.

Helper retains converter ownership across the gap. Safe: sweepToken is onlyOwner + not pause-gated; helper holds zero ACM rights between VIPs; converters paused and PSR no longer routes to them.

Flags executed1 / executedSwap / executed2 + Execute1NotRun guard prevent out-of-order or repeat calls.

Buyback proxies use the PR #162 redeploy.

Test plan

  • npx hardhat compile — clean
  • npx solhint contracts/helpers/TokenBuybackMigrationHelper.sol — 0 errors
  • slither — 0 findings
  • FORK=true FORKED_NETWORK=bscmainnet npx hardhat test tests/fork/TokenBuybackMigrationHelper.ts
  • Post-state assertions: executed1/executedSwap/executed2 flags true, ownership returned on all 16, converters paused, Shortfall auctionsPaused, zero residual on every converter for every drained token, recipient buybacks credited with drained balances, PLP credited with >= USDT_MIN_OUT USDT and >= U_MIN_OUT U, helper holds zero USDC after executeSwap, USDC leftover refunded to NormalTimelock, operator has executeBuyback + forwardBaseAsset perms on every buyback, every router allowlisted on every buyback, PSR rows correct + per-schema sum == 1e4, helper holds no DEFAULT_ADMIN_ROLE.
  • Guards: NotTimelock on all three entrypoints, AlreadyExecuted on repeat, Execute1NotRun on out-of-order.

Wrapping-VIP recipe

  • VIP-A: timelock transferOwnership(helper) on the 6 timelock-owned converters (buyback pendingOwner is already set to helper by the deploy script) → grantRole(DEFAULT_ADMIN_ROLE, helper)helper.execute1().
  • VIP-B (queued after VIP-A executes): Prime block direct commands — Prime.addMarket(vU, …), PLP.initializeTokens([U]), PLP.setMaxTokensDistributionSpeed([U], …), PLP.sweepToken(USDC, helper, USDC_TO_SWEEP), helper.executeSwap(), PLP.setTokensDistributionSpeed([USDT, U], …), then helper.execute2()acceptOwnership on all 16 contracts.

Debugger022 and others added 21 commits May 7, 2026 19:33
One-shot helper executed by NormalTimelock to atomically replace the
5-contract Token Converter system with 10 TokenBuyback contracts. Claims
ownership of 16 contracts, drains legacy converters into buybacks,
allowlists routers, grants operator permissions, pauses converters,
rewires PSR distributions, then hands ownership back and renounces ACM
admin. Includes fork test covering the full migration flow.
- Fix invalid EIP-55 checksum on UNIV4_SWAP_ROUTER and UNI_UNIVERSAL_ROUTER
  (compile error blocked Deploy/Docgen/test CI jobs).
- Move _coreTokens() pure helper after non-pure internals to satisfy
  solhint 'ordering' rule (Lint CI job).
Three inline interfaces (IAccessControlManagerV8, IProtocolShareReserve,
IAbstractTokenConverter) duplicated names already declared elsewhere
in the repo / @venusprotocol/governance-contracts, breaking smock's
contract-name lookup and failing 9 pre-existing tests in their
'before each' hook with 'unable to generate smock spec from contract name'.

Renamed to migration-scoped names: IACMForMigration, IPsrForMigration,
ITokenConverterForMigration. Function selectors unchanged.
Suite hits BSC mainnet token addresses in its before-all hook
(balanceOf on USDT/USDC etc.); without an actual fork, the call
reverts with empty data. Wrap the describe in the standard Venus
'FORK === "true" && FORKED_NETWORK === bscmainnet' guard so the
suite is skipped during normal 'hardhat coverage' runs and only
executes when the archive node is configured.
Refactor the gate to follow the existing Venus fork-test pattern
(see tests/fork/RiskFundConverter.ts, SingleTokenConverter.ts):
wrap the suite in forking(FORK_BLOCK, () => { ... }) and gate the
inner describe with FORK_MAINNET. Replace the local impersonate()
helper with initMainnetUser from tests/utils so impersonation logic
stays in one place.
One-shot helper for the Token Converter -> TokenBuyback migration
has hardcoded BSC mainnet addresses, so the script restricts
deployment to bscmainnet (and hardhat for fork tests). No proxy or
ownership transfer needed; the wrapping VIP grants privileges and
invokes execute().
Bundling 18 new buyback rows + 12 zeroed stale rows in a single
addOrUpdateDistributionConfigs call grew distributionTargets to 30,
breaching PSR's maxLoopsLimit (mainnet: 20). Split _rewireProtocolShareReserve
into seven phases (A-G) that interleave addOrUpdateDistributionConfigs and
removeDistributionConfig so the array length stays <= 20 at every checkpoint
while preserving the per-schema sum invariant (1e4 or 0) at each add boundary.
Final length: 18.

Update the fork test scaffold: pin FORK_BLOCK to the helper deploy block
(97105830), replace placeholder zero addresses with the helper's hardcoded
mainnet buyback + OPERATOR addresses, and switch the operator-permission
assertion to isAllowedToCall via an impersonated buyback signer (matches
the on-chain msg.sender encoding ACM uses).
BSC Osaka hardfork enforces per-tx gas cap of 16,777,216 (2^24). The
single-tx execute() needed ~17.5M and is unexecutable as queued. Split
into two one-shot entrypoints driven by separate VIPs:

- execute1: accept all 16 ownerships, allowlist routers, grant operator
  perms, pause converters, rewire PSR, revoke transient ACM perms, hand
  back 10 buybacks, renounce DEFAULT_ADMIN_ROLE (~4.26M gas).
- execute2: drain 6 converters into replacement buybacks, hand back 6
  converters (~8.71M gas). sweepToken is onlyOwner and not pause-gated,
  so the helper retains converter ownership across the gap while
  holding zero ACM rights.

Fork test drives the split flow end-to-end, asserts the new flags and
Execute1NotRun guard, and logs per-tx gasUsed vs the Osaka cap.
Resolved conflicts in deployments/bscmainnet.json and
deployments/bscmainnet_addresses.json by taking develop's new buyback
entries (UPrimeBuyback + related) and re-adding TokenBuybackMigrationHelper
pointing at the new redeployed address 0xa30fcE7A72aD101f6afd4D8b89D1AD8687f51cb0.
…ep 3

Update 10 TokenBuyback proxy addresses to PR #162 redeploy. Move
Shortfall.pauseAuctions out of _runPrimeAllocation into _pauseRevenueFlows
so revenue-flow pause is atomic with converter pause (single step 3 rather
than split across step 3 and step 5). Rewrite trust-model docstring to
reflect deploy scripts setting pendingOwner = helper directly, dropping
the timelock-hop preparatory acceptOwnership step.
Hardhat 2.16 lacks reliable Cancun hardfork support; 2.22 ships it as
default. Pin hardfork: 'cancun' on both Hardhat network configs and
flip solc 'evmVersion' from 'paris' to 'cancun' so emitted bytecode
matches BSC mainnet's post-Lorentz EVM and benefits from MCOPY for
memory copies. Required by the new TokenBuybackMigrationHelper which
relies on Cancun-era opcodes on a Cancun-target fork.
VIP now drives PLP/Prime setters directly (addMarket, initializeTokens,
setMax/TokensDistributionSpeed) — helper kept only the PCS V3 USDC ->
{USDT, U} swap legs behind a new 'executeSwap()' one-shot, since the
soft-fail try/catch + leftover-refund still warrants on-chain glue.
Drops unused IPrime/IPLP interfaces + ACM grants, tightens deadline to
1h, consolidates V3 fee constants, DRYs buyback/converter tables.
Add the PLP USDC handoff + executeSwap call between execute1 and execute2,
update buyback addresses to the PR #162 redeploy, pin FORK_BLOCK past it,
and add asserts for: executedSwap flag, USDT/U landing in PLP above
MIN_OUT, USDC leftover refunded to timelock, and Shortfall.auctionsPaused
(now set in execute1's _pauseRevenueFlows, not the swap block).
@Debugger022 Debugger022 marked this pull request as ready for review May 13, 2026 10:48
/// depth is too thin for 7,493 USDC; USDT/U pool is deep enough).
uint256 private constant USDC_PER_LEG = 7493e18;
uint256 private constant USDT_MIN_OUT = 7418e18;
uint256 private constant U_MIN_OUT = 7418e18;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i checked, so among roughly 14k usdc on PrimeLiquidityProvider

  • 4k actually belongs to the user (just they don't claim yet)
  • so we have 10k usdc left

lets convert 10k to U, for USDT, it's already more than enough

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugger022 and others added 3 commits May 13, 2026 16:31
PLP already holds ~25k USDT, more than enough for the May 2026
distribution, and ~4k of PLP's 14.9k USDC is reserved for unclaimed
user rewards. Sweep only 10k USDC into the helper and run a single
USDC -> USDT -> U multihop. Drops _trySwap and the ExactInputSingleParams
struct, retunes USDC_TO_SWAP / U_MIN_OUT against a fresh QuoterV2 read
(9,996.60 U quote, ~1% buffer at 9,900e18). Fork test mirrors the new
budget and asserts PLP USDT balance is unchanged post-swap.
@fred-venus fred-venus marked this pull request as draft May 13, 2026 11:30
@fred-venus fred-venus changed the title Feat/vpd 1167 TokenBuybackMigrationHelperV2 May 13, 2026
@github-actions
Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Health
Interfaces 100% 100%
ProtocolReserve 95% 78%
Test 100% 100%
Test.Mocks 65% 44%
TokenConverter 89% 74%
Utils 100% 100%
helpers 0% 0%
Summary 70% (694 / 992) 69% (241 / 350)

Copy link
Copy Markdown
Contributor

@fred-venus fred-venus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants