Skip to content

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Dec 11, 2025

Description

Implement the referral dashboard:

  • Showing your first code on top
  • Ability to create codes and copy/share them
  • Under a flag to disable it for now
  • Counts your referrals amounts (it wont be working if it's not locally until we deploy properly on AWS, updating railway would be a huge lift right now...)
  • Counts your fees for the current period and total fees regardless of the current period

Issue (if applicable)

No issue

Risk

Low, under a flag and not activated yet

High Risk PRs Require 2 approvals

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Testing

  • Launch the docker containers of the microservices
  • Uncomment the local URLs of microservices in env.development
  • Create a referral code
  • Use the referral code with another accounts
  • Do swaps with the other accounts
  • See that your numbers did increase! Currently it's counting 10% of the fees we take for every swap, but this can change in the future

Engineering

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

image image

Summary by CodeRabbit

  • New Features

    • Added referral program enabling users to create and share referral codes.
    • Referral dashboard with statistics on rewards, referrals, and code management.
    • Automatic referral code capture from URLs for tracking referred users.
  • Bug Fixes

    • Enhanced order metadata in swap transactions with improved data structure.
  • Navigation

    • Added Referral section to header navigation menu.
    • Improved Ramp feature route visibility.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive referral system feature, including environment flags, UI components for code management and rewards tracking, hooks for data fetching and URL-based code capture, API integration with the user service, and translations. Additionally, it enhances swapper metadata to include per-swapper-specific fields and order identifiers.

Changes

Cohort / File(s) Summary
Environment & Configuration
.env, .env.development, src/config.ts, src/state/slices/preferencesSlice/preferencesSlice.ts
Added VITE_FEATURE_REFERRAL environment variable (default false) and integrated it into the feature flags configuration and Redux preferences slice.
Referral Type System
src/lib/referral/types.ts, src/lib/user/types.ts
Introduced ReferralCode, ReferralStats, CreateReferralCodeRequest, CreateReferralCodeResponse types, and custom ReferralApiError error class; added optional referralCode field to GetOrCreateUserRequest.
Referral API Integration
src/lib/referral/api.ts, src/hooks/useReferral/useReferral.tsx
Created referral API client with getReferralStatsByOwner and createReferralCode endpoints; added useReferral hook for data fetching and code creation mutations.
Referral Code Capture
src/hooks/useReferralCapture/useReferralCapture.tsx, src/hooks/useUser/useUser.tsx
Implemented URL-based referral code capture and localStorage persistence; integrated referral code into user creation workflow.
Referral UI Components
src/components/Referral/*, src/components/Referral/index.ts
Added comprehensive referral feature components: ReferralDashboard (main container), ReferralHeader, ReferralCodeCard, ReferralStatsCards, ReferralTabs, ReferralCodesTable, ReferralCodesManagementTable, CreateCodeCard.
Page Integration & Navigation
src/pages/Referral/Referral.tsx, src/pages/Fox/FoxEcosystemPage.tsx, src/components/Layout/Header/Header.tsx, src/Routes/RoutesCommon.tsx
Added Referral page component; integrated ReferralDashboard into FoxEcosystemPage behind feature flag; added referral navigation button to Header; increased Ramp route priority.
App-Level Integration
src/App.tsx
Initialized useReferralCapture hook at app root to capture referral codes from URL on mount and route changes.
Translations
src/assets/translations/en/main.json
Added comprehensive translation keys for referral feature including descriptions, action labels, statuses, and navigation entry.
Swapper Metadata Enhancement
packages/swapper/src/types.ts, packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts, src/lib/tradeExecution.ts
Added orderId field to TradeQuoteStep; added cowswapQuoteSpecific to SwapperSpecificMetadata; augmented Portals trade quote with orderId from order response; propagated swapper-specific metadata fields in trade execution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25–35 minutes

  • Key areas requiring attention:
    • The useReferral hook's React Query integration and error handling, particularly the conditional query execution based on ownerAddress and feature flags
    • The referral code capture mechanism in useReferralCapture and its integration into the App lifecycle; ensure localStorage error handling is robust
    • The swapper metadata additions (orderId, cowswapQuoteSpecific) and their propagation through tradeExecution; verify backward compatibility
    • Integration points where referralCode is passed to the user creation API; ensure validation and error handling
    • Feature flag guarding in Header and FoxEcosystemPage to confirm proper conditional rendering

Possibly related PRs

  • feat: portals cross-chain swaps #11281: Modifies Portals swapper trade-quote metadata for cross-chain data; this PR adds orderId to the same metadata structure.
  • feat: sun.io swapper #11261: Adds optional metadata fields to TradeQuoteStep in packages/swapper/src/types.ts, similar to this PR's orderId and cowswapQuoteSpecific additions.
  • feat: near intents swapper #11016: Modifies swapper metadata and tradeExecution metadata propagation for per-swapper-specific fields, directly related to this PR's swapper enhancements.

Suggested reviewers

  • gomesalexandre
  • premiumjibles
  • 0xApotheosis

Poem

🐰 A shiny referral path appears,
With codes and stats to bring good cheer,
From URL to local store,
Swapper metadata galore!
We hop through features, clean and clear. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: referral dashboard and registering' accurately summarizes the main changes: implementing a referral dashboard feature with code creation/registration functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch referral-dashboard

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@NeOMakinG NeOMakinG marked this pull request as ready for review December 12, 2025 12:43
@NeOMakinG NeOMakinG requested a review from a team as a code owner December 12, 2025 12:43
Comment on lines +182 to +185
cowswapQuoteSpecific: tradeQuote.steps[0]?.cowswapQuoteResponse,
portalsTransactionMetadata: tradeQuote.steps[0]?.portalsTransactionMetadata,
zrxTransactionMetadata: tradeQuote.steps[0]?.zrxTransactionMetadata,
bebopTransactionMetadata: tradeQuote.steps[0]?.bebopTransactionMetadata,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Needed to verify upstream that the user did apply the fees

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/swapper/src/types.ts (1)

444-459: Critical: Missing type definitions for transaction metadata fields.

SwapperSpecificMetadata is missing three fields that are being assigned in src/lib/tradeExecution.ts (lines 183-185):

  • portalsTransactionMetadata
  • zrxTransactionMetadata
  • bebopTransactionMetadata

These fields need to be added to the type definition to match the runtime assignments, or TypeScript compilation will fail.

Apply this diff to add the missing fields:

 export type SwapperSpecificMetadata = {
   chainflipSwapId: number | undefined
   nearIntentsSpecific?: {
     depositAddress: string
     depositMemo?: string
     timeEstimate: number
     deadline: string
   }
   cowswapQuoteSpecific?: OrderQuoteResponse
+  portalsTransactionMetadata?: TradeQuoteStep['portalsTransactionMetadata']
+  zrxTransactionMetadata?: TradeQuoteStep['zrxTransactionMetadata']
+  bebopTransactionMetadata?: TradeQuoteStep['bebopTransactionMetadata']
   relayTransactionMetadata: RelayTransactionMetadata | undefined
   relayerExplorerTxLink: string | undefined
   relayerTxHash: string | undefined
   stepIndex: SupportedTradeQuoteStepIndex
   quoteId: string
   streamingSwapMetadata: StreamingSwapMetadata | undefined
 }
src/config.ts (1)

12-228: Add Referral flag to test mocks

The config validator and FeatureFlags type are correctly wired, but the test mock in src/test/mocks/store.ts is missing the Referral entry in the featureFlags object. Add Referral: false, to the mock (after AppRating at line 181) to match the FeatureFlags type definition and prevent test failures.

♻️ Duplicate comments (1)
src/lib/tradeExecution.ts (1)

182-185: Duplicate: Type definitions needed for these metadata fields.

These assignments reference the critical issue flagged in packages/swapper/src/types.ts at lines 444-459. The portalsTransactionMetadata, zrxTransactionMetadata, and bebopTransactionMetadata fields must be added to the SwapperSpecificMetadata type definition before these assignments will type-check correctly.

🧹 Nitpick comments (11)
src/components/Referral/index.ts (1)

1-1: Barrel export is OK; just confirm it matches local import conventions. Some parts of the repo prefer direct component-file imports over directory barrels. Based on learnings, consider aligning with the dominant pattern if this causes churn later.

src/pages/Fox/FoxEcosystemPage.tsx (1)

11-16: Consider lazy-loading ReferralDashboard behind the feature flag to avoid bundle impact.

 import { Box } from '@chakra-ui/react'
 import { lazy } from 'react'
 import { useTranslate } from 'react-polyglot'
@@
 import { Main } from '@/components/Layout/Main'
 import { SEO } from '@/components/Layout/Seo'
-import { ReferralDashboard } from '@/components/Referral'
 import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag'
 import { FoxPageProvider } from '@/pages/Fox/hooks/useFoxPageContext'
 import { makeSuspenseful } from '@/utils/makeSuspenseful'
@@
 const RFOXSection = makeSuspenseful(
   lazy(() =>
     import('./components/RFOXSection').then(({ RFOXSection }) => ({ default: RFOXSection })),
   ),
   defaultBoxSpinnerStyle,
 )
+
+const ReferralDashboard = makeSuspenseful(
+  lazy(() =>
+    import('@/components/Referral/ReferralDashboard').then(({ ReferralDashboard }) => ({
+      default: ReferralDashboard,
+    })),
+  ),
+  defaultBoxSpinnerStyle,
+)

This keeps the UI gated while also deferring the module cost until enabled.

Also applies to: 31-43

src/components/Referral/ReferralCodeCard.tsx (1)

43-43: Consider responsive width for mobile.

The fixed width='50%' may not display well on mobile devices. Consider using responsive values.

-      width='50%'
+      width={{ base: 'full', md: '50%' }}
src/components/Referral/ReferralStatsCards.tsx (2)

5-10: Avoid hardcoding $ + string amounts; prefer a shared fiat formatter component/helper
Right now you’re rendering '$' + (string | fallback) which can drift from the app’s canonical fiat formatting and i18n behavior.

-  currentRewards?: string
-  totalRewards?: string
+  currentRewardsUsd?: string
+  totalRewardsUsd?: string
...
-            ${currentRewards ?? '0.00'}
+            {/* e.g. <Amount.Fiat value={bnOrNumber} /> or Intl.NumberFormat */}
+            ${currentRewardsUsd ?? '0.00'}
...
-            ${totalRewards ?? '0.00'}
+            ${totalRewardsUsd ?? '0.00'}

