-
Notifications
You must be signed in to change notification settings - Fork 479
fix(incentives): align hook inputs with V3 backend + preserve data shapes across refetch #2959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mgrabina
wants to merge
11
commits into
main
Choose a base branch
from
SDK-779-interface-incentives
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
c28d657
feat(incentives): centralize all reward sources via aave-v3-backend
mgrabina 8bcafb0
fix(incentives): align hook inputs + harden react-query data shapes
mgrabina 9e5ff90
fix(incentives): address codex review feedback
mgrabina 5ffad30
Merge branch 'main' into SDK-779-interface-incentives
mgrabina 2b35623
fix: payout token not displayed
AGMASO 2eed8cf
fix: merit payouttoken not displayed and customMessages not displayed
AGMASO 65c8d2d
fix: poitns campaigns not displaying points and messages
AGMASO 6bb1cbb
fix: gho displaying wrong apys in for different protocol actions
AGMASO b308608
fix: missing symbol token mapping
AGMASO 202a8e9
fix(incentives): consume Ethena/EtherFi/Sonic as SupplyPointsIncentive
mgrabina f1f0d1d
fix(incentives): rename Merkl extraApy/discountApy to *Apr to match v…
mgrabina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,3 +51,4 @@ package-lock.json | |
|
|
||
| # Sentry Config File | ||
| .env.sentry-build-plugin | ||
| tsconfig.tsbuildinfo | ||
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /** | ||
| * Per-market Merit APR lookup for the net-APY calculation in | ||
| * `useUserYield`. | ||
| * | ||
| * Reads directly from the SDK's `markets()` query (same react-query cache | ||
| * as `useAppDataProvider`'s `useMarketsData`), extracts each reserve's | ||
| * active `MeritSupply/Borrow/Conditional` incentive, and keys by | ||
| * underlying address. The backend already evaluates `userEligible` when | ||
| * the user address is passed, so we only credit APR for reserves the user | ||
| * is actually eligible for — same behaviour as the legacy aavechan | ||
| * per-user fetch. | ||
| * | ||
| * No new GraphQL query: the shared cache means calling this hook | ||
| * alongside the main AppDataProvider fetch is a cache hit. | ||
| */ | ||
| import { chainId as sdkChainId, evmAddress, OrderDirection } from '@aave/client'; | ||
| import { markets } from '@aave/client/actions'; | ||
| import { useQueries } from '@tanstack/react-query'; | ||
| import { client } from 'pages/_app.page'; | ||
| import { MarketDataType } from 'src/ui-config/marketsConfig'; | ||
| import { queryKeysFactory } from 'src/ui-config/queries'; | ||
|
|
||
| /** | ||
| * Map of `lowercase(underlyingAddress) -> {supplyApr, borrowApr}`. Backed | ||
| * by a plain Record because react-query's default `structuralSharing` | ||
| * deep-merges fetched data against the previous value, and `Map` instances | ||
| * don't round-trip through that merge — they come back as plain objects on | ||
| * refetch and `.get()` blows up at the consumer. | ||
| */ | ||
| export type MeritAprByUnderlying = Record<string, { supplyApr: number; borrowApr: number }>; | ||
|
|
||
| const EMPTY_MAP: MeritAprByUnderlying = Object.freeze({}); | ||
|
|
||
| type Incentive = { | ||
| __typename?: string; | ||
| userEligible?: boolean | null; | ||
| extraSupplyApr?: { formatted: string } | null; | ||
| borrowAprDiscount?: { formatted: string } | null; | ||
| extraApr?: { formatted: string } | null; | ||
| }; | ||
|
|
||
| const parseApr = (value?: { formatted: string } | null): number => { | ||
| if (!value) return 0; | ||
| const n = parseFloat(value.formatted); | ||
| return Number.isFinite(n) && n > 0 ? n : 0; | ||
| }; | ||
|
|
||
| /** | ||
| * Per-market query that resolves the SDK's `markets()` response and builds | ||
| * a `Map<underlyingAddress, {supplyApr, borrowApr}>` of eligible Merit | ||
| * APRs for the user. Entries are only present when the user passes the | ||
| * backend's eligibility criteria for that reserve; missing keys mean "no | ||
| * Merit contribution for this position". | ||
| */ | ||
| export const usePoolsMerits = ( | ||
| marketsData: MarketDataType[], | ||
| userAddress?: string | null, | ||
| ) => { | ||
| const userAddr = userAddress ? evmAddress(userAddress) : undefined; | ||
|
|
||
| return useQueries({ | ||
| queries: marketsData.map((marketData) => ({ | ||
| queryKey: [ | ||
| ...queryKeysFactory.market(marketData), | ||
| ...queryKeysFactory.user(userAddr ?? 'anonymous'), | ||
| ], | ||
| enabled: !!client, | ||
| queryFn: async (): Promise<MeritAprByUnderlying> => { | ||
| const response = await markets(client, { | ||
| chainIds: [sdkChainId(marketData.chainId)], | ||
| user: userAddr, | ||
| suppliesOrderBy: { tokenName: OrderDirection.Asc }, | ||
| borrowsOrderBy: { tokenName: OrderDirection.Asc }, | ||
| }); | ||
| if (response.isErr()) throw response.error; | ||
|
|
||
| const result: MeritAprByUnderlying = {}; | ||
| for (const sdkMarket of response.value) { | ||
| const allReserves = [ | ||
| ...(sdkMarket.supplyReserves ?? []), | ||
| ...(sdkMarket.borrowReserves ?? []), | ||
| ]; | ||
| for (const r of allReserves) { | ||
| const underlying = r.underlyingToken.address.toLowerCase(); | ||
| const existing = result[underlying] ?? { supplyApr: 0, borrowApr: 0 }; | ||
| const incentives: Incentive[] = (r.incentives ?? []) as Incentive[]; | ||
| for (const inc of incentives) { | ||
| if (!inc.userEligible) continue; | ||
| if (inc.__typename === 'MeritSupplyIncentive') { | ||
| existing.supplyApr += parseApr(inc.extraSupplyApr); | ||
| } else if (inc.__typename === 'MeritBorrowIncentive') { | ||
| existing.borrowApr += parseApr(inc.borrowAprDiscount); | ||
| } else if (inc.__typename === 'MeritBorrowAndSupplyIncentiveCondition') { | ||
| // Conditional reward: paid to both sides when the user | ||
| // holds the specified collateral + debt simultaneously. | ||
| const apr = parseApr(inc.extraApr); | ||
| existing.supplyApr += apr; | ||
| existing.borrowApr += apr; | ||
| } | ||
| } | ||
| result[underlying] = existing; | ||
| } | ||
| } | ||
| return result; | ||
| }, | ||
| })), | ||
| }); | ||
| }; | ||
|
|
||
| export const emptyMeritMap = (): MeritAprByUnderlying => EMPTY_MAP; | ||
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.