Skip to content

feat: support abi & api#5641

Open
cuzz-venus wants to merge 3 commits into
feat/prime-rank-cardfrom
feat/prime-leaderboard-api
Open

feat: support abi & api#5641
cuzz-venus wants to merge 3 commits into
feat/prime-rank-cardfrom
feat/prime-leaderboard-api

Conversation

@cuzz-venus

Copy link
Copy Markdown
Contributor

Jira ticket(s)

VPD-1337

Changes

  • support contract abi
  • support api

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4d8d786

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cuzz-venus cuzz-venus force-pushed the feat/prime-leaderboard-api branch from bf5d385 to 4b14711 Compare June 17, 2026 02:49
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dapp-preview Ready Ready Preview Jun 17, 2026 3:48pm
dapp-testnet Ready Ready Preview Jun 17, 2026 3:48pm
venus.io Ready Ready Preview Jun 17, 2026 3:48pm

Request Review

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds 10 new clients/api query modules (fetch functions + useQuery hooks) to support the Prime Leaderboard V2 feature, covering both REST API endpoints and on-chain reads via two new external contract ABIs (PrimeV2, PrimeLeaderboard).

  • Six REST-backed queries (getPrimeLeaderboard, getPrimeCurrentCycle, getPrimeCycles, getPrimePastCycle, getPrimeMinimumStake, getPrimeUserPendingRewards, getPrimeUserCycleRewards) follow the project's 3-layer pattern with FunctionKey enum entries and VError error handling; most correctly separate the API response type from the domain output type and convert date strings to Date objects.
  • Four on-chain queries (getPrimeEffectiveStake, getPrimeMultiplierTiers, getPrimeOnChainPendingRewards) use callOrThrow, feature-flag guards, and the contract registry for addresses.
  • A local planning file (.claude/prime-leaderboard-api-mapping.local.md) is included in the commit; two user-specific query hooks use an unsafe as Address cast in their query keys; and two fetch functions (getPrimeUserCycleRewards, getPrimeUserPendingRewards) skip the API-response → domain-output normalisation step.

Confidence Score: 4/5

The new query layer is additive and not yet wired into any UI, so the practical blast radius is low on this branch.

Most of the 10 new query modules are well-structured and follow established conventions. The as Address casts in two query keys and the missing response-normalisation layer in two fetch functions are code-quality issues but don't cause runtime breakage (affected hooks are disabled when accountAddress is undefined). The committed local planning file is the only item that warrants cleanup before merging.

getPrimeUserCycleRewards/index.ts, getPrimeUserCycleRewards/useGetPrimeUserCycleRewards.ts, getPrimeUserPendingRewards/index.ts, getPrimeUserPendingRewards/useGetPrimeUserPendingRewards.ts, and .claude/prime-leaderboard-api-mapping.local.md

Important Files Changed

Filename Overview
.claude/prime-leaderboard-api-mapping.local.md Local developer planning doc with Chinese notes and unresolved TODOs; should not be committed to the repo
apps/evm/src/clients/api/index.ts Barrel exports for all 10 new Prime query modules; follows existing pattern correctly
apps/evm/src/constants/functionKey.ts Adds 10 new FunctionKey enum entries for the new Prime queries; naming is consistent with existing values
apps/evm/src/libs/contracts/config/index.ts Registers PrimeV2 and PrimeLeaderboard contracts for BSC_TESTNET with JSON ABIs; includes TODOs to source from @VenusProtocol packages once published
apps/evm/src/clients/api/queries/getPrimeUserPendingRewards/index.ts Fetch function for /prime/users/:address/pending-rewards; uses output type as response type and returns raw payload, bypassing the normalization layer pattern
apps/evm/src/clients/api/queries/getPrimeUserPendingRewards/useGetPrimeUserPendingRewards.ts Query hook uses unsafe as Address cast for accountAddress in the query key when it can be undefined
apps/evm/src/clients/api/queries/getPrimeUserCycleRewards/index.ts Fetch function for /prime/cycles/:cycleIndex/users/:address; same raw-payload-return issue as getPrimeUserPendingRewards
apps/evm/src/clients/api/queries/getPrimeUserCycleRewards/useGetPrimeUserCycleRewards.ts Query hook uses unsafe as Address cast for accountAddress in the query key

