feat: rfox v3#11668
Conversation
📝 WalkthroughWalkthroughRemoved Fox/RFOX feature flags and standalone Fox/RFOX pages; unified Fox navigation to /fox-ecosystem; replaced Rune-based affiliate revenue queries with a USD-based affiliate revenue endpoint and adapted reward hooks, helpers, and displays. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI Component
participant Hook as useAffiliateRevenueUsdQuery
participant Query as ReactQuery
participant Axios as Axios HTTP
participant API as Affiliate Revenue API
UI->>Hook: call useAffiliateRevenueUsdQuery(start,end)
Hook->>Query: build queryKey & queryFn
Query->>Axios: execute GET to VITE_AFFILIATE_REVENUE_URL?start=YYYY-MM-DD&end=YYYY-MM-DD
Axios->>API: HTTP request
API-->>Axios: { totalUsd }
Axios-->>Query: response { totalUsd }
Query-->>Hook: resolved affiliateRevenueUsd
Hook-->>UI: returns affiliateRevenueUsd (number)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/pages/RFOX/components/Stake/hooks/useRfoxStake.tsx (1)
192-199: Removeerrors?.manualRuneAddresschecks—the field is never used in the form.The
manualRuneAddressfield is defined inAddressSelectionValuestype but is never registered, set, or validated in the form. It only appears indefaultFormValuescomposition through the type definition, but is never actually initialized or populated. This meanserrors?.manualRuneAddresswill always beundefined, making the validation checks at lines 192 and 315 dead code that can be safely removed.src/pages/RFOX/hooks/useCurrentEpochRewardsQuery.ts (1)
43-103: Fix USD↔user‑currency mismatch in USDC conversion.
affiliateRevenueUsdis explicitly USD, butselectMarketDataByAssetIdUserCurrencyreturns a user‑currency price. Dividing USD by a non‑USD price inflates/deflates rewards for non‑USD users. Use a USD‑denominated USDC price or convert the USD total into user currency before dividing. If the endpoint already represents USDC‑denominated USD totals, you can skip the price conversion and go straight to base units.🐛 Suggested fix (treat USD as USDC 1:1; or swap in USD‑priced market data)
- const usdcMarketData = useAppSelector(state => - selectMarketDataByAssetIdUserCurrency(state, usdcOnArbitrumOneAssetId), - ) - const combine = useCallback( ( queries: [ UseQueryResult<Epoch[], Error>, UseQueryResult<bigint, Error>, UseQueryResult<number, Error>, @@ - if ( - !epochHistory || - !currentEpochRewardUnits || - !affiliateRevenueUsd || - !currentEpochMetadata || - !usdcAsset || - !usdcMarketData - ) + if ( + !epochHistory || + !currentEpochRewardUnits || + !affiliateRevenueUsd || + !currentEpochMetadata || + !usdcAsset + ) return 0n @@ - const affiliateRevenueUsdcBaseUnit = toBaseUnit( - bn(affiliateRevenueUsd).div(usdcMarketData.price), - usdcAsset.precision, - ) + const affiliateRevenueUsdcBaseUnit = toBaseUnit( + bn(affiliateRevenueUsd), + usdcAsset.precision, + ) @@ - [currentEpochMetadata, stakingAssetId, stakingAssetAccountId, usdcAsset, usdcMarketData], + [currentEpochMetadata, stakingAssetId, stakingAssetAccountId, usdcAsset], )
🤖 Fix all issues with AI agents
In `@src/pages/RFOX/hooks/useAffiliateRevenueUsdQuery.ts`:
- Around line 44-49: The try/catch is incorrectly placed around the simple
property access (data.totalUsd) so it never catches network/parse errors; move
the try/catch to wrap the axios.get call and the response parsing in
useAffiliateRevenueUsdQuery (i.e., the code that calls axios.get and assigns
response.data), catch and log the actual error (include the error object) and
then throw a descriptive Error('Error parsing affiliate revenue') so failures
during the HTTP request or JSON parsing are handled correctly.
🧹 Nitpick comments (7)
src/pages/RFOX/types.ts (1)
53-60: Consider type aliases for USD strings and revenue maps.Using a descriptive alias can improve clarity and reduce accidental misuse across the file (and related types).
♻️ Suggested refactor
+export type UsdAmount = string +export type RevenueByService = Record<string, UsdAmount> + export type Epoch = { /** The epoch number for this epoch */ number: number @@ - /** The total revenue (USD) earned this epoch */ - totalRevenue: string + /** The total revenue (USD) earned this epoch */ + totalRevenue: UsdAmount @@ - /** The revenue (USD) earned (by service) for this epoch */ - revenue: Record<string, string> + /** The revenue (USD) earned (by service) for this epoch */ + revenue: RevenueByService @@ - /** The spot price of reward asset in USD */ - rewardAssetPriceUsd: string + /** The spot price of reward asset in USD */ + rewardAssetPriceUsd: UsdAmount } @@ export type EpochDetails = { @@ - /** The spot price of asset in USD */ - assetPriceUsd: string + /** The spot price of asset in USD */ + assetPriceUsd: UsdAmountsrc/pages/Fox/components/RFOXSimulator.tsx (3)
65-71: Unused dependencies inestimatedBurnuseMemo.
stakingAssetUsdPriceandstakingAssetare in the dependency array but are not used in the calculation - the burn is now a simple percentage ofshapeShiftRevenueusingepochMetadata.burnRate. These guards can be removed.♻️ Suggested simplification
const estimatedBurn = useMemo(() => { if (!epochMetadata) return - if (!stakingAsset) return - if (!stakingAssetUsdPrice) return return bnOrZero(shapeShiftRevenue).times(epochMetadata.burnRate).toFixed(2) - }, [epochMetadata, shapeShiftRevenue, stakingAssetUsdPrice, stakingAsset]) + }, [epochMetadata, shapeShiftRevenue])
73-82: Unused dependencyusdcUsdPriceinestimatedRewardsuseMemo.The
usdcUsdPricevariable is checked but not used in the calculation. Since rewards are now displayed as fiat viaAmount.Fiat, and the calculation yields a USD value directly from revenue and distribution rate, this guard appears unnecessary.♻️ Suggested simplification
const estimatedRewards = useMemo(() => { if (!epochMetadata) return if (!poolShare) return - if (!usdcUsdPrice) return const distributionRate = epochMetadata.distributionRateByStakingContract[getStakingContract(stakingAssetId)] ?? 0 return bnOrZero(shapeShiftRevenue).times(distributionRate).times(poolShare).toFixed(2) - }, [epochMetadata, shapeShiftRevenue, usdcUsdPrice, stakingAssetId, poolShare]) + }, [epochMetadata, shapeShiftRevenue, stakingAssetId, poolShare])
140-140: RedundantBoolean()wrapper.
estimatedBurn !== undefinedalready evaluates to a boolean; theBoolean()wrapper is unnecessary.♻️ Suggested fix
- <Skeleton isLoaded={Boolean(estimatedBurn !== undefined)}> + <Skeleton isLoaded={estimatedBurn !== undefined}>src/pages/RFOX/components/Claim/ClaimSelect.tsx (1)
21-24: Remove unusedsetStepIndexfromNoClaimsAvailablePropstype.The
setStepIndexprop is defined in the type but never used in the component (line 26 only destructuresisError).♻️ Suggested cleanup
type NoClaimsAvailableProps = { isError?: boolean - setStepIndex?: (index: number) => void }src/pages/RFOX/components/Overview/EmissionsPool.tsx (1)
26-29: Unnecessary generic type parameter.The
<string>generic type parameter appears unused since noselectfunction is provided. The hook will returnnumber(the defaultTotalRevenueUsdtype), and the calculation on line 40 usesbn()which accepts numbers.Consider removing the generic:
♻️ Suggested change
- const affiliateRevenueUsdQuery = useAffiliateRevenueUsdQuery<string>({ + const affiliateRevenueUsdQuery = useAffiliateRevenueUsdQuery({ startTimestamp: currentEpochMetadataQuery.data?.epochStartTimestamp, endTimestamp: currentEpochMetadataQuery.data?.epochEndTimestamp, })src/pages/RFOX/hooks/useLifetimeRewardsQuery.ts (1)
54-65: Consider extracting the epoch threshold as a named constant.The magic number
17representing the rFOX v3 transition epoch could be more self-documenting as a constant.♻️ Suggested refactor
+const RFOX_V3_FIRST_EPOCH = 18 + const epochRewardUserCurrency = (() => { // rFOX v3 updated rewards from rune to usdc - if (epoch.number > 17) { + if (epoch.number >= RFOX_V3_FIRST_EPOCH) { return bn(fromBaseUnit(distribution.amount, usdcAsset?.precision)).times( usdcMarketData.price, ) }
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/pages/RFOX/hooks/useAffiliateRevenueUsdQuery.ts`:
- Line 41: The constructed URL in useAffiliateRevenueUsdQuery (`const url`) is
missing a forward slash between `baseUrl` and the path; update the URL
construction in useAffiliateRevenueUsdQuery so it inserts a slash (i.e., use
`baseUrl` + "/" + path or template
`${baseUrl}/api/v1/affiliate/revenue?startDate=...`) ensuring `url` is
well-formed when env `baseUrl` has no trailing slash.
0d8fe62 to
8eccad9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/pages/RFOX/hooks/useAffiliateRevenueUsdQuery.ts`:
- Around line 32-33: The current guard in useAffiliateRevenueUsdQuery that
returns skipToken for falsy startTimestamp or endTimestamp incorrectly treats 0
(epoch) as missing; update the check in the function (the condition referencing
startTimestamp, endTimestamp and skipToken) to use nullish checks (e.g.,
startTimestamp == null || endTimestamp == null) or explicit ===
null/===undefined so only null/undefined cause the query to be skipped while
allowing 0 as a valid timestamp.
♻️ Duplicate comments (1)
src/pages/RFOX/hooks/useAffiliateRevenueUsdQuery.ts (1)
34-45: Add explicit error handling for the axios call.Right now any network/parsing errors bubble without context. Wrapping the request lets you log and surface a more descriptive error.
♻️ Suggested adjustment
return async () => { const baseUrl = getConfig().VITE_AFFILIATE_REVENUE_URL @@ - const url = `${baseUrl}/api/v1/affiliate/revenue?startDate=${startDate}&endDate=${endDate}` - const { data } = await axios.get<{ totalUsd: number }>(url) - - return data.totalUsd + const url = `${baseUrl}/api/v1/affiliate/revenue?startDate=${startDate}&endDate=${endDate}` + try { + const { data } = await axios.get<{ totalUsd: number }>(url) + return data.totalUsd + } catch (error) { + console.error('Error fetching affiliate revenue:', error) + throw new Error('Failed to fetch affiliate revenue') + } }#!/bin/bash # Check existing axios + React Query patterns in hooks for consistency rg -n "useQuery\\(" src/pages -g'*.ts*' -C2 rg -n "axios\\.get" src/pages -g'*.ts*' -C2As per coding guidelines, wrap async operations with meaningful error handling.
🧹 Nitpick comments (2)
src/pages/RFOX/hooks/useAffiliateRevenueUsdQuery.ts (2)
37-37: Remove the inline comment in TS hooks.The current guidance is to avoid adding code comments unless explicitly requested.
♻️ Proposed cleanup
- // Convert timestamps (in milliseconds) to YYYY-MM-DD format const startDate = new Date(startTimestamp).toISOString().split('T')[0]As per coding guidelines, avoid adding comments in TS/TSX unless requested.
28-31: Add explicit return types for exported functions.Both
getAffiliateRevenueUsdQueryFnanduseAffiliateRevenueUsdQueryshould declare return types explicitly for TS clarity.♻️ Suggested typing
-import { skipToken, useQuery } from '@tanstack/react-query' +import { skipToken, useQuery, type UseQueryResult } from '@tanstack/react-query' @@ -export const getAffiliateRevenueUsdQueryFn = ({ +export const getAffiliateRevenueUsdQueryFn = ({ startTimestamp, endTimestamp, -}: UseAffiliateRevenueUsdQueryProps) => { +}: UseAffiliateRevenueUsdQueryProps): typeof skipToken | (() => Promise<TotalRevenueUsd>) => { @@ -export const useAffiliateRevenueUsdQuery = <SelectData = TotalRevenueUsd>({ +export const useAffiliateRevenueUsdQuery = <SelectData = TotalRevenueUsd>({ startTimestamp, endTimestamp, select, -}: UseAffiliateRevenueUsdQueryProps<SelectData>) => { +}: UseAffiliateRevenueUsdQueryProps<SelectData>): UseQueryResult<SelectData, Error> => {As per coding guidelines, always use explicit return types in TypeScript.
Also applies to: 48-52
Description
Update rfox to v3 showing rewards in usdc and using new affiliate revenue endpoint.
Issue (if applicable)
closes #11632
closes #11669
closes https://linear.app/shapeshift-dao/issue/PROD-99/metadata-clarifying
Risk
Low
Testing
Engineering
☝️
Operations
☝️
Screenshots (if applicable)
Summary by CodeRabbit
New Features
Changes
✏️ Tip: You can customize this high-level summary in your review settings.