Skip to content

feat(prime): add PrimeV2 and PrimeLeaderboard contracts#670

Open
Debugger022 wants to merge 99 commits into
developfrom
feat/prime-v2
Open

feat(prime): add PrimeV2 and PrimeLeaderboard contracts#670
Debugger022 wants to merge 99 commits into
developfrom
feat/prime-v2

Conversation

@Debugger022

Copy link
Copy Markdown
Contributor

Summary

  • Introduces PrimeV2, a leaderboard-based Prime token replacing epoch/round logic with continuous time-weighted XVS staking scores
  • Introduces PrimeLeaderboard, which tracks per-deposit LIFO timestamps and calculates effective stake for off-chain ranking; admin calls issue()/burn() based on ranked results
  • Adds batch operations (issueBatch, burnBatch), market lifecycle management (addMarket/removeMarket with active-member guard), tier-based compaction, and full fork test coverage against BSC mainnet

Changes

Contracts

  • PrimeV2.sol — new 1102-line core contract: score-based interest accrual, market add/remove, single issue() + issueBatch(), burnBatch(), initializeStakers, finalizeInitialization, maxLoops guards, reentrancy protection
  • PrimeLeaderboard.sol — new 587-line contract: time-weighted scoring, multiplier tiers (30/60/90-day), deposit compaction, xvsUpdated callback, getEffectiveStakeBatch()
  • PrimeV2Storage.sol / PrimeLeaderboardStorage.sol — storage layouts
  • IPrimeV2.sol / IPrimeLeaderboard.sol — interfaces
  • XVSVaultStorage.sol — remove unused field (revert of earlier XVSVault integration)

Deploy

  • 014-deploy-prime-v2.ts / 015-configure-prime-v2.ts — deployment and configuration scripts

Tests

  • 1534-line BSC fork test covering batch ops, market removal, compaction, migration, and interest accrual
  • Unit suites: admin, initialization, interest, markets, scores, integration, compaction, deposits, effectiveStake, scenarios

Test plan

  • npx hardhat test tests/hardhat/PrimeV2/** — all unit suites pass
  • npx hardhat test tests/hardhat/Fork/PrimeV2ForkTest.ts --network bscmainnet — fork tests pass against live BSC state
  • Verify removeMarket reverts when active members exist
  • Verify burnBatch skips non-holders without reverting
  • Verify residual interest claimable after market removal
  • Verify maxLoops guard triggers on oversized batch

- 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)
… tags

Follow Venus protocol convention by adding @Custom:event, @Custom:error,
and @Custom:access tags to all public/external functions across PrimeV2,
PrimeLeaderboard, and PrimeV2Keeper contracts. Also adds missing @notice
NatSpec to PrimeV2Keeper events and errors.
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 fred-venus left a comment

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.

cap removing logic looks good as well

Debugger022 and others added 26 commits May 20, 2026 13:37
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
@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Health
contracts 100% 100%
contracts.Admin 88% 41%
contracts.Comptroller 100% 90%
contracts.Comptroller.Diamond 95% 66%
contracts.Comptroller.Diamond.facets 87% 73%
contracts.Comptroller.Diamond.interfaces 100% 100%
contracts.Comptroller.Types 100% 100%
contracts.Comptroller.legacy 100% 100%
contracts.Comptroller.legacy.Diamond 0% 0%
contracts.Comptroller.legacy.Diamond.facets 0% 0%
contracts.Comptroller.legacy.Diamond.interfaces 100% 100%
contracts.DelegateBorrowers 100% 89%
contracts.FlashLoan.interfaces 100% 100%
contracts.Governance 68% 45%
contracts.InterestRateModels 74% 59%
contracts.Lens 43% 46%
contracts.Liquidator 83% 60%
contracts.Oracle 100% 100%
contracts.PegStability 88% 84%
contracts.Swap 87% 58%
contracts.Swap.interfaces 100% 100%
contracts.Swap.lib 81% 55%
contracts.Tokens 100% 100%
contracts.Tokens.Prime 97% 82%
contracts.Tokens.Prime.Interfaces 100% 100%
contracts.Tokens.Prime.libs 90% 77%
contracts.Tokens.VAI 82% 52%
contracts.Tokens.VRT 20% 9%
contracts.Tokens.VTokens 70% 52%
contracts.Tokens.VTokens.legacy 0% 0%
contracts.Tokens.VTokens.legacy.Utils 0% 0%
contracts.Tokens.XVS 19% 8%
contracts.Tokens.test 100% 100%
contracts.Utils 52% 31%
contracts.VAIVault 50% 45%
contracts.VRTVault 49% 36%
contracts.XVSVault 63% 50%
contracts.external 100% 100%
contracts.lib 89% 71%
Summary 60% (4308 / 7126) 46% (1638 / 3530)

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