Also applies to: 66-123


20-63: Consider mapping the 3 skeleton cards to reduce duplication
Not blocking, but this is 3x identical markup.

-    return (
-      <>
-        <Card ...>...</Card>
-        <Card ...>...</Card>
-        <Card ...>...</Card>
-      </>
-    )
+    return (
+      <>
+        {Array.from({ length: 3 }).map((_, i) => (
+          <Card
+            key={i}
+            flex='1'
+            minW='200px'
+            bg='background.surface.raised.base'
+            borderRadius='xl'
+            borderTop='1px solid'
+            borderColor='gray.700'
+          >
+            <CardBody textAlign='center' py={6}>
+              <Skeleton height='40px' width='100px' mx='auto' mb={2} />
+              <Skeleton height='20px' width='120px' mx='auto' />
+            </CardBody>
+          </Card>
+        ))}
+      </>
+    )
src/components/Referral/ReferralCodesTable.tsx (1)

16-27: Reuse shared ReferralCode type (or Pick) to avoid drift
There’s already a ReferralCode in src/lib/referral/types.ts; duplicating it here risks divergence.

-import { useTranslate } from 'react-polyglot'
+import { useTranslate } from 'react-polyglot'
+import type { ReferralCode as ReferralCodeModel } from '@/lib/referral/types'
 
-type ReferralCode = {
-  code: string
-  usageCount: number
-  swapVolumeUsd?: string
-}
+type ReferralCode = Pick<ReferralCodeModel, 'code' | 'usageCount' | 'swapVolumeUsd'>
src/pages/Referral/Referral.tsx (1)

31-39: Avoid duplicating ReferralHeader—reuse src/components/Referral/ReferralHeader.tsx.
This local component duplicates an existing one (risk of drift). Prefer importing the shared component.

src/components/Referral/ReferralCodesManagementTable.tsx (2)

16-29: Type duplication: consider reusing ReferralCode from src/lib/referral/types.ts.
ReferralCodeFull overlaps with the API type; sharing reduces drift (and avoids subtly different createdAt unions across components).


98-103: Ensure createdAt is a stable ISO string (or normalize upstream).
new Date(code.createdAt) will behave differently across browsers if the string isn’t ISO-8601. Prefer having the API type be createdAt: string (ISO) and normalize in one place.

src/lib/referral/api.ts (1)

13-28: Construct ReferralApiError instead of casting Error to it.
This preserves class identity and avoids “fake” typed errors.