Reviews (1): Last reviewed commit: "feat: support abi & api" | Re-trigger Greptile

Comment on lines +33 to +36
queryKey: [
FunctionKey.GET_PRIME_USER_PENDING_REWARDS,
{ chainId, accountAddress: accountAddress as Address },
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Unsafe as Address cast embeds undefined into the query key

accountAddress is Address | undefined, but the cast to Address makes the query key's TypeScript type claim it is always a valid address. When the hook is disabled (because accountAddress is undefined), the key is { ..., accountAddress: undefined } even though the type says Address. This can cause silent cache-key mismatches if downstream code ever inspects or matches these keys. The same pattern appears in useGetPrimeUserCycleRewards.ts. A safe fallback avoids the assertion entirely.

Suggested change
queryKey: [
FunctionKey.GET_PRIME_USER_PENDING_REWARDS,
{ chainId, accountAddress: accountAddress as Address },
],
queryKey: [
FunctionKey.GET_PRIME_USER_PENDING_REWARDS,
{ chainId, accountAddress: accountAddress ?? ('' as Address) },
],

return useQuery({
queryKey: [
FunctionKey.GET_PRIME_USER_CYCLE_REWARDS,
{ chainId, cycleIndex, accountAddress: accountAddress as Address },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Same unsafe as Address cast in query key

accountAddress can be undefined here too (it is typed Address | undefined in the hook input), yet it is cast to Address in the query key. When the hook is disabled, the serialised key silently contains undefined while the type asserts Address.

Suggested change
{ chainId, cycleIndex, accountAddress: accountAddress as Address },
{ chainId, cycleIndex, accountAddress: accountAddress ?? ('' as Address) },

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for ./apps/evm

Status Category Percentage Covered / Total
🔵 Lines 81.99% 49201 / 60006
🔵 Statements 81.99% 49201 / 60006
🔵 Functions 62.3% 666 / 1069
🔵 Branches 73.01% 5554 / 7607
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
apps/evm/src/clients/api/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeCurrentCycle/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeCurrentCycle/useGetPrimeCurrentCycle.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeCycles/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeCycles/useGetPrimeCycles.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeEffectiveStake/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeEffectiveStake/useGetPrimeEffectiveStake.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeLeaderboard/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeLeaderboard/useGetPrimeLeaderboard.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeMinimumStake/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeMinimumStake/useGetPrimeMinimumStake.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeMultiplierTiers/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeMultiplierTiers/useGetPrimeMultiplierTiers.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeOnChainPendingRewards/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeOnChainPendingRewards/useGetPrimeOnChainPendingRewards.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimePastCycle/index.ts 76.92% 100% 0% 76.92% 62-71
apps/evm/src/clients/api/queries/getPrimePastCycle/useGetPrimePastCycle.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeRewardsLeaderboard/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeRewardsLeaderboard/useGetPrimeRewardsLeaderboard.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeUserCycleRewards/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeUserCycleRewards/useGetPrimeUserCycleRewards.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeUserPendingRewards/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getPrimeUserPendingRewards/useGetPrimeUserPendingRewards.ts 100% 100% 100% 100%
apps/evm/src/constants/functionKey.ts 100% 50% 100% 100%
apps/evm/src/containers/PrimeRank/getRankLabels/index.ts 91.66% 66.66% 100% 91.66% 1
apps/evm/src/containers/PrimeRank/useGetPrimeRank/index.ts 95.74% 75% 100% 95.74% 1, 42
apps/evm/src/libs/contracts/config/index.ts 0% 100% 100% 0% 3-930
apps/evm/src/pages/PrimeLeaderboard/index.tsx 100% 85.71% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/EndOfCycle/index.tsx 93.22% 62.5% 66.66% 93.22% 39-44
apps/evm/src/pages/PrimeLeaderboard/LastCycleSummaryModal/index.tsx 100% 0% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/LastCycleSummaryModal/useGetPrimeLastCycleSummary/index.ts 97.56% 89.47% 100% 97.56% 1
apps/evm/src/pages/PrimeLeaderboard/MarketActions/index.tsx 95.23% 66.66% 33.33% 95.23% 31-32
apps/evm/src/pages/PrimeLeaderboard/RankCard/index.tsx 100% 80% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/RankSection/index.tsx 93.75% 0% 0% 93.75% 19
apps/evm/src/pages/PrimeLeaderboard/RankTable/index.tsx 100% 63.63% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/RewardTable/index.tsx 100% 73.68% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/TotalRewardsCard/index.tsx 100% 66.66% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/TotalRewardsSection/index.tsx 100% 0% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/UserRewardsCard/index.tsx 100% 87.5% 100% 100%
apps/evm/src/pages/PrimeLeaderboard/UserRewardsSection/index.tsx 91.89% 20% 100% 91.89% 27-28, 36
apps/evm/src/pages/PrimeLeaderboard/useGetPrimeTotalRewards/index.ts 95.45% 77.77% 100% 95.45% 1
apps/evm/src/pages/PrimeLeaderboard/useGetPrimeUserRewards/index.ts 94.11% 76.92% 100% 94.11% 1, 33
Generated in workflow #13668 for commit 4d8d786 by the Vitest Coverage Report Action

@cuzz-venus cuzz-venus force-pushed the feat/prime-rank-card branch from 9bfb2b4 to b2440a8 Compare June 17, 2026 09:54
Comment on lines +75 to +87
cycle: payload.cycle
? {
...payload.cycle,
startsAt: new Date(payload.cycle.startsAt),
endsAt: new Date(payload.cycle.endsAt),
}
: null,
pendingPool: payload.pendingPool
? {
...payload.pendingPool,
computedAt: new Date(payload.pendingPool.computedAt),
}
: null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not use null in the frontend repo, but rather undefined. null has many many issues: https://craftbettersoftware.com/p/stop-using-null-its-a-bad-practice


export interface PrimeCurrentCycle {
cycleIndex: number;
status: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be an enum so we can list all the possible statuses.

blockNumber: string;
computedAt: Date;
primeHolderCount: number;
totalPendingUsdCents: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You don't need to add "Usd" into the name (the repo considers that dollars are the the only on-crypto currency used).

Comment on lines +5 to +12
export interface PrimeFinalizedCycle {
cycleIndex: number;
startsAt: Date;
endsAt: Date;
mintLimitUsed: number;
totalRewardPoolUsdCents: string | null;
finalizedAt: Date | null;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The type for defining cycles is defined in multiple places.

I recommend creating a shared type that can live in side the root types file (apps/evm/src/types/index.ts) and that contains all the optional properties to define the various states of a cycle (past, current, etc..).


interface PrimeCurrentCycleResponse {
cycleIndex: number;
status: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same thing here, let's use an enum.

}

export interface GetPrimePastCycleOutput {
cycle: PrimePastCycle | null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No null

};
};

export const getPrimePastCycle = async ({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For simplicity and to follow the same naming logic as getPrimeUserCycleRewards, I'd name this function getPrimeCycle (and propagate the change onto the related types and hook).

getPrimeUserCycleRewards({ chainId, cycleIndex, ...params }),
),
...options,
enabled: (options?.enabled === undefined || options?.enabled) && !!accountAddress,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since we are fetching past data, we don't need to refetch it every 5 minutes (which is what React Query will do by default). You can pass the following options to prevent the query from being refetched this session"

    refetchOnMount: false,
    refetchOnReconnect: false,
    refetchOnWindowFocus: false,
    staleTime: Number.POSITIVE_INFINITY,
    gcTime: Number.POSITIVE_INFINITY,

return useQuery({
queryKey: [FunctionKey.GET_PRIME_PAST_CYCLE, params],
queryFn: () => getPrimePastCycle(params),
...options,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since we are fetching past data, we don't need to refetch it every 5 minutes (which is what React Query will do by default). You can pass the following options to prevent the query from being refetched this session"

    refetchOnMount: false,
    refetchOnReconnect: false,
    refetchOnWindowFocus: false,
    staleTime: Number.POSITIVE_INFINITY,
    gcTime: Number.POSITIVE_INFINITY,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To follow the codebase practice of adding a suffix with the unit, I'd rename this: TOP_500_GAP_THRESHOLD_XVS_TOKENS.

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