feat(prime): add PrimeV2 and PrimeLeaderboard contracts#670
Open
Debugger022 wants to merge 99 commits into
Open
feat(prime): add PrimeV2 and PrimeLeaderboard contracts#670Debugger022 wants to merge 99 commits into
Debugger022 wants to merge 99 commits into
Conversation
- Add IPrimeLeaderboard interface with structs, events, and errors - Add PrimeLeaderboardStorage with LIFO deposit stack structure - Implement PrimeLeaderboard with: - Per-deposit timestamp tracking for LIFO withdrawals - Time-weighted multiplier tiers (1.0x to 2.0x based on deposit age) - Epoch-based processing with off-chain ranking verification - Participant management with 500 XVS minimum stake - Withdrawn score locking until epoch ends
- Test initialization and default multiplier tiers - Test deposit recording and participant management - Test LIFO withdrawal processing - Test effective stake calculation with time-weighted multipliers - Test epoch processing and finalization - Test admin functions (setEpochDuration, setPrimeSlots, etc.) - Test pause functionality - 46 passing tests
…cking - Add IPrimeLeaderboard interface (Solidity 0.5.16 compatible) - Add primeLeaderboard storage variable to XVSVaultStorage - Add setPrimeLeaderboard() setter function - Call recordDeposit() on XVS deposits to prime pool - Call recordWithdrawal() on withdrawal requests from prime pool - Reduce storage gap from 46 to 45 for new variable
…bution PrimeV2 replaces the self-claim model with a leaderboard-driven Prime token system powered by PrimeLeaderboard.
Comprehensive unit tests using smock mocks for all external dependencies (XVSVault, Oracle, PrimeLeaderboard, PrimeLiquidityProvider).
Remove recordDeposit/recordWithdrawal callbacks added in 20adb68. The new approach uses the existing primeToken.xvsUpdated() callback instead — zero vault code changes needed. Governance repoints primeToken to PrimeLeaderboard via setPrimeToken().
1. Remove on-chain epoch processing — replace finalizeEpoch(), revokePrimeStatus(), hasPrime, userRank, primeSlots, epochDuration with simple currentRound + advanceRound(). Admin calls PrimeV2.issue()/burn() directly based on off-chain ranking. 2. Keep XVSVault unchanged — replace recordDeposit/recordWithdrawal with xvsUpdated(user) that reads vault balance via getUserInfo(), diffs against _lastKnownStake, and infers deposit/withdrawal.
Admin now calls issue()/burn() directly based on off-chain leaderboard ranking. No need for pull-based status sync from PrimeLeaderboard.
Replace all epoch-based and recordDeposit/recordWithdrawal tests with xvsUpdated + advanceRound pattern. Use smock FakeContract<IXVSVault> mock instead of Signer for vault interaction. Update effective stake assertions to include hold_duration in formula (amount × multiplier × min(holdDays, 90)). Add tests for advanceRound, setXVSVaultPoolConfig, setXVSVault, getDeposits, getTotalStaked, and ACM access control.
Comprehensive integration test suite covering the full PrimeV2 + PrimeLeaderboard lifecycle with mock contracts: - Full E2E flow: deposit → issue → accrue → claim - LIFO withdrawal mechanics preserving oldest deposits - Time-weighted multiplier tier progression (1.0x → 2.0x) - Round-based withdrawn score tracking and reset - Batch score updates after alpha/multiplier changes - Deposit compaction at 100-deposit limit - Participant threshold boundary behavior - Pause/unpause and admin operations
Validates the full PrimeV2 + PrimeLeaderboard system on a mainnet fork using real XVSVault, Comptroller, Oracle, and PrimeLiquidityProvider.
Keeper helper that batches score updates and accrues interest across all Prime markets.
- Fix inverted condition in _updateRoundAfterTokenBurned - Add partial claim with remainder tracking in _claimInterest - Use SafeCast for uint128/uint64 casts in PrimeLeaderboard - Enforce xvsVault caller check on xvsUpdated - Add getPendingRewardsStatic view function - Remove dead poolRegistry storage and setPoolRegistry - Enforce batchSize in PrimeV2Keeper.processScoreUpdates - Add _ensureMaxLoops in _burn and _initializeMarkets - Emit IncompleteRoundDiscarded on unfinished round overwrite - Validate non-zero multipliers in addMarket
- Remove setPoolRegistry tests (function removed from PrimeV2) - Update xvsUpdated calls to go through xvsVault (M-01 access control) - Flip unrestricted xvsUpdated test to expect OnlyXVSVaultAllowed revert - Fund smock FakeContract wallets with ETH for gas via setBalance - Add xvsVaultSigner via initMainnetUser in fork tests - Remove PrimeV2Integration test suite (will be re-integrated later)
Deploy PrimeV2 and PrimeLeaderboard as upgradeable proxies with multi-network support. Includes a separate configuration script to transfer ownership to timelocks on non-hardhat networks.
…ore from effective stake Withdrawn score no longer inflates getEffectiveStake(), fixing phantom eligibility for users with 0 XVS staked. Backend now controls leaderboard timing via 24h polling and resetWithdrawnScore() setter. Removes advanceRound() in favor of fully backend-driven reward distribution.
Simplify naming now that epoch/round management has been removed. The variable is a simple accumulator reset by backend, so the shorter name better reflects its purpose.
Rewrite Section 6 and Section 8 fork tests to match the new design where withdrawn score is NOT included in effective stake. Replace advanceRound() permissions with resetWithdrawnScore(address) and fix hold-time calculations to account for XVSVault 7-day lock period.
Add 3 deposit compaction tests to PrimeLeaderboard covering the MAX_DEPOSITS_PER_USER trigger, event emission, and stake preservation. Add 11 market management tests to PrimeV2 covering addMarket and updateMultipliers with proper oracle/comptroller/underlying mocks.
Add 25 dedicated unit tests covering all PrimeV2Keeper functions: initialization, processScoreUpdates, accrueAllMarkets, view functions, and admin setters. Add deploy/016 script for proxy deployment.
…NotPaused Account for pending vault withdrawals when computing effective stake, matching old Prime's _xvsBalanceOfUser behavior. Remove whenNotPaused from xvsUpdated to prevent paused leaderboard from blocking vault deposit() and requestWithdrawal() calls.
Move _lastKnownStake write inside change branches to skip SSTORE on no-change path. Use delete for withdrawnScore reset. Remove misleading default value from minimumStake NatSpec.
Remove unused IneligibleToClaim, InvalidLength, InvalidComptroller errors. Remove duplicate UserHasNoPrimeToken check in burn() since _burn() already validates. Fix issue() NatSpec to not say irrevocable.
PrimeLeaderboard never calls _ensureMaxLoops — its loops are already bounded by MAX_DEPOSITS_PER_USER. Removing the unused inheritance eliminates unnecessary storage slots and dead code.
…erboard Merge identical _updateRoundAfterTokenMinted/_Burned into single _updateRoundAfterTokenChanged (also fixes missing isScoreUpdated flag on burn), add zero-check to updateMultipliers, use delete for _participantIndex, rename shadowed local variable, and add NatSpec documenting design decisions for setMinimumStake and primeV2 storage.
…ix _capitalForScore - Score calculation now uses raw seconds instead of dividing by 1 days for finer granularity in effective stake and withdrawal tracking - Remove redundant comptroller param from addMarket, use stored corePoolComptroller directly - Fix _capitalForScore to use EXP_SCALE instead of per-token decimals - Cache storage pointer in updateMultipliers to reduce redundant SLOADs - Add backwards-compat NatSpec to IPrimeV5
…nges - Update all expected values from day-based to seconds-based (×86400) - Add expectApprox helper for second-level timestamp drift tolerance - Fix PrimeLeaderboard deploy args in fork test (remove extra param) - Update addMarket calls to drop removed comptroller argument
Community feedback: 100k cap on XVS stake used in score formula unfairly limits whales' contribution. Removed the cap entirely since PrimeV2 is not yet deployed — score now uses full vault balance. Drops the immutable, constructor param, and helper fn.
fred-venus
approved these changes
May 12, 2026
fred-venus
left a comment
Contributor
There was a problem hiding this comment.
cap removing logic looks good as well
Round the amount-weighted merge timestamp up instead of down so the merged deposit's effective duration rounds down conservatively, removing a directional bias that let users inflate effective stake by adding tiny deposits without advancing the deposit timestamp.
Restores the original advance-baseline behavior in accrueInterest. Rewarding a behavior requires the user to be actively contributing (XVS stake + supported-market position); carrying the slice forward would credit arrival timing rather than participation. The tokens remain in the PrimeLiquidityProvider balance and can be reallocated via sweepToken in a governance VIP if needed. Reverts 61625b1.
When sumOfMembersScore is zero the PLP slice has no recipient and was previously stranded once unreleasedPLPIncome advanced past it. Recording it in undistributedReward and adding an ACM-gated sweepUndistributed lets governance reclaim the funds without touching user-owed balances, addressing Sherlock #10.
…pshot anchor Adds monotonic lifetimeAccrued field on Interest struct, bumped at every accrual site (executeBoost, executeBoostWithoutAccrual, claimInterest) and never decremented by claim. Backend computes per-cycle earnings as diff between two snapshots — no event indexing needed. Counter is per (vToken, user). Survives burn so re-issued users resume from prior total. Storage-safe: appended to Interest struct, __gap unchanged. Adds: - getLifetimeAccruedByMarket(market, users[]) — leaderboard per market - getLifetimeAccruedByUser(user, markets[]) — user dashboard fan-out - recordCycleSnapshot(cycleId) — ACM-gated, event-only keeper hook emitting CycleSnapshotRecorded(cycleId, blockNumber, timestamp). Non-idempotent on-chain; indexer dedupes. Grant to keeper EOA, not Timelock. Gas: accrual sites gated with `if (delta != 0)` to skip SSTOREs on no-op checkpoints (~5k gas saved per skip, compounds in updateScores).
Exercise the deployed PrimeV2 + PrimeLeaderboard testnet proxies against the real XVSVault, Comptroller, oracle and PLP — validating the post-VIP state (wiring, 4 Core markets, compressed 1h/2h/3h tiers, sweep impl). Covers same-timestamp seeding, block-based interest accrual + claim, undistributed-income carry-forward/sweep, keeper cycle reconciliation, borrow-side scoring, unstake-without-auto-burn and access-control guards.
[VPD-1366] Certik Audit Mitigations for PrimeV2
[VPD-1292] PrimeV2 Sherlock Audit
[VPD-1247]: Hashdit Audit Mitigations for PrimeV2
|
4 tasks
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
Changes
Contracts
PrimeV2.sol— new 1102-line core contract: score-based interest accrual, market add/remove, singleissue()+issueBatch(),burnBatch(),initializeStakers,finalizeInitialization, maxLoops guards, reentrancy protectionPrimeLeaderboard.sol— new 587-line contract: time-weighted scoring, multiplier tiers (30/60/90-day), deposit compaction,xvsUpdatedcallback,getEffectiveStakeBatch()PrimeV2Storage.sol/PrimeLeaderboardStorage.sol— storage layoutsIPrimeV2.sol/IPrimeLeaderboard.sol— interfacesXVSVaultStorage.sol— remove unused field (revert of earlier XVSVault integration)Deploy
014-deploy-prime-v2.ts/015-configure-prime-v2.ts— deployment and configuration scriptsTests
Test plan
npx hardhat test tests/hardhat/PrimeV2/**— all unit suites passnpx hardhat test tests/hardhat/Fork/PrimeV2ForkTest.ts --network bscmainnet— fork tests pass against live BSC stateremoveMarketreverts when active members existburnBatchskips non-holders without reverting