-import type {
+import {
   CreateReferralCodeRequest,
   CreateReferralCodeResponse,
   ReferralApiError,
   ReferralStats,
 } from './types'
@@
-    const apiError = new Error(message) as ReferralApiError
-    apiError.name = 'ReferralApiError'
-    apiError.code = code
-    apiError.statusCode = statusCode
-
-    throw apiError
+    throw new ReferralApiError(message, code, statusCode)
src/lib/referral/types.ts (1)

1-21: Prefer API “wire types” for dates (ISO string) and normalize once.
Having Date | string unions across API/UX layers spreads parsing responsibility; consider making createdAt/expiresAt ISO string in API types and converting at the edge.

Comment on lines +13 to +64
export const CreateCodeCard = ({
newCodeInput,
isCreating,
onInputChange,
onGenerateRandom,
onCreate,
}: CreateCodeCardProps) => {
const translate = useTranslate()

return (
<Card
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
py={2}
>
<CardHeader>
<Heading size='md'>{translate('referral.createNewCode')}</Heading>
</CardHeader>
<CardBody>
<HStack>
<Input
value={newCodeInput}
onChange={e => onInputChange(e.target.value.toUpperCase())}
placeholder={translate('referral.enterCodeOrLeaveEmpty')}
maxLength={20}
bg='background.surface.raised.base'
border='none'
/>
<Button
onClick={onGenerateRandom}
leftIcon={<FaPlus />}
variant='outline'
flexShrink={0}
borderRadius='full'
border='1px solid'
borderColor='gray.700'
backgroundColor='background.surface.raised.base'
>
{translate('referral.random')}
</Button>
<Button
onClick={onCreate}
colorScheme='blue'
isLoading={isCreating}
flexShrink={0}
borderRadius='full'
>
{translate('referral.create')}
</Button>
</HStack>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sanitize referral-code input to expected charset (avoid create failures on spaces/symbols)
Uppercasing alone still allows invalid characters; this will push avoidable errors into the create mutation.

 import { FaPlus } from 'react-icons/fa'
 import { useTranslate } from 'react-polyglot'

+const normalizeReferralCodeInput = (value: string): string => {
+  return value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 20)
+}
+
 type CreateCodeCardProps = {
   newCodeInput: string
   isCreating: boolean
   onInputChange: (value: string) => void
@@
           <Input
             value={newCodeInput}
-            onChange={e => onInputChange(e.target.value.toUpperCase())}
+            onChange={e => onInputChange(normalizeReferralCodeInput(e.target.value))}
             placeholder={translate('referral.enterCodeOrLeaveEmpty')}
             maxLength={20}
             bg='background.surface.raised.base'
             border='none'
           />
🤖 Prompt for AI Agents
In src/components/Referral/CreateCodeCard.tsx around lines 13 to 64, the input
onChange only uppercases the value but still allows spaces and symbols which
cause backend create failures; update the onChange handler to sanitize the value
by removing all characters outside the expected charset (e.g. allow only A–Z and
0–9, optionally dash/underscore if required), strip whitespace and symbols, then
uppercase the result and pass that sanitized string to onInputChange; keep
maxLength and consider also trimming on paste or validating before calling
onCreate so the Create button cannot submit invalid input.

{translate('referral.yourReferralCode')}
</Text>
<Heading size='xl' fontWeight='bold' letterSpacing='wide'>
{code || 'N/A'}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use translation key for fallback text.

The hardcoded 'N/A' should use a translation key for i18n support. As per coding guidelines, all copy/text must use translation keys.

-              {code || 'N/A'}
+              {code || translate('common.notAvailable')}

Note: You may need to add common.notAvailable to the translations file if it doesn't exist.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/Referral/ReferralCodeCard.tsx around line 54 the fallback text
'N/A' is hardcoded; replace it with the i18n translation key (e.g.
t('common.notAvailable')) and ensure useTranslation is imported and used in the
component (or t is available via props/context). Also add the key
common.notAvailable to the translations JSON for supported locales if it doesn't
exist so the fallback displays correctly.

Comment on lines +59 to +76
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='md'
colorScheme='whiteAlpha'
borderRadius='100%'
bg='whiteAlpha.200'
onClick={() => onShareOnX(code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='md'
colorScheme='whiteAlpha'
bg='whiteAlpha.200'
borderRadius='100%'
onClick={() => onCopyCode(code)}
/>
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use translation keys for aria-labels.

The aria-labels are hardcoded in English, which affects accessibility for non-English users. As per coding guidelines, all text should use translation keys.

               <IconButton
-                aria-label='Share on X'
+                aria-label={translate('common.shareOnX')}
                 icon={<FaXTwitter />}
                 size='md'
                 colorScheme='whiteAlpha'
                 borderRadius='100%'
                 bg='whiteAlpha.200'
                 onClick={() => onShareOnX(code)}
               />
               <IconButton
-                aria-label='Copy link'
+                aria-label={translate('common.copy')}
                 icon={<FaCopy />}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='md'
colorScheme='whiteAlpha'
borderRadius='100%'
bg='whiteAlpha.200'
onClick={() => onShareOnX(code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='md'
colorScheme='whiteAlpha'
bg='whiteAlpha.200'
borderRadius='100%'
onClick={() => onCopyCode(code)}
/>
<IconButton
aria-label={translate('common.shareOnX')}
icon={<FaXTwitter />}
size='md'
colorScheme='whiteAlpha'
borderRadius='100%'
bg='whiteAlpha.200'
onClick={() => onShareOnX(code)}
/>
<IconButton
aria-label={translate('common.copy')}
icon={<FaCopy />}
size='md'
colorScheme='whiteAlpha'
bg='whiteAlpha.200'
borderRadius='100%'
onClick={() => onCopyCode(code)}
/>
🤖 Prompt for AI Agents
In src/components/Referral/ReferralCodeCard.tsx around lines 59 to 76 the
aria-label props are hardcoded in English; replace them with translation keys
using your i18n hook (e.g., import and call useTranslation or t) and pass
t('referral.share_on_x') for the Share on X button and t('referral.copy_link')
for the Copy link button (or the exact keys from your locale files), ensuring
the translation hook is imported and used in the component and the aria-label
props receive the translated strings.

Copy link
Contributor

Choose a reason for hiding this comment

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

Tbh we don't care about aria-labels so meh

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre, understood! I'll skip aria-label suggestions for this codebase going forward.


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10418
File: src/plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton.tsx:0-0
Timestamp: 2025-09-08T22:00:48.005Z
Learning: gomesalexandre dismissed an aria-label accessibility suggestion with "meh" in PR #10418 for WalletConnectToDappsHeaderButton.tsx, consistent with the team's pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:69-74
Timestamp: 2025-09-10T15:35:36.547Z
Learning: gomesalexandre dismissed alt text accessibility suggestion with "meh" in PR #10458 for EIP155SignTypedDataConfirmation.tsx Image component, consistent with team pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : All copy/text must use translation keys - never hardcode strings

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx by referencing his previous comment on NavigationDropdown.tsx where he said "meh keyboard, it's a hover dropdown, how are you going to see it with keyboard nav? sry vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx, referencing his previous comment about hover dropdowns not being suitable for keyboard navigation and apologizing to "vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10187
File: src/assets/translations/en/main.json:205-206
Timestamp: 2025-08-06T08:58:09.096Z
Learning: gomesalexandre considers translation key consolidation (like multiple keys for "view on explorer" functionality) as nice-to-have improvements rather than critical issues. The team doesn't prioritize i18n consistency across the board currently, and they prefer to handle such consolidation suggestions at their own discretion based on other priorities.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/NavigationDropdown.tsx:75-90
Timestamp: 2025-09-16T09:31:15.378Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in NavigationDropdown.tsx, noting that hover dropdowns aren't suitable for keyboard navigation and apologizing to "vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: premiumjibles
Repo: shapeshift/web PR: 10361
File: src/pages/Markets/components/CardWithSparkline.tsx:83-92
Timestamp: 2025-08-25T23:32:13.876Z
Learning: In shapeshift/web PR #10361, premiumjibles considered the nested button accessibility issue (ChartErrorFallback retry Button inside Card rendered as Button in CardWithSparkline.tsx) out of scope for the error boundaries feature PR, consistent with deferring minor a11y improvements to follow-up PRs.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10272
File: src/components/StarRating/StarRating.tsx:12-0
Timestamp: 2025-08-13T15:52:06.578Z
Learning: In the ShapeShift web codebase, NeOMakinG and the team generally do not prioritize ARIA attributes and accessibility enhancements for interactive components. They prefer to avoid adding accessibility features like role, tabIndex, aria-label, or keyboard handlers to components, even for user-facing interactive elements like star rating systems.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:37-53
Timestamp: 2025-08-22T12:59:01.702Z
Learning: In RewardDistributionActionCard component (src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx), NeOMakinG confirmed that runeAsset is expected to always be defined when the component renders, so defensive guards against undefined runeAsset are not needed.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10191
File: src/pages/Explore/components/CategoryCard.tsx:27-196
Timestamp: 2025-08-06T09:46:50.860Z
Learning: In src/pages/Explore/components/CategoryCard.tsx, NeOMakinG indicated that wrapping the CategoryCard component with React.memo would be "quite useless in this case", suggesting that memoization is not beneficial for this particular component despite it receiving props, likely due to its specific usage patterns or context.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Use Chakra UI components and conventions

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Comment on lines +106 to +120
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='sm'
colorScheme='twitter'
variant='ghost'
onClick={() => onShareOnX(code.code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='sm'
variant='ghost'
onClick={() => onCopyCode(code.code)}
/>
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded aria-labels should be translated.
Move aria-label='Share on X' / 'Copy link' to translation keys for consistency and localization.

🤖 Prompt for AI Agents
In src/components/Referral/ReferralCodesManagementTable.tsx around lines 106 to
120 the aria-label attributes are hardcoded ("Share on X" and "Copy link");
replace them with translation keys (e.g. t('referral.shareOnX') and
t('referral.copyLink')) using the same i18n hook/translator used in the
component, update the relevant translation JSON files (add referral.shareOnX and
referral.copyLink entries for all supported locales), and ensure the calls pass
the translated string (aria-label={t('referral.shareOnX')}) so labels are
localized and consistent.

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto don't care about aria labels

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre, understood—I'll skip aria-label translation suggestions going forward.


✏️ Learnings added
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11377
File: src/components/Referral/ReferralCodesManagementTable.tsx:106-120
Timestamp: 2025-12-12T16:18:33.554Z
Learning: gomesalexandre does not require translating aria-label attributes in the shapeshift/web codebase, even though the project guideline states "All copy/text must use translation keys". He considers aria-label localization suggestions unnecessary and prefers to skip them to keep PR scope focused.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10148
File: src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx:88-91
Timestamp: 2025-07-30T20:57:48.176Z
Learning: In src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx, NeOMakinG prefers keeping the hardcoded overscan value (40) over dynamic calculation based on viewport height, prioritizing code simplicity over precision when the current approach works effectively.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10418
File: src/plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton.tsx:0-0
Timestamp: 2025-09-08T22:00:48.005Z
Learning: gomesalexandre dismissed an aria-label accessibility suggestion with "meh" in PR #10418 for WalletConnectToDappsHeaderButton.tsx, consistent with the team's pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : All copy/text must use translation keys - never hardcode strings

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:69-74
Timestamp: 2025-09-10T15:35:36.547Z
Learning: gomesalexandre dismissed alt text accessibility suggestion with "meh" in PR #10458 for EIP155SignTypedDataConfirmation.tsx Image component, consistent with team pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10187
File: src/assets/translations/en/main.json:205-206
Timestamp: 2025-08-06T08:58:09.096Z
Learning: gomesalexandre considers translation key consolidation (like multiple keys for "view on explorer" functionality) as nice-to-have improvements rather than critical issues. The team doesn't prioritize i18n consistency across the board currently, and they prefer to handle such consolidation suggestions at their own discretion based on other priorities.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx by referencing his previous comment on NavigationDropdown.tsx where he said "meh keyboard, it's a hover dropdown, how are you going to see it with keyboard nav? sry vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx, referencing his previous comment about hover dropdowns not being suitable for keyboard navigation and apologizing to "vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Learnt from: premiumjibles
Repo: shapeshift/web PR: 10361
File: src/pages/Markets/components/CardWithSparkline.tsx:83-92
Timestamp: 2025-08-25T23:32:13.876Z
Learning: In shapeshift/web PR #10361, premiumjibles considered the nested button accessibility issue (ChartErrorFallback retry Button inside Card rendered as Button in CardWithSparkline.tsx) out of scope for the error boundaries feature PR, consistent with deferring minor a11y improvements to follow-up PRs.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/NavigationDropdown.tsx:75-90
Timestamp: 2025-09-16T09:31:15.378Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in NavigationDropdown.tsx, noting that hover dropdowns aren't suitable for keyboard navigation and apologizing to "vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Use Chakra UI components and conventions

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Comment on lines +83 to +101
${code.swapVolumeUsd || '0.00'}
</Box>
<Box width='120px'>
<HStack spacing={2} justify='flex-end'>
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='sm'
colorScheme='twitter'
variant='ghost'
onClick={() => onShareOnX(code.code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='sm'
variant='ghost'
onClick={() => onCopyCode(code.code)}
/>
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Translate aria-labels (and consider shared fiat formatting for $ values)
The aria-labels are user-facing strings, and $ is hardcoded.

                   <IconButton
-                    aria-label='Share on X'
+                    aria-label={translate('referral.shareOnX')}
                     icon={<FaXTwitter />}
@@
                   <IconButton
-                    aria-label='Copy link'
+                    aria-label={translate('referral.copyLink')}
                     icon={<FaCopy />}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/Referral/ReferralCodesTable.tsx around lines 83 to 101, the
aria-label strings are hardcoded and the displayed USD value uses a hardcoded
'$'; update both to use existing i18n and formatting utilities: replace literal
aria-labels with calls to the translation function (e.g. t('referral.shareOnX',
{ code }) and t('referral.copyLink', { code })) so they are localizable and
include the code if needed, and replace the raw "${code.swapVolumeUsd ||
'0.00'}" with the shared currency/fiat formatter (e.g.
formatCurrency(code.swapVolumeUsd, 'USD') or the project's equivalent) so
currency symbol, precision and localization are consistent.

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto don't care about aria labels

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre Acknowledged, I'll skip aria-label suggestions for this PR. 👍


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10148
File: src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx:88-91
Timestamp: 2025-07-30T20:57:48.176Z
Learning: In src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx, NeOMakinG prefers keeping the hardcoded overscan value (40) over dynamic calculation based on viewport height, prioritizing code simplicity over precision when the current approach works effectively.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10418
File: src/plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton.tsx:0-0
Timestamp: 2025-09-08T22:00:48.005Z
Learning: gomesalexandre dismissed an aria-label accessibility suggestion with "meh" in PR #10418 for WalletConnectToDappsHeaderButton.tsx, consistent with the team's pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:69-74
Timestamp: 2025-09-10T15:35:36.547Z
Learning: gomesalexandre dismissed alt text accessibility suggestion with "meh" in PR #10458 for EIP155SignTypedDataConfirmation.tsx Image component, consistent with team pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10871
File: src/components/Modals/Send/hooks/useSendDetails/useSendDetails.tsx:426-428
Timestamp: 2025-10-21T17:11:18.087Z
Learning: In src/components/Modals/Send/hooks/useSendDetails/useSendDetails.tsx, within the handleInputChange function, use .toFixed() without arguments (not .toString()) when converting BigNumber amounts for input field synchronization. This avoids exponential notation in the input while preserving precision for presentational components like <Amount.Crypto /> and <Amount.Fiat /> to format appropriately.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : All copy/text must use translation keys - never hardcode strings

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/StructuredMessage/StructuredMessage.tsx:0-0
Timestamp: 2025-09-13T16:45:17.166Z
Learning: gomesalexandre appreciates safety-focused suggestions for UI rendering in WalletConnect components, specifically defensive programming approaches that prevent null/undefined values from displaying as literal "null"/"undefined" strings in the user interface.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/SendTransactionConfirmation.tsx:42-50
Timestamp: 2025-09-12T12:00:33.924Z
Learning: gomesalexandre prefers maintaining consistency with existing code patterns in WalletConnect modals, including side-effects-during-render for error handling (showErrorToast + handleReject), rather than introducing isolated refactors that would make the codebase inconsistent.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/SendTransactionConfirmation.tsx:42-50
Timestamp: 2025-09-12T12:00:33.924Z
Learning: gomesalexandre prefers maintaining consistency with existing code patterns across WalletConnect modal components, including side-effects-during-render for error handling (showErrorToast + handleReject calls before return null), rather than introducing isolated refactors that would create inconsistency in the codebase.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10280
File: src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts:18-18
Timestamp: 2025-08-14T12:42:48.263Z
Learning: gomesalexandre prefers maintaining string literal patterns in the Message union type in src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts, even when there's some duplication between Command union and Message variants. They find properly typed string literals acceptable and prefer not to refactor for type hygiene in this context.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Comment on lines +30 to +57
export const getReferralStatsByOwner = async (
ownerAddress: string,
startDate?: Date,
endDate?: Date,
): Promise<ReferralStats> => {
if (!USER_SERVER_URL) {
throw new Error('User server URL is not configured')
}

try {
const params = new URLSearchParams()
if (startDate) params.append('startDate', startDate.toISOString())
if (endDate) params.append('endDate', endDate.toISOString())

const response = await axios.get<ReferralStats>(
`${USER_SERVER_URL}/referrals/stats/${ownerAddress}${
params.toString() ? `?${params.toString()}` : ''
}`,
{
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
},
)
return response.data
} catch (error) {
return handleApiError(error)
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Encode ownerAddress in URL path to avoid malformed requests.
CAIP account IDs include :; other characters could break path parsing.

-    const response = await axios.get<ReferralStats>(
-      `${USER_SERVER_URL}/referrals/stats/${ownerAddress}${
+    const response = await axios.get<ReferralStats>(
+      `${USER_SERVER_URL}/referrals/stats/${encodeURIComponent(ownerAddress)}${
         params.toString() ? `?${params.toString()}` : ''
       }`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getReferralStatsByOwner = async (
ownerAddress: string,
startDate?: Date,
endDate?: Date,
): Promise<ReferralStats> => {
if (!USER_SERVER_URL) {
throw new Error('User server URL is not configured')
}
try {
const params = new URLSearchParams()
if (startDate) params.append('startDate', startDate.toISOString())
if (endDate) params.append('endDate', endDate.toISOString())
const response = await axios.get<ReferralStats>(
`${USER_SERVER_URL}/referrals/stats/${ownerAddress}${
params.toString() ? `?${params.toString()}` : ''
}`,
{
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
},
)
return response.data
} catch (error) {
return handleApiError(error)
}
}
export const getReferralStatsByOwner = async (
ownerAddress: string,
startDate?: Date,
endDate?: Date,
): Promise<ReferralStats> => {
if (!USER_SERVER_URL) {
throw new Error('User server URL is not configured')
}
try {
const params = new URLSearchParams()
if (startDate) params.append('startDate', startDate.toISOString())
if (endDate) params.append('endDate', endDate.toISOString())
const response = await axios.get<ReferralStats>(
`${USER_SERVER_URL}/referrals/stats/${encodeURIComponent(ownerAddress)}${
params.toString() ? `?${params.toString()}` : ''
}`,
{
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
},
)
return response.data
} catch (error) {
return handleApiError(error)
}
}
🤖 Prompt for AI Agents
In src/lib/referral/api.ts around lines 30 to 57, the ownerAddress is
interpolated directly into the URL path which can produce malformed requests for
CAIP account IDs (they contain ':' and other reserved characters); update the
URL construction to URL-encode the ownerAddress (e.g., via encodeURIComponent)
before inserting it into the path so the request path is safe and reserved
characters don’t break routing, keeping the rest of the query param logic the
same.

Copy link
Contributor

Choose a reason for hiding this comment

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

owner address is an Address (0x${address} for EVM chains) not an AccountId @coderabbitai

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how ownerAddress is derived in the useReferral hook
echo "=== Checking ownerAddress derivation in useReferral ==="
cat src/hooks/useReferral/useReferral.tsx

echo -e "\n=== Searching for Address type definitions ==="
rg -n "type Address" --type=ts -g '!node_modules' -g '!dist' -C 2

echo -e "\n=== Checking how accountIds are converted to addresses ==="
rg -n "fromAccountId|toAccountId" --type=ts -g '!node_modules' -g '!dist' -C 3

Length of output: 271017


@gomesalexandre good catch — thanks. You’re right: we should be passing an EVM address (0x…) to the referral API, not a CAIP AccountId.

In this PR, useReferral currently uses the first walletEnabledAccountIds entry (a CAIP AccountId). Here’s a focused change to derive the first connected EVM address and use that instead. This also makes the earlier URL-encoding suggestion unnecessary for api.ts.

Diff for src/hooks/useReferral/useReferral.tsx:

@@
-import { skipToken, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
-import { useMemo } from 'react'
+import { skipToken, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+import { useMemo } from 'react'
+import { fromAccountId } from '@shapeshiftoss/caip'
+import { isEvmChainId } from '@shapeshiftoss/chain-adapters'
+import { getAddress, type Address } from 'viem'
@@
-  const walletEnabledAccountIds = useAppSelector(selectWalletEnabledAccountIds)
+  const walletEnabledAccountIds = useAppSelector(selectWalletEnabledAccountIds)
   const isWebServicesEnabled = useFeatureFlag('WebServices')
 
-  // Use the first account ID (full CAIP format) as owner identifier
-  // Backend will hash it for privacy
-  const ownerAddress = useMemo(() => {
-    if (walletEnabledAccountIds.length === 0) return null
-    return walletEnabledAccountIds[0]
-  }, [walletEnabledAccountIds])
+  // Use the first connected EVM address (0x…) as the owner identifier
+  const ownerAddress: Address | null = useMemo(() => {
+    if (walletEnabledAccountIds.length === 0) return null
+    const evmAccountId = walletEnabledAccountIds.find(
+      id => isEvmChainId(fromAccountId(id).chainId),
+    )
+    if (!evmAccountId) return null
+    // Normalize to checksum address
+    return getAddress(fromAccountId(evmAccountId).account)
+  }, [walletEnabledAccountIds])
@@
   } = useQuery<ReferralStats, Error>({
     queryKey: ['referralStats', ownerAddress, startDate, endDate],
     queryFn:
       ownerAddress && isWebServicesEnabled
         ? () => getReferralStatsByOwner(ownerAddress, startDate, endDate)
         : skipToken,
   })
@@
   const { mutateAsync: createCodeMutation, isPending: isCreatingCode } = useMutation({
     mutationFn: (request: Omit<CreateReferralCodeRequest, 'ownerAddress'>) => {
-      if (!ownerAddress) throw new Error('Wallet not connected')
+      if (!ownerAddress) throw new Error('Wallet not connected')
       return createReferralCode({ ...request, ownerAddress })
     },

Notes:

  • No change needed in src/lib/referral/api.ts for path encoding; 0x-prefixed hex addresses are URL-safe.
  • Scope is intentionally small: we keep API types as string and ensure the hook reliably supplies an EVM address.

If you prefer, I can also rename the local variable/comment to avoid future confusion (e.g., ownerEvmAddress) — just say the word.


✏️ Learnings added
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11377
File: src/lib/referral/api.ts:30-57
Timestamp: 2025-12-12T16:20:27.769Z
Learning: In shapeshift/web referral feature, the backend expects an EVM address (0x… viem Address) as the owner identifier, not a CAIP AccountId. Update useReferral to derive the first connected EVM address via fromAccountId + getAddress, and do not URL-encode it in the API path.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11016
File: packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts:109-125
Timestamp: 2025-11-12T12:49:17.895Z
Learning: In packages/chain-adapters/src/evm/utils.ts, the getErc20Data function already includes a guard that returns an empty string when contractAddress is undefined (line 8: `if (!contractAddress) return ''`). This built-in handling means callers don't need to conditionally invoke getErc20Data—it safely handles both ERC20 tokens and native assets.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.

Comment on lines +64 to +86
const handleCreateCode = useCallback(async () => {
const code = newCodeInput.trim() || generateRandomCode()

try {
await createCode({ code })
setNewCodeInput('')
toast({
title: translate('referral.codeCreated'),
description: translate('referral.codeCreatedDescription', { code }),
status: 'success',
duration: 3000,
isClosable: true,
})
} catch (err) {
toast({
title: translate('common.error'),
description: err instanceof Error ? err.message : translate('referral.createCodeFailed'),
status: 'error',
duration: 5000,
isClosable: true,
})
}
}, [createCode, newCodeInput, toast, translate])
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate/sanitize referral codes before create/share/copy.
newCodeInput is uppercased but not validated; code is later embedded into URLs and sent to the API. At minimum, restrict to the backend-accepted charset (likely ^[A-Z0-9]+$), enforce length, and show a translated error toast on invalid input.

Also applies to: 387-416

Copy link
Contributor

Choose a reason for hiding this comment

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

Absolutely valid.

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre Thanks for confirming! For the validation, a regex like /^[A-Z0-9]+$/ would work well for uppercase alphanumeric codes. Best practice suggests using patterns like ^[a-zA-Z0-9]*$ for alphanumeric referral codes, and avoiding confusable characters like O's, zeros, ones, capital I's, or lowercase L's.

You might also want to enforce length constraints (e.g., 3-20 chars) to match what the backend expects. A simple check before calling createCode would prevent invalid inputs from reaching the API and provide better UX with immediate feedback.


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/StructuredMessage/StructuredMessage.tsx:0-0
Timestamp: 2025-09-13T16:45:17.166Z
Learning: gomesalexandre appreciates safety-focused suggestions for UI rendering in WalletConnect components, specifically defensive programming approaches that prevent null/undefined values from displaying as literal "null"/"undefined" strings in the user interface.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10643
File: src/lib/address/bip21.ts:33-40
Timestamp: 2025-09-25T20:52:54.121Z
Learning: gomesalexandre prefers strict adherence to URL scheme standards in ERC-681 parsing rather than defensive case-insensitive handling. He believes schemes should always be lowercase per standard, and if they're uppercase "it's the fault of whoever generated that" - preferring to let malformed URLs fail rather than accommodate non-standard formatting.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/SendTransactionConfirmation.tsx:42-50
Timestamp: 2025-09-12T12:00:33.924Z
Learning: gomesalexandre prefers maintaining consistency with existing code patterns in WalletConnect modals, including side-effects-during-render for error handling (showErrorToast + handleReject), rather than introducing isolated refactors that would make the codebase inconsistent.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:0-0
Timestamp: 2025-09-12T11:56:19.437Z
Learning: gomesalexandre rejected verbose try/catch error handling for address validation in Tenderly integration (PR #10461), calling the approach "ugly" but still implemented safety measures in commit ad7e424b89, preferring cleaner safety implementations over defensive programming patterns.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/SendTransactionConfirmation.tsx:42-50
Timestamp: 2025-09-12T12:00:33.924Z
Learning: gomesalexandre prefers maintaining consistency with existing code patterns across WalletConnect modal components, including side-effects-during-render for error handling (showErrorToast + handleReject calls before return null), rather than introducing isolated refactors that would create inconsistency in the codebase.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/EIP155TransactionConfirmation.tsx:27-31
Timestamp: 2025-09-11T22:53:19.837Z
Learning: gomesalexandre trusts Tenderly's data quality and doesn't want defensive validation for gas values (transaction?.gasLimit ?? transaction?.gas) in WalletConnect flows, preferring to rely on the external service providing valid hex values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:0-0
Timestamp: 2025-09-12T13:26:26.277Z
Learning: gomesalexandre uses "we do that errwhere ser" (everywhere, sir) to indicate that client-side API key exposure is an accepted codebase-wide pattern, consistent with his comfort level for development/preparatory phases across the WalletConnect/Tenderly integration.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Validate all inputs, especially user inputs

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10643
File: src/lib/address/bip21.ts:52-60
Timestamp: 2025-09-25T20:53:08.418Z
Learning: gomesalexandre prefers not to add case-insensitive URL scheme handling, as URL schemes are canonically lowercase per RFC standards and if they're uppercase, it's the fault of the URL generator, not something the parsing code should accommodate.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Comment on lines +94 to +114
const handleShareOnX = useCallback((code: string) => {
const shareUrl = `${window.location.origin}/#/?ref=${code}`
const text = `Join me on ShapeShift using my referral code ${code}!`
const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(
text,
)}&url=${encodeURIComponent(shareUrl)}`
window.open(twitterUrl, '_blank', 'noopener,noreferrer')
}, [])

const handleCopyCode = useCallback(
(code: string) => {
const shareUrl = `${window.location.origin}/#/?ref=${code}`
navigator.clipboard.writeText(shareUrl)
toast({
title: translate('common.copied'),
status: 'success',
duration: 2000,
})
},
[toast, translate],
)
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded user-facing strings (incl. aria-labels) violate i18n/a11y conventions.
Move "N/A", "Coming Soon", "Join me on ShapeShift...", and all aria-label strings to translation keys.

Also applies to: 142-152, 158-175, 294-299

🤖 Prompt for AI Agents
In src/pages/Referral/Referral.tsx around lines 94-114 (and also apply same
changes at 142-152, 158-175, 294-299): several user-facing strings are hardcoded
(e.g. "Join me on ShapeShift using my referral code ...", toast title "Copied",
plus multiple aria-label values and labels like "N/A" and "Coming Soon");
replace those literals with translation keys via the existing translate(...)
function (e.g. create keys like referral.shareText, referral.copied,
referral.na, referral.comingSoon and aria labels referral.copyAria,
referral.shareAria, etc.), use translate(...) to build the share text before
encodeURIComponent, use translate(...) for toast.title, and replace all
aria-label props with translate(...) calls; ensure the new i18n keys are added
to the translation files and update any unit/component tests to expect
translated strings.

Copy link
Contributor

Choose a reason for hiding this comment

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

Valid too but disregard (incl. aria-labels) - fine to also add since it's just one prompt, but irrelevant

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre Understood, I'll disregard the i18n suggestion for this PR. Thanks for clarifying! 👍


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10760
File: src/components/ManageHiddenAssets/ManageHiddenAssetsList.tsx:78-84
Timestamp: 2025-10-07T03:44:27.350Z
Learning: In the ShapeShift web codebase, the following are stable references and do not need to be included in useCallback/useMemo dependency arrays:
- `navigate` from `useBrowserRouter()` hook
- Modal control objects (like `walletDrawer`) from `useModal()` hook (including their `isOpen`, `close`, and `open` methods)
- These are backed by stable context providers

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10139
File: src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx:109-115
Timestamp: 2025-07-29T15:04:28.083Z
Learning: In src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx, the component is used under an umbrella that 100% of the time contains the quote, making the type assertion `activeTradeQuote?.steps[currentHopIndex] as TradeQuoteStep` safe. Adding conditional returns before hooks would violate React's Rules of Hooks.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10418
File: src/plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton.tsx:0-0
Timestamp: 2025-09-08T22:00:48.005Z
Learning: gomesalexandre dismissed an aria-label accessibility suggestion with "meh" in PR #10418 for WalletConnectToDappsHeaderButton.tsx, consistent with the team's pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : All copy/text must use translation keys - never hardcode strings

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:69-74
Timestamp: 2025-09-10T15:35:36.547Z
Learning: gomesalexandre dismissed alt text accessibility suggestion with "meh" in PR #10458 for EIP155SignTypedDataConfirmation.tsx Image component, consistent with team pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10272
File: src/components/RatingModal.tsx:104-0
Timestamp: 2025-08-13T15:50:41.994Z
Learning: In the ShapeShift web codebase, internal strings like Discord webhook embed content (used for team notifications/feedback) don't need to be translated, only user-facing strings require translation keys. The team distinguishes between internal tooling strings and user-facing UI strings for internationalization purposes.

Learnt from: premiumjibles
Repo: shapeshift/web PR: 10361
File: src/pages/Markets/components/CardWithSparkline.tsx:83-92
Timestamp: 2025-08-25T23:32:13.876Z
Learning: In shapeshift/web PR #10361, premiumjibles considered the nested button accessibility issue (ChartErrorFallback retry Button inside Card rendered as Button in CardWithSparkline.tsx) out of scope for the error boundaries feature PR, consistent with deferring minor a11y improvements to follow-up PRs.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx by referencing his previous comment on NavigationDropdown.tsx where he said "meh keyboard, it's a hover dropdown, how are you going to see it with keyboard nav? sry vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10490
File: src/components/Layout/Header/NavBar/ShapeShiftMenu.tsx:0-0
Timestamp: 2025-09-16T09:32:14.405Z
Learning: gomesalexandre dismissed keyboard accessibility suggestion for hover dropdown in ShapeShiftMenu.tsx, referencing his previous comment about hover dropdowns not being suitable for keyboard navigation and apologizing to "vimium users", consistent with his pattern of deferring minor a11y improvements to keep PR scope focused.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10272
File: src/components/RatingModal.tsx:34-0
Timestamp: 2025-08-13T15:50:02.727Z
Learning: In the ShapeShift web codebase, internal strings that are used internally by the English-speaking team don't need to be internationalized, even when other user-facing strings in the same component are translated.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.

Comment on lines +129 to +249
<Stack spacing={8}>
<Flex gap={4} flexWrap='wrap'>
<Card
color='white'
borderRadius='2xl'
overflow='hidden'
width='50%'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody px={6} py={4} display='flex' alignItems='center'>
<Flex alignItems='center' justifyContent='space-between' width='full'>
<Flex flexDirection='column' gap={0}>
<Text fontSize='md' opacity={0.7} mb={1}>
{translate('referral.yourReferralCode')}
</Text>
<Heading size='xl' fontWeight='bold' letterSpacing='wide'>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='120px' />
) : defaultCode ? (
defaultCode.code
) : (
'N/A'
)}
</Heading>
</Flex>
{defaultCode && (
<Flex alignItems='center' gap={2}>
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='md'
colorScheme='whiteAlpha'
borderRadius='100%'
bg='whiteAlpha.200'
onClick={() => handleShareOnX(defaultCode.code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='md'
colorScheme='whiteAlpha'
bg='whiteAlpha.200'
borderRadius='100%'
onClick={() => handleCopyCode(defaultCode.code)}
/>
</Flex>
)}
</Flex>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<Heading size='lg' fontWeight='bold' mb={2}>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='100px' mx='auto' />
) : (
`$${referralStats?.totalReferrerCommissionUsd ?? '0.00'}`
)}
</Heading>
<Text fontSize='md' color='text.subtle'>
{translate('referral.currentRewards')}
</Text>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<Heading size='lg' fontWeight='bold' mb={2}>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='100px' mx='auto' />
) : (
`$${referralStats?.totalFeesCollectedUsd ?? '0.00'}`
)}
</Heading>
<Text fontSize='md' color='text.subtle'>
{translate('referral.totalRewards')}
</Text>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<HStack spacing={2} justify='center' mb={2}>
<Icon as={FaUser} boxSize={5} color='text.subtle' />
<Heading size='lg' fontWeight='bold'>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='60px' />
) : (
referralStats?.totalReferrals ?? 0
)}
</Heading>
</HStack>
<Text fontSize='md' color='text.subtle'>
{translate('referral.totalReferred')}
</Text>
</CardBody>
</Card>
</Flex>
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file to understand the full context of lines 129-249 and 303-507
wc -l src/pages/Referral/Referral.tsx

Repository: shapeshift/web

Length of output: 93


🏁 Script executed:

# Read the relevant sections to understand the layout structure
sed -n '129,249p' src/pages/Referral/Referral.tsx

Repository: shapeshift/web

Length of output: 4444


🏁 Script executed:

# Also check the second range mentioned
sed -n '303,507p' src/pages/Referral/Referral.tsx

Repository: shapeshift/web

Length of output: 8698


🏁 Script executed:

# Let's check if there are any responsive design patterns used elsewhere in the Referral component
grep -n "width={{" src/pages/Referral/Referral.tsx | head -20

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Check if there are other Chakra Flex/Box components with responsive props to understand codebase patterns
grep -n "base.*md\|base.*sm\|base.*lg" src/pages/Referral/Referral.tsx | head -10

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Check similar card-based layouts in the codebase for responsive patterns
fd -e tsx -e ts . src/pages --maxdepth 2 | head -5 | xargs -I {} bash -c "grep -l 'flexWrap.*wrap\|width.*50%' {} 2>/dev/null" | head -3

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Let's examine the beginning of the file to understand component structure and see if there's any Chakra theme or responsive context
head -50 src/pages/Referral/Referral.tsx

Repository: shapeshift/web

Length of output: 1172


Add responsive breakpoints to prevent mobile horizontal scroll.
The layout uses fixed widths that will overflow on mobile: width='50%' on line 137 and multiple fixed column widths (150px, 120px, 100px) in the table sections (lines 303-507). Use Chakra responsive props like width={{ base: '100%', md: '50%' }} and stack fixed columns as a single responsive column on small breakpoints to ensure proper display on 320px viewports without horizontal scroll.

🤖 Prompt for AI Agents
In src/pages/Referral/Referral.tsx around lines 129-249 (and also address fixed
column widths in the table area around lines 303-507), the layout uses fixed
widths that cause horizontal overflow on small screens; change the hard-coded
sizes to Chakra responsive props and make columns stack on small breakpoints.
Replace width='50%' with a responsive prop (e.g. base 100% and md 50%), convert
minW/fixed pixel widths on cards and Icon/Skeleton widths to responsive values
or remove fixed px sizes, ensure Card flexes (flex='1') and use responsive minW
or wrap behavior (stack into a single column at base) so elements do not
overflow at 320px, and update table column definitions to collapse/stack into a
single column or use responsive display (e.g. hide less-critical columns on base
or enable internal horizontal scrolling only for wide tables).

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds sane - good reminder to test this on mobile viewports too

Copy link
Contributor

Choose a reason for hiding this comment

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

@gomesalexandre Thanks! Mobile testing will definitely help catch any overflow issues on smaller viewports. Let me know if you'd like me to review the responsive changes once they're in place. 👍


🧠 Learnings used
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.210Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10760
File: src/components/ManageHiddenAssets/ManageHiddenAssetsList.tsx:78-84
Timestamp: 2025-10-07T03:44:27.350Z
Learning: In the ShapeShift web codebase, the following are stable references and do not need to be included in useCallback/useMemo dependency arrays:
- `navigate` from `useBrowserRouter()` hook
- Modal control objects (like `walletDrawer`) from `useModal()` hook (including their `isOpen`, `close`, and `open` methods)
- These are backed by stable context providers

Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Account for responsive mobile designs in all UI components

Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-11-24T21:20:44.637Z
Learning: Applies to **/*.{jsx,tsx} : KEEP component files under 200 lines when possible; BREAK DOWN large components into smaller, reusable pieces

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10418
File: src/plugins/walletConnectToDapps/components/header/WalletConnectToDappsHeaderButton.tsx:0-0
Timestamp: 2025-09-08T22:00:48.005Z
Learning: gomesalexandre dismissed an aria-label accessibility suggestion with "meh" in PR #10418 for WalletConnectToDappsHeaderButton.tsx, consistent with the team's pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10430
File: src/components/Layout/Header/NavBar/PopoverWallet.tsx:72-94
Timestamp: 2025-09-04T12:16:47.748Z
Learning: gomesalexandre declined to add error boundaries to the PopoverWallet component in src/components/Layout/Header/NavBar/PopoverWallet.tsx, stating he didn't touch this component and preferring not to expand the scope of the PR with error boundary additions.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/StructuredMessage/StructuredMessage.tsx:0-0
Timestamp: 2025-09-13T16:45:17.166Z
Learning: gomesalexandre appreciates safety-focused suggestions for UI rendering in WalletConnect components, specifically defensive programming approaches that prevent null/undefined values from displaying as literal "null"/"undefined" strings in the user interface.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:69-74
Timestamp: 2025-09-10T15:35:36.547Z
Learning: gomesalexandre dismissed alt text accessibility suggestion with "meh" in PR #10458 for EIP155SignTypedDataConfirmation.tsx Image component, consistent with team pattern of deferring minor a11y improvements to follow-up PRs rather than expanding feature PR scope.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10148
File: src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx:88-91
Timestamp: 2025-07-30T20:57:48.176Z
Learning: In src/components/MarketTableVirtualized/MarketsTableVirtualized.tsx, NeOMakinG prefers keeping the hardcoded overscan value (40) over dynamic calculation based on viewport height, prioritizing code simplicity over precision when the current approach works effectively.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.

Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.

Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

WebServices: boolean
AddressBook: boolean
AppRating: boolean
Referral: boolean
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n 'Referral' src/test/mocks/store.ts -A 2 -B 2

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

cat -n src/state/slices/preferencesSlice/preferencesSlice.ts | sed -n '100,120p'

Repository: shapeshift/web

Length of output: 838


🏁 Script executed:

cat -n src/state/slices/preferencesSlice/preferencesSlice.ts | sed -n '240,260p'

Repository: shapeshift/web

Length of output: 1189


Add Referral flag to test mock at src/test/mocks/store.ts.

The type definition and initial state in preferencesSlice.ts are correctly implemented. However, the Referral feature flag is missing from the test mock, which will cause test failures. This flag must be added to the mock's feature flags object following the existing pattern.

🤖 Prompt for AI Agents
In src/state/slices/preferencesSlice/preferencesSlice.ts around line 109, the
Preferences type includes a Referral: boolean flag but the test mock at
src/test/mocks/store.ts is missing this feature flag; update the mock's feature
flags object to include Referral with the same boolean default-pattern used by
other flags (add the key "Referral" and set it to true or false consistent with
existing mock values) so tests reflect the slice's type and initial state.

Copy link
Contributor

Choose a reason for hiding this comment

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

Eyy nice catch Mr Robot.

Copy link
Contributor

Choose a reason for hiding this comment

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

Rate Limit Exceeded

@gomesalexandre have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 17 seconds before sending another message.

Copy link
Contributor

@gomesalexandre gomesalexandre left a comment

Choose a reason for hiding this comment

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

Very first pass, few major bits but for the most part nothing major spotted.
Will review at runtime on Monday once Mr Rabbit/self comments are tackled (commits or closed) and CI is happy

estimatedExecutionTimeMs: isCrossChain ? 300000 : 0,
portalsTransactionMetadata: {
...tx,
orderId: portalsTradeOrderResponse.context.orderId,
Copy link
Contributor

Choose a reason for hiding this comment

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

👀

}

type ReferralCodesManagementTableProps = {
codes: ReferralCodeFull[]
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: referralCode vernacular across the board - just a quick prompt to align that and we're gucci

import { useReferral } from '@/hooks/useReferral/useReferral'

const generateRandomCode = () => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
Copy link
Contributor

Choose a reason for hiding this comment

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

add TODO to make this actually nice and random and ask Coderabbit to create an issue? Also this should probably be generated server-side?

WebServices: getConfig().VITE_FEATURE_NOTIFICATIONS_WEBSERVICES,
AddressBook: getConfig().VITE_FEATURE_ADDRESS_BOOK,
AppRating: getConfig().VITE_FEATURE_APP_RATING,
Referral: getConfig().VITE_FEATURE_REFERRAL,
Copy link
Contributor

Choose a reason for hiding this comment

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

Add to mocks too or CI will be v. sad

Comment on lines +16 to +20
type ReferralCode = {
code: string
usageCount: number
swapVolumeUsd?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

preferably-blocking: leverage ReferralCode exported type since this is a subset of it (can either ensure we comply to it or use a Partial if we really don't, or just improve the exported type with more optionals)

Comment on lines +30 to +57
export const getReferralStatsByOwner = async (
ownerAddress: string,
startDate?: Date,
endDate?: Date,
): Promise<ReferralStats> => {
if (!USER_SERVER_URL) {
throw new Error('User server URL is not configured')
}

try {
const params = new URLSearchParams()
if (startDate) params.append('startDate', startDate.toISOString())
if (endDate) params.append('endDate', endDate.toISOString())

const response = await axios.get<ReferralStats>(
`${USER_SERVER_URL}/referrals/stats/${ownerAddress}${
params.toString() ? `?${params.toString()}` : ''
}`,
{
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
},
)
return response.data
} catch (error) {
return handleApiError(error)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

owner address is an Address (0x${address} for EVM chains) not an AccountId @coderabbitai

Comment on lines +64 to +86
const handleCreateCode = useCallback(async () => {
const code = newCodeInput.trim() || generateRandomCode()

try {
await createCode({ code })
setNewCodeInput('')
toast({
title: translate('referral.codeCreated'),
description: translate('referral.codeCreatedDescription', { code }),
status: 'success',
duration: 3000,
isClosable: true,
})
} catch (err) {
toast({
title: translate('common.error'),
description: err instanceof Error ? err.message : translate('referral.createCodeFailed'),
status: 'error',
duration: 5000,
isClosable: true,
})
}
}, [createCode, newCodeInput, toast, translate])
Copy link
Contributor

Choose a reason for hiding this comment

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

Absolutely valid.

Comment on lines +94 to +114
const handleShareOnX = useCallback((code: string) => {
const shareUrl = `${window.location.origin}/#/?ref=${code}`
const text = `Join me on ShapeShift using my referral code ${code}!`
const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(
text,
)}&url=${encodeURIComponent(shareUrl)}`
window.open(twitterUrl, '_blank', 'noopener,noreferrer')
}, [])

const handleCopyCode = useCallback(
(code: string) => {
const shareUrl = `${window.location.origin}/#/?ref=${code}`
navigator.clipboard.writeText(shareUrl)
toast({
title: translate('common.copied'),
status: 'success',
duration: 2000,
})
},
[toast, translate],
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Valid too but disregard (incl. aria-labels) - fine to also add since it's just one prompt, but irrelevant

Comment on lines +129 to +249
<Stack spacing={8}>
<Flex gap={4} flexWrap='wrap'>
<Card
color='white'
borderRadius='2xl'
overflow='hidden'
width='50%'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody px={6} py={4} display='flex' alignItems='center'>
<Flex alignItems='center' justifyContent='space-between' width='full'>
<Flex flexDirection='column' gap={0}>
<Text fontSize='md' opacity={0.7} mb={1}>
{translate('referral.yourReferralCode')}
</Text>
<Heading size='xl' fontWeight='bold' letterSpacing='wide'>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='120px' />
) : defaultCode ? (
defaultCode.code
) : (
'N/A'
)}
</Heading>
</Flex>
{defaultCode && (
<Flex alignItems='center' gap={2}>
<IconButton
aria-label='Share on X'
icon={<FaXTwitter />}
size='md'
colorScheme='whiteAlpha'
borderRadius='100%'
bg='whiteAlpha.200'
onClick={() => handleShareOnX(defaultCode.code)}
/>
<IconButton
aria-label='Copy link'
icon={<FaCopy />}
size='md'
colorScheme='whiteAlpha'
bg='whiteAlpha.200'
borderRadius='100%'
onClick={() => handleCopyCode(defaultCode.code)}
/>
</Flex>
)}
</Flex>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<Heading size='lg' fontWeight='bold' mb={2}>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='100px' mx='auto' />
) : (
`$${referralStats?.totalReferrerCommissionUsd ?? '0.00'}`
)}
</Heading>
<Text fontSize='md' color='text.subtle'>
{translate('referral.currentRewards')}
</Text>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<Heading size='lg' fontWeight='bold' mb={2}>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='100px' mx='auto' />
) : (
`$${referralStats?.totalFeesCollectedUsd ?? '0.00'}`
)}
</Heading>
<Text fontSize='md' color='text.subtle'>
{translate('referral.totalRewards')}
</Text>
</CardBody>
</Card>

<Card
flex='1'
minW='200px'
bg='background.surface.raised.base'
borderRadius='xl'
borderTop='1px solid'
borderColor='gray.700'
>
<CardBody textAlign='center' py={6}>
<HStack spacing={2} justify='center' mb={2}>
<Icon as={FaUser} boxSize={5} color='text.subtle' />
<Heading size='lg' fontWeight='bold'>
{isLoadingReferralStats ? (
<Skeleton height='40px' width='60px' />
) : (
referralStats?.totalReferrals ?? 0
)}
</Heading>
</HStack>
<Text fontSize='md' color='text.subtle'>
{translate('referral.totalReferred')}
</Text>
</CardBody>
</Card>
</Flex>
Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds sane - good reminder to test this on mobile viewports too

WebServices: boolean
AddressBook: boolean
AppRating: boolean
Referral: boolean
Copy link
Contributor

Choose a reason for hiding this comment

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

Eyy nice catch Mr Robot.

@NeOMakinG NeOMakinG marked this pull request as draft December 23, 2025 17:36
@NeOMakinG
Copy link
Collaborator Author

Switching to draft until we know how we will distribute amount, focusing on other priorities

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.

3 participants