Skip to content

Conversation

@gomesalexandre
Copy link
Contributor

@gomesalexandre gomesalexandre commented Sep 24, 2025

Description

Dog food is the best food 🐶

Many birds one stone:

  • Allows to set amount for receives
  • Allows to generate EIP-681 spec-compliant QR codes for EVM chains, both with and without an amount
    • Without an amount set, now generates 681-compliant QRs, adding the ethereum: prefix as well as chain_id param. EVM chains only, others unaffected, still just an addy string.
    • With an amount set:
      • for EVM chains, set the amount, either as value or uint256 depending on amount/token
      • for EVM chains, set the transfer() and target_address params if applicable for ERC-20 sends
      • for UTXO chains, adds the amount param
      • display said amount (+ fiat)
  • dogfeeds addition of BIP-21 scanning and generation capabilities for other chains as well (Thor, Maya, Cosmos), tested in app and Trust
  • dogfeeds addition of Solana Pay support i.e same scanning and generation capabilities. Flow is rougly the same as EVM chains with one smol diff for the default no amount case:
    • no amount is similar to UTXOs and other chains in that it just yields an address
    • amount set will add solana: prefix, amount, as well as spl-token param in case of SPL token receives

Also rage cleanups current address utils, which are inherently painful and bad atm, mixing concerns of URL, maybe input chainId and assetId, and URL parsing (which inherently needs no input, and should not have any).
Very long diff, but figured out might as well, as this will make things a lot more sane.
Now we have utils for address/vanity address and bip21 (including supersets) clearly extracted, each in their correct home. URL functionality also is extracted out of those utils, though kept them all not to make the diff even worse.

Issue (if applicable)

Risk

High Risk PRs Require 2 approvals

High by virtue of the refactor - but in reality low/medium: should make this saner (especially with added tests in this PR and the ones below) but...

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

Testing

Deployed to gome for ease of testing - point mobile app to gome, since you can't use QR code with Expo

E2E dogfeeding

  • Go to gome on mobile browser, or set prod app to gome
  • Generate QR codes for ETH, other native EVM assets, Ethereum tokens, other EVM chain tokens, SOL, Solana tokens, UTXOs, and well, any other chain for that matter
  • Ensure that with desktop app, you're able to scan those and the flow is noice
  • Ensure that the amount feature is working and that setting the amount on one reflects is on the other

Trust sanity check (bonus, not required but nice to test)

  • Generate QR codes with Trust mobile
    • non-EVM/Solana ones should be parsed similarly as the ones we now generate
    • EVM and Solana ones should work too, though the ones we generate in app should work nicer (EIP-681/Solana Pay vs. BIP-21, meaning app will include token contract address, which should ensure asset selection is automagically done, vs. trust QRs assuming native assets always)
  • Scan app QR codes with Trust
    • EVM/Solana ones should be parsed proper (with amount/token). ⚠️ Note: Trust may struggle to parse amount in proper unit e.g for SOL tokens, that's a Trust issue, not an us issue
    • UTXOs, RUNE, ATOM, MAYA should be parsed with amount if set

Phantom sanity check (bonus, not required but nice to test)

  • Scan app Solana QR codes with Phantom, Phantom should be happy scanning QR codes with/out amount (see below)
  • Generate Solana QR codes with Phantom, app should be happy scanning those (good ol' addies without anything else)

⚠️ Notes for Solana:

  • no wallet of all the major ones I have tested (Backpack/Phantom/Solflare) seem to support generating URLs with amount. With that being said, Phantom is very happy to scan those (i.e the ones we generate in app with amount) and probably others too
  • The magic is you can generate them in app - so do precisely that, generate them in app (SOL/tokens with amount) and test scanning them in app and Phantom. Dogfood baby! 🐶🥫

Engineering

  • ^

Operations

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

Screenshots (if applicable)

E2E dogfeeding

  • UTXO
image IMG_5518
  • RUNE
image IMG_5519
  • EVM - no amount/asset
image Screenshot 2025-09-25 at 18 46 56
  • EVM with amount/token
image IMG_5521
  • Solana - no amount/asset
image IMG_5522
  • Solana - SLP token with amount
image IMG_5523

Trust sanity check (bonus, not required but nice to test)

  • non-EVM/Solana ones should be parsed similarly as the ones we now generate
    image
    image

  • EVM and Solana ones should work too, though the ones we generate in app should work nicer (EIP-681/Solana Pay vs. BIP-21, meaning app will include token contract address, which should ensure asset selection is automagically done, vs. trust QRs assuming native assets always)

    image
image
  • Scan app QR codes with Trust

    • EVM/Solana ones should be parsed without amount or token info
    image IMG_5512 image image
    • UTXOs, RUNE, ATOM, MAYA should be parsed with amount if set
      image
      IMG_5514
      image
      IMG_5515

Phantom sanity check (bonus, not required but nice to test)

  • Scan app Solana QR codes with Phantom, Phantom should be happy scanning QR codes with/out amount (see below)
    image
Screenshot 2025-09-25 at 18 34 27
  • Generate QR codes with Phantom, app should be happy scanning those (good ol' addies without anything else)
image image

Summary by CodeRabbit

  • New Features

    • Set a receive amount (with fiat preview) and generate multi-chain QR/text.
  • QR & Send

    • Improved QR scanning and send flows: supports BIP-21, EIP-681 and Solana Pay to auto-fill address, asset/network, and amount.
  • Address parsing & validation

    • Centralized direct-URL parsing, vanity resolution, and chain-specific address validation.
  • Tests & Mocks

    • Expanded URL/address tests and richer mock assets/adapters.
  • UX/Copy

    • Added explanatory amount note on Receive screen.
  • Chores

    • Added @solana/pay dependency.

gomesalexandre and others added 30 commits September 22, 2025 19:15
Enhanced EIP-681 QR code support with smart asset validation and navigation.

- Replace security blanket with intelligent asset validation
- Add support for ERC-20 token QRs with proper asset lookup
- Implement smart step skipping based on available QR data
- Use viem utilities for proper hex/chain ID conversion
- Add chain pre-selection for all QR types
- Handle both token and native asset QRs appropriately

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…dation

- Replace blanket security blocking with intelligent asset validation
- Support MetaMask-style QR codes with proper token contract validation
- Add smart step skipping based on available QR data (asset, recipient, amount)
- Convert amounts from base units using asset precision
- Set default accountId for asset when QR is scanned
- Navigate to Details screen for review instead of skipping to Confirm
- Clean up debug logging statements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Restored helpful comments that explain control flow and error handling
in address parsing logic, including URL vs address error handling
and ChainId validation flow explanations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add console log for QR scanning debugging
- Add float amount detection using BigNumber.js to skip Trust's imprecise amounts
- Add comprehensive test coverage for 8 major wallets:
  - Trust Mobile: Stellar (unsupported), float detection, raw amounts
  - MetaMask Mobile: Full EIP-681 compliance, scientific notation, ERC-20
  - Rabby Mobile: Basic addresses, Zerion format
  - BASE Mobile: Simple ethereum: prefix
  - Ledger Live: Plain addresses (ETH + BTC)
  - Keplr: Chain ID parsing, unsupported schemas (Cosmos/Thor)
  - Phantom: Plain Solana addresses
  - Solflare: Plain Solana addresses
- Use proper chain ID constants (bscChainId) instead of hard-coded strings
- Randomize personal wallet addresses while keeping real contract addresses

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add Bitcoin BIP-21 with float amount test case
- Add plain DOGE address parsing test
- Add DOGE BIP-21 with amount test case
- Import dogeAssetId and dogeChainId constants

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove direct navigation to Details screen from QR code parsing to honor intentional ambiguity for safety
- Update test case names to be more descriptive and contextual
- Change "asset registry" terminology to "asset slice"

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ip ci]

- QR codes with both asset and amount should go to Details (amount confirmation screen)
- Preserve existing behavior for amount confirmation while maintaining safety

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Extract parseMaybeAmountCryptoPrecision to top-level with destructured args
- Add EVM float handling for non-compliant wallets (Trust, etc)
- Improve QR navigation logic to only show Select for ambiguous multi-token chains
- Clean up parseHexChainId by inlining usage and improve variable naming
- Update test to reflect new EVM float parsing behavior
- Replace let reassignments with const ternaries for cleaner code
- Add proper ChainReference typing instead of any casts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Base automatically changed from feat_bip21_full_support to develop September 25, 2025 14:31
gomesalexandre and others added 4 commits September 25, 2025 18:01
Merged latest changes from origin/develop while preserving
the BIP-21/ERC-681/Solana Pay functionality. Architecture
now uses single-responsibility modules:

- bip21.ts: Comprehensive URL parsing
- generateReceiveQrText.ts: QR code generation
- address.ts: Simplified address utilities
- Proper separation of concerns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gomesalexandre gomesalexandre marked this pull request as ready for review September 25, 2025 16:49
@gomesalexandre gomesalexandre requested a review from a team as a code owner September 25, 2025 16:49
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: 8

🧹 Nitpick comments (18)
src/lib/address/validation.ts (1)

5-16: Normalize input and short‑circuit on empty strings

Pre-trim and early-return for empty input to avoid adapter calls on blank values.

 export const validateAddress: ValidateAddressByChainId = async ({
   chainId,
   maybeAddress,
 }: ValidateAddressArgs): Promise<ValidateAddressReturn> => {
   try {
+    const candidate = (maybeAddress ?? '').trim()
+    if (!candidate) return false
     const adapter = getChainAdapterManager().get(chainId)
     if (!adapter) return false
-    return (await adapter.validateAddress(maybeAddress)).valid
+    return (await adapter.validateAddress(candidate)).valid
   } catch (e) {
     return false
   }
 }
src/components/Modals/Receive/ReceiveAmount.tsx (2)

27-33: Plumb isModal into content so desktop section can be gated

Pass isModal to ReceiveAmountContent and extend its props accordingly.

 type ReceiveAmountContentProps = {
   onClose: () => void
   symbol: string
   currentAmount?: string
   onConfirm: (amount: string | undefined) => void
+  isModal?: boolean
 }
 
 const ReceiveAmountContent = ({
   onClose,
   symbol,
   currentAmount,
   onConfirm,
+  isModal,
 }: ReceiveAmountContentProps) => {
   const content = useMemo(
     () => (
       <ReceiveAmountContent
         onClose={onClose}
         symbol={symbol}
         currentAmount={currentAmount}
         onConfirm={onConfirm}
+        isModal={isModal}
       />
     ),
-    [onClose, symbol, currentAmount, onConfirm],
+    [onClose, symbol, currentAmount, onConfirm, isModal],
   )

Also applies to: 34-39, 181-199


84-100: Disallow negative inputs in amount field

Amounts should not be negative.

               <NumberFormat
                 customInput={Input}
                 value={amountInput}
                 onValueChange={handleValueChange}
                 placeholder={`0 ${symbol.toUpperCase()}`}
                 data-test='receive-amount-input'
                 inputMode='decimal'
                 autoFocus
                 size='lg'
                 fontSize='4xl'
                 fontWeight='bold'
                 textAlign='center'
                 variant='unstyled'
                 border='none'
                 suffix={` ${symbol.toUpperCase()}`}
                 isNumericString
+                allowNegative={false}
               />
             <NumberFormat
               customInput={Input}
               value={amountInput}
               onValueChange={handleValueChange}
               placeholder={`0 ${symbol.toUpperCase()}`}
               data-test='receive-amount-input'
               inputMode='decimal'
               autoFocus
               size='lg'
               fontSize='xl'
               fontWeight='semibold'
               textAlign='center'
               variant='flushed'
               suffix={` ${symbol.toUpperCase()}`}
               isNumericString
+              allowNegative={false}
             />

Also applies to: 131-146

src/components/Modals/Send/Form.tsx (1)

230-275: Populate asset, to, and vanity fields when QR contains a payment URI or parsed address

For consistency with the QR modal flow and to reduce re-parsing downstream, set AssetId/To/VanityAddress along with Input.

-        const urlDirectResult = parseUrlDirect(decodedText)
-
-        const maybeUrlResult = await (() => {
+        const urlDirectResult = parseUrlDirect(decodedText)
+        const maybeUrlResult = await (() => {
           if (urlDirectResult)
             return {
               assetId: urlDirectResult.assetId,
               chainId: urlDirectResult.chainId,
               value: decodedText,
               amountCryptoPrecision: urlDirectResult.amountCryptoPrecision,
             }
           return parseAddress({ address: decodedText })
         })()
 
-        const address = urlDirectResult
-          ? urlDirectResult.maybeAddress
-          : (
-              await parseAddressInputWithChainId({
-                assetId: maybeUrlResult.assetId,
-                chainId: maybeUrlResult.chainId,
-                urlOrAddress: decodedText,
-              })
-            ).address
+        const parsed = urlDirectResult
+          ? { address: urlDirectResult.maybeAddress, vanityAddress: urlDirectResult.maybeAddress }
+          : await parseAddressInputWithChainId({
+              assetId: maybeUrlResult.assetId,
+              chainId: maybeUrlResult.chainId,
+              urlOrAddress: decodedText,
+            })
+        const address = parsed.address
+        const vanityAddress = parsed.vanityAddress ?? ''
 
         methods.setValue(SendFormFields.Input, address)
+        methods.setValue(SendFormFields.To, address)
+        methods.setValue(SendFormFields.VanityAddress, vanityAddress)
+        if (maybeUrlResult.assetId) {
+          methods.setValue(SendFormFields.AssetId, maybeUrlResult.assetId)
+        }
src/lib/address/bip21.test.ts (2)

203-209: Dangerous ETH URL error string coupling.

Asserting the thrown message to the i18n key couples tests to translation keys. Consider asserting an exported constant (e.g., DANGEROUS_ETH_URL_ERROR) to avoid brittle tests when i18n keys change.

Apply this diff:

-      ).toThrow('modals.send.errors.qrDangerousEthUrl')
+      ).toThrow(DANGEROUS_ETH_URL_ERROR)

Add to imports:

+import { DANGEROUS_ETH_URL_ERROR } from './constants'

347-356: Unsupported scheme fallback behavior.

Returning null for unknown schemes is fine for the dispatcher. Optional: add a test ensuring supported-but-malformed URLs (e.g., ethereum: with bad params) also return null vs throw, to lock in resilience.

src/components/Modals/Receive/ReceiveInfo.tsx (6)

180-188: Guard mobile detection fallback.

useBreakpointValue can return undefined on first render. Minor: coerce to boolean for clarity.

Apply this diff:

-const isMobile = useBreakpointValue({ base: true, md: false })
+const isMobile = Boolean(useBreakpointValue({ base: true, md: false }))

190-199: Reset amount UI state coherently.

If a user opens the modal, then switches breakpoints (orientation/resize), isSettingAmount and isAmountModalOpen can diverge. Consider resetting both states on close to avoid stale UI modes.

Apply this diff:

-const handleSetAmountClose = useCallback(() => {
-  setIsSettingAmount(false)
-  onAmountModalClose()
-}, [onAmountModalClose])
+const handleSetAmountClose = useCallback(() => {
+  setIsSettingAmount(false)
+  onAmountModalClose()
+}, [onAmountModalClose])

And add an effect to clear one when the other opens:

+useEffect(() => {
+  if (isAmountModalOpen) setIsSettingAmount(false)
+}, [isAmountModalOpen])

238-246: QR generation: consider defensive try/catch.

generateReceiveQrText can throw on invalid input. You guard receiveAddress, but a defensive try/catch returning '' would keep the QR from breaking the view if future changes add more validations.


96-101: Clear amount when account or asset changes.

Switching account/asset with a previously set amount keeps the old amount, which can be misleading. Consider resetting receiveAmount in the same effect that sets receiveAddress or when asset/accountId changes.

Apply this diff:

 useEffect(() => {
   ;(async () => {
     if (!accountMetadata) return
+    // Reset amount on account/asset change
+    setReceiveAmount(undefined)
     setIsAddressLoading(true)
     const selectedAccountAddress = await getReceiveAddress({

Also applies to: 103-131


133-136: Avoid setting state on unmounted component (ENS lookup).

Add an abort flag or ignore late resolves to prevent state updates after unmount or asset change.

Apply this diff:

 useEffect(() => {
-  if (asset.chainId !== KnownChainIds.EthereumMainnet || !receiveAddress) return
-  viemEthMainnetClient.getEnsName({ address: receiveAddress as Address }).then(setEnsName)
+  if (asset.chainId !== KnownChainIds.EthereumMainnet || !receiveAddress) return
+  let cancelled = false
+  viemEthMainnetClient.getEnsName({ address: receiveAddress as Address }).then(name => {
+    if (!cancelled) setEnsName(name)
+  })
+  return () => {
+    cancelled = true
+  }
 }, [asset.chainId, receiveAddress])

317-321: Quick win: pass a stable key to MiddleEllipsis.

Not required, but adding key={receiveAddress} can help React reconcile if the address flips quickly.

src/lib/address/generateReceiveQrText.ts (2)

38-40: Make Bitcoin Cash prefix removal case-insensitive.

Some wallets emit mixed-case prefixes. Safer to strip with a case-insensitive regex.

Apply this diff:

-      const cleanAddress = receiveAddress.replace('bitcoincash:', '')
+      const cleanAddress = receiveAddress.replace(/^bitcoincash:/i, '')

81-83: Rounding behavior for base units.

toBaseUnit defaults to HALF_UP. For on-chain amounts, rounding down avoids accidental over-sends. Consider passing a ROUND_DOWN roundingMode to toBaseUnit for EVM (and similar logic if expanded elsewhere).

Also applies to: 96-103

src/lib/address/bip21.ts (2)

96-105: Normalize scheme before decoding.

Ensures bip21.decode receives lowercase scheme.

-  const scheme = url.split(':')[0]
+  const scheme = url.split(':')[0]?.toLowerCase()

245-265: Don’t silently treat parser bugs as plain addresses without a log.

At least log parse failures (except the two explicit rethrows) to aid debugging.

Consider adding a debug log with the error message and url.

src/lib/address/vanityAddress.ts (1)

78-79: Remove unnecessary Address cast.

Return type is string; casting '' to Address is misleading.

-  return '' as Address
+  return ''
src/lib/address/address.ts (1)

37-39: Return a typed error or code (for i18n/UX).

Plain 'Address validation failed' lacks context and code.

Consider a custom error class or error code constant to align with app i18n handling.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee3269 and f079fab.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • package.json (1 hunks)
  • src/assets/translations/en/main.json (1 hunks)
  • src/components/Modals/FiatRamps/views/FiatForm.tsx (1 hunks)
  • src/components/Modals/QrCode/Form.tsx (2 hunks)
  • src/components/Modals/Receive/ReceiveAmount.tsx (1 hunks)
  • src/components/Modals/Receive/ReceiveInfo.tsx (11 hunks)
  • src/components/Modals/Send/Form.tsx (2 hunks)
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx (1 hunks)
  • src/components/Modals/Send/views/Address.tsx (1 hunks)
  • src/lib/address/address.test.ts (1 hunks)
  • src/lib/address/address.ts (1 hunks)
  • src/lib/address/bip21.test.ts (1 hunks)
  • src/lib/address/bip21.ts (1 hunks)
  • src/lib/address/constants.ts (1 hunks)
  • src/lib/address/ens.ts (1 hunks)
  • src/lib/address/generateReceiveQrText.test.ts (1 hunks)
  • src/lib/address/generateReceiveQrText.ts (1 hunks)
  • src/lib/address/types.ts (1 hunks)
  • src/lib/address/validation.ts (1 hunks)
  • src/lib/address/vanityAddress.ts (1 hunks)
  • src/pages/RFOX/components/AddressSelection.tsx (1 hunks)
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx (1 hunks)
  • src/test/mocks/assets.ts (3 hunks)
  • src/test/mocks/portfolio.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/lib/address/bip21.test.ts
  • src/components/Modals/Send/views/Address.tsx
  • src/lib/address/generateReceiveQrText.test.ts
  • src/test/mocks/portfolio.ts
  • src/lib/address/generateReceiveQrText.ts
  • src/components/Modals/FiatRamps/views/FiatForm.tsx
  • src/components/Modals/QrCode/Form.tsx
  • src/lib/address/address.ts
  • src/test/mocks/assets.ts
  • src/lib/address/bip21.ts
  • src/pages/RFOX/components/AddressSelection.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
  • src/lib/address/vanityAddress.ts
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx
  • src/lib/address/ens.ts
  • src/lib/address/address.test.ts
  • src/lib/address/validation.ts
  • src/components/Modals/Send/Form.tsx
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
  • src/lib/address/constants.ts
  • src/lib/address/types.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Address.tsx
  • src/components/Modals/FiatRamps/views/FiatForm.tsx
  • src/components/Modals/QrCode/Form.tsx
  • src/pages/RFOX/components/AddressSelection.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx
  • src/components/Modals/Send/Form.tsx
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/lib/address/bip21.test.ts
  • src/assets/translations/en/main.json
  • src/components/Modals/Send/views/Address.tsx
  • package.json
  • src/lib/address/generateReceiveQrText.test.ts
  • src/test/mocks/portfolio.ts
  • src/lib/address/generateReceiveQrText.ts
  • src/components/Modals/FiatRamps/views/FiatForm.tsx
  • src/components/Modals/QrCode/Form.tsx
  • src/lib/address/address.ts
  • src/test/mocks/assets.ts
  • src/lib/address/bip21.ts
  • src/pages/RFOX/components/AddressSelection.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
  • src/lib/address/vanityAddress.ts
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx
  • src/lib/address/ens.ts
  • src/lib/address/address.test.ts
  • src/lib/address/validation.ts
  • src/components/Modals/Send/Form.tsx
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
  • src/lib/address/constants.ts
  • src/lib/address/types.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Address.tsx
  • src/components/Modals/FiatRamps/views/FiatForm.tsx
  • src/components/Modals/QrCode/Form.tsx
  • src/pages/RFOX/components/AddressSelection.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx
  • src/components/Modals/Send/Form.tsx
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/lib/address/bip21.test.ts
  • src/components/Modals/Send/views/Address.tsx
  • src/lib/address/generateReceiveQrText.test.ts
  • src/test/mocks/portfolio.ts
  • src/lib/address/generateReceiveQrText.ts
  • src/components/Modals/FiatRamps/views/FiatForm.tsx
  • src/components/Modals/QrCode/Form.tsx
  • src/lib/address/address.ts
  • src/test/mocks/assets.ts
  • src/lib/address/bip21.ts
  • src/pages/RFOX/components/AddressSelection.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
  • src/lib/address/vanityAddress.ts
  • src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx
  • src/lib/address/ens.ts
  • src/lib/address/address.test.ts
  • src/lib/address/validation.ts
  • src/components/Modals/Send/Form.tsx
  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
  • src/lib/address/constants.ts
  • src/lib/address/types.ts
**/use*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/use*.{ts,tsx}: ALWAYS use use prefix for custom hooks
ALWAYS use descriptive names that indicate the hook's purpose
ALWAYS use camelCase after the use prefix for custom hooks

Files:

  • src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx
🧠 Learnings (14)
📓 Common learnings
Learnt from: gomesalexandre
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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: gomesalexandre
PR: shapeshift/web#10418
File: src/Routes/RoutesCommon.tsx:231-267
Timestamp: 2025-09-03T21:17:27.699Z
Learning: gomesalexandre prefers to keep PR diffs focused and reasonable in size, deferring tangential improvements (like Mixpanel privacy enhancements) to separate efforts rather than expanding the scope of feature PRs.
📚 Learning: 2025-08-08T20:16:12.898Z
Learnt from: gomesalexandre
PR: shapeshift/web#10222
File: package.json:202-202
Timestamp: 2025-08-08T20:16:12.898Z
Learning: In shapeshift/web, the semver package must be included in dependencies (not devDependencies) because hdwallet packages have transient dependencies that require semver but don't ship it themselves. This ensures semver is available at runtime for hdwallet functionality.

Applied to files:

  • package.json
📚 Learning: 2025-09-12T13:44:17.019Z
Learnt from: gomesalexandre
PR: shapeshift/web#10461
File: src/plugins/walletConnectToDapps/hooks/useSimulateEvmTransaction.ts:0-0
Timestamp: 2025-09-12T13:44:17.019Z
Learning: gomesalexandre prefers letting chain adapter errors throw naturally in useSimulateEvmTransaction rather than adding explicit error handling for missing adapters, consistent with his fail-fast approach and dismissal of defensive validation as "stale" in WalletConnect transaction simulation flows.

Applied to files:

  • src/test/mocks/portfolio.ts
📚 Learning: 2025-07-24T09:43:11.699Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-07-24T09:43:11.699Z
Learning: Applies to packages/swapper/src/swappers/*/{*.ts,endpoints.ts} : Verify chain ID filtering in filterAssetIdsBySellable and filterBuyAssetsBySellAssetId methods.

Applied to files:

  • src/test/mocks/portfolio.ts
  • src/test/mocks/assets.ts
📚 Learning: 2025-08-05T23:36:13.214Z
Learnt from: premiumjibles
PR: shapeshift/web#10187
File: src/state/slices/preferencesSlice/selectors.ts:21-25
Timestamp: 2025-08-05T23:36:13.214Z
Learning: The AssetId type from 'shapeshiftoss/caip' package is a string type alias, so it can be used directly as a return type for cache key resolvers in re-reselect selectors without needing explicit string conversion.

Applied to files:

  • src/test/mocks/portfolio.ts
  • src/test/mocks/assets.ts
  • src/lib/address/types.ts
📚 Learning: 2025-09-04T17:29:59.479Z
Learnt from: NeOMakinG
PR: shapeshift/web#10380
File: src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx:28-33
Timestamp: 2025-09-04T17:29:59.479Z
Learning: In shapeshift/web, the useGetPopularAssetsQuery function in src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx intentionally uses primaryAssets[assetId] instead of falling back to assets[assetId]. The design distributes primary assets across chains by iterating through their related assets and adding the primary asset to each related asset's chain. This ensures primary assets appear in all chains where they have related assets, supporting the grouped asset system.

Applied to files:

  • src/test/mocks/portfolio.ts
  • src/test/mocks/assets.ts
📚 Learning: 2025-08-17T21:53:03.806Z
Learnt from: 0xApotheosis
PR: shapeshift/web#10290
File: scripts/generateAssetData/color-map.json:41-47
Timestamp: 2025-08-17T21:53:03.806Z
Learning: In the ShapeShift web codebase, native assets (using CAIP-19 slip44 namespace like eip155:1/slip44:60, bip122:.../slip44:..., cosmos:.../slip44:...) are manually hardcoded and not generated via the automated asset generation script. Only ERC20/BEP20 tokens go through the asset generation process. The validation scripts should only validate generated assets, not manually added native assets.

Applied to files:

  • src/test/mocks/portfolio.ts
  • src/test/mocks/assets.ts
📚 Learning: 2025-07-24T09:43:11.699Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-07-24T09:43:11.699Z
Learning: Check registry entry in constants.ts during code review.

Applied to files:

  • src/lib/address/address.ts
📚 Learning: 2025-08-05T22:41:35.473Z
Learnt from: premiumjibles
PR: shapeshift/web#10187
File: src/pages/Assets/Asset.tsx:1-1
Timestamp: 2025-08-05T22:41:35.473Z
Learning: In the shapeshift/web codebase, component imports use direct file paths like '@/components/ComponentName/ComponentName' rather than barrel exports. The AssetAccountDetails component should be imported as '@/components/AssetAccountDetails/AssetAccountDetails', not from a directory index.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts expects a filter object with an assetId property, not a raw AssetId string. The selector signature is (state: ReduxState, filter) where filter should have an assetId property. This pattern is consistent across portfolio selectors that use selectAssetIdParamFromFilter. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts accepts a filter object with an assetId property (signature: (state, { assetId })), not a raw AssetId string. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts accepts a filter object with an assetId property, not a raw AssetId string. The selector signature is (state: ReduxState, filter) where filter is expected to have an assetId property. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-07-24T09:43:11.699Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-07-24T09:43:11.699Z
Learning: Applies to packages/swapper/**/*.ts : Use TypeScript with explicit types (e.g., SupportedChainIds) in all swapper-related files.

Applied to files:

  • src/lib/address/types.ts
🧬 Code graph analysis (17)
src/components/Modals/Receive/ReceiveAmount.tsx (1)
src/components/Text/Text.tsx (1)
  • Text (19-83)
src/lib/address/bip21.test.ts (4)
src/test/mocks/portfolio.ts (1)
  • mockChainAdapters (81-234)
src/lib/address/bip21.ts (1)
  • parseUrlDirect (245-265)
packages/caip/src/assetId/assetId.ts (1)
  • toAssetId (59-130)
src/lib/address/constants.ts (1)
  • EMPTY_ADDRESS_ERROR (45-45)
src/lib/address/generateReceiveQrText.test.ts (3)
src/lib/address/generateReceiveQrText.ts (1)
  • generateReceiveQrText (20-108)
src/test/mocks/assets.ts (19)
  • bitcoin (109-121)
  • dogecoin (221-233)
  • litecoin (235-247)
  • bitcoinCash (249-261)
  • ethereum (53-65)
  • arbitrum (123-135)
  • optimism (137-149)
  • polygon (151-163)
  • bsc (165-177)
  • gnosis (179-191)
  • base (193-205)
  • avalanche (207-219)
  • usdc (67-79)
  • fox (95-107)
  • thorchain (263-275)
  • cosmos (277-289)
  • mayachain (291-303)
  • solana (305-317)
  • wif (319-332)
src/lib/address/constants.ts (1)
  • EMPTY_ADDRESS_ERROR (45-45)
src/test/mocks/portfolio.ts (2)
packages/chain-adapters/src/utils/index.ts (1)
  • chainIdToChainLabel (20-72)
packages/caip/src/constants.ts (16)
  • btcAssetId (4-4)
  • cosmosAssetId (48-48)
  • ethAssetId (9-9)
  • avalancheAssetId (10-10)
  • optimismAssetId (11-11)
  • bscAssetId (12-12)
  • polygonAssetId (13-13)
  • arbitrumAssetId (15-15)
  • arbitrumNovaAssetId (16-16)
  • baseAssetId (17-17)
  • thorchainAssetId (49-49)
  • mayachainAssetId (52-52)
  • solAssetId (18-18)
  • gnosisAssetId (14-14)
  • dogeAssetId (6-6)
  • ltcAssetId (7-7)
src/lib/address/generateReceiveQrText.ts (5)
src/lib/address/constants.ts (2)
  • EMPTY_ADDRESS_ERROR (45-45)
  • CHAIN_ID_TO_URN_SCHEME (21-38)
packages/caip/src/constants.ts (1)
  • CHAIN_NAMESPACE (77-82)
packages/utils/src/index.ts (1)
  • isToken (28-41)
packages/caip/src/assetId/assetId.ts (1)
  • fromAssetId (143-178)
src/lib/math.ts (1)
  • toBaseUnit (26-34)
src/components/Modals/QrCode/Form.tsx (2)
src/lib/address/bip21.ts (1)
  • parseUrlDirect (245-265)
src/lib/address/address.ts (2)
  • parseAddress (12-39)
  • parseAddressInputWithChainId (42-70)
src/lib/address/address.ts (4)
src/lib/address/types.ts (1)
  • ParseAddressResult (84-89)
src/constants/chains.ts (1)
  • knownChainIds (9-20)
src/lib/address/validation.ts (1)
  • validateAddress (5-16)
packages/chain-adapters/src/evm/EvmBaseAdapter.ts (1)
  • validateAddress (575-579)
src/test/mocks/assets.ts (2)
packages/types/src/base.ts (1)
  • Asset (71-93)
packages/caip/src/constants.ts (28)
  • arbitrumChainId (66-66)
  • arbitrumAssetId (15-15)
  • optimismChainId (62-62)
  • optimismAssetId (11-11)
  • polygonChainId (64-64)
  • polygonAssetId (13-13)
  • bscChainId (63-63)
  • bscAssetId (12-12)
  • gnosisChainId (65-65)
  • gnosisAssetId (14-14)
  • baseChainId (68-68)
  • baseAssetId (17-17)
  • avalancheChainId (61-61)
  • avalancheAssetId (10-10)
  • dogeChainId (57-57)
  • dogeAssetId (6-6)
  • ltcChainId (58-58)
  • ltcAssetId (7-7)
  • bchChainId (56-56)
  • bchAssetId (5-5)
  • thorchainChainId (71-71)
  • thorchainAssetId (49-49)
  • cosmosChainId (70-70)
  • cosmosAssetId (48-48)
  • mayachainChainId (72-72)
  • mayachainAssetId (52-52)
  • solanaChainId (75-75)
  • solAssetId (18-18)
src/lib/address/bip21.ts (7)
src/lib/address/constants.ts (4)
  • CHAIN_ID_TO_URN_SCHEME (21-38)
  • URN_SCHEME_TO_CHAIN_ID (40-42)
  • EMPTY_ADDRESS_ERROR (45-45)
  • DANGEROUS_ETH_URL_ERROR (44-44)
src/lib/address/types.ts (1)
  • ParseUrlDirectResult (95-100)
packages/caip/src/assetId/assetId.ts (1)
  • toAssetId (59-130)
packages/caip/src/constants.ts (4)
  • solanaChainId (75-75)
  • ASSET_NAMESPACE (109-118)
  • ASSET_REFERENCE (120-139)
  • CHAIN_NAMESPACE (77-82)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectAssetById (16-20)
src/state/store.ts (1)
  • store (137-137)
src/lib/math.ts (1)
  • fromBaseUnit (10-24)
src/components/Modals/Receive/ReceiveInfo.tsx (6)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/marketDataSlice/selectors.ts (1)
  • selectMarketDataByAssetIdUserCurrency (66-72)
src/lib/address/generateReceiveQrText.ts (1)
  • generateReceiveQrText (20-108)
src/components/Modals/Receive/ReceiveAmount.tsx (1)
  • ReceiveAmount (181-210)
src/components/LogoQRCode/LogoQRCode.tsx (1)
  • LogoQRCode (18-44)
src/components/Modal/components/DialogFooter.tsx (1)
  • DialogFooter (9-23)
src/lib/address/vanityAddress.ts (3)
src/lib/address/types.ts (12)
  • VanityAddressValidatorsByChainId (15-17)
  • ValidateVanityAddress (11-13)
  • ValidateVanityAddressArgs (3-7)
  • ValidateVanityAddressReturn (9-9)
  • VanityAddressResolversByChainId (31-33)
  • ResolveVanityAddress (27-29)
  • ResolveVanityAddressArgs (19-23)
  • ResolveVanityAddressReturn (25-25)
  • ReverseResolversByChainId (46-48)
  • ReverseLookupVanityAddress (42-44)
  • ReverseLookupVanityAddressArgs (35-38)
  • ReverseLookupVanityAddressReturn (40-40)
packages/caip/src/constants.ts (1)
  • ethChainId (60-60)
src/lib/address/ens.ts (3)
  • validateEnsDomain (15-16)
  • resolveEnsDomain (12-12)
  • ensReverseLookupShim (42-45)
src/lib/address/ens.ts (1)
src/lib/address/types.ts (1)
  • ResolveVanityAddress (27-29)
src/lib/address/address.test.ts (3)
src/test/mocks/portfolio.ts (1)
  • mockChainAdapters (81-234)
src/lib/address/address.ts (2)
  • parseAddress (12-39)
  • parseAddressInputWithChainId (42-70)
packages/caip/src/constants.ts (6)
  • btcChainId (55-55)
  • btcAssetId (4-4)
  • ethChainId (60-60)
  • ethAssetId (9-9)
  • solanaChainId (75-75)
  • solAssetId (18-18)
src/lib/address/validation.ts (1)
src/lib/address/types.ts (3)
  • ValidateAddressByChainId (57-57)
  • ValidateAddressArgs (50-53)
  • ValidateAddressReturn (55-55)
src/components/Modals/Send/Form.tsx (2)
src/lib/address/bip21.ts (1)
  • parseUrlDirect (245-265)
src/lib/address/address.ts (2)
  • parseAddress (12-39)
  • parseAddressInputWithChainId (42-70)
src/lib/address/constants.ts (1)
packages/caip/src/constants.ts (16)
  • ethChainId (60-60)
  • arbitrumChainId (66-66)
  • optimismChainId (62-62)
  • polygonChainId (64-64)
  • bscChainId (63-63)
  • avalancheChainId (61-61)
  • baseChainId (68-68)
  • gnosisChainId (65-65)
  • btcChainId (55-55)
  • bchChainId (56-56)
  • dogeChainId (57-57)
  • ltcChainId (58-58)
  • thorchainChainId (71-71)
  • cosmosChainId (70-70)
  • mayachainChainId (72-72)
  • solanaChainId (75-75)
src/lib/address/types.ts (1)
packages/caip/src/assetId/assetId.ts (1)
  • AssetId (17-17)
🪛 Gitleaks (8.28.0)
src/lib/address/bip21.test.ts

[high] 270-270: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

src/lib/address/generateReceiveQrText.test.ts

[high] 395-395: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 407-407: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 419-419: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache
🔇 Additional comments (22)
package.json (1)

119-119: Solana Pay dependency addition makes sense for the new QR workflows.

This aligns with the new Solana Pay parsing/generation surfaces introduced in the PR.

src/components/Modals/Send/views/Address.tsx (1)

77-84: Good call on hard-disabling URL parsing here.

Explicitly flagging this as an address-only field prevents the legacy URL path from interfering with plain address validation.

src/lib/address/ens.ts (1)

12-12: Parameter rename keeps the ENS resolver consistent with the new type surface.

The destructuring now mirrors ResolveVanityAddressArgs, avoiding confusing aliasing.

src/test/mocks/assets.ts (1)

123-333: Expanded asset fixtures look great.

The broader cross-chain coverage (EVM L2s, UTXO, Cosmos, Solana) will make the new parsing tests much more representative.

src/lib/address/types.ts (1)

59-100: 👍 Nice to see the parsing and vanity types centralized.

These shared contracts will save a lot of duplication across the refactor.

src/pages/RFOX/components/AddressSelection.tsx (1)

31-31: Import swap fits the new validation module.

Thanks for wiring the consumer to the dedicated validation entry point.

src/pages/TCY/components/Claim/components/ClaimAddressInput.tsx (1)

13-13: Likewise here—thanks for pointing at the new validation module.

Keeps the shared address validation logic in one place.

src/components/Modals/FiatRamps/views/FiatForm.tsx (1)

15-15: Import moved to types module — OK

This matches the address/types public surface refactor.

src/components/Modals/Send/Form.tsx (1)

27-29: Imports updated for direct URL parsing — OK

Switch to parseUrlDirect and new address utils matches the refactor.

src/components/Modals/QrCode/Form.tsx (1)

21-23: Imports updated for direct URL parsing — OK

The switch to parseUrlDirect and the new address utils looks correct.

src/components/Modals/Send/hooks/useSendDetails/useSendDetails.test.tsx (1)

21-21: Approve: no legacy ResolveVanityAddressReturn imports remain No occurrences of ResolveVanityAddressReturn are still imported from @/lib/address/address.

src/lib/address/bip21.test.ts (2)

40-66: Solid, comprehensive coverage of parsing across schemes.

The scenarios exercise BIP‑21, ERC‑681, Solana Pay, Trust variants, and edge/error paths well. Dispatcher behavior and error propagation look correct.

Also applies to: 74-106, 108-147, 149-236, 238-293, 295-343, 345-366, 368-397


325-331: Known precision loss from bip21 decode (acknowledged).

The expected value includes a minor float precision artifact. This aligns with the prior note that bip21.decode emits this precision loss.

src/lib/address/address.test.ts (2)

19-43: parseAddress tests look good.

Happy path and failure path are covered; adapter lookup and default fee asset resolution are exercised.


66-105: parseAddressInputWithChainId behavior validated correctly.

Valid, invalid, and wrong‑chain cases are covered. Nice use of the adapter mock.

src/lib/address/generateReceiveQrText.test.ts (3)

188-227: Confirm eth-url-parser number formatting expectations.

Tests expect scientific notation in query params (e.g., value=1e18, uint256=1e8). Please confirm eth-url-parser.build produces this format from decimal strings returned by toBaseUnit. If not deterministic across environments, either:

  • normalize to exponential before build, or
  • relax assertions to accept decimal or exponential forms.

I can draft a helper to format base units consistently if you want.

Also applies to: 230-301


425-466: Zero and very small amounts behavior is consistent.

Good edge coverage for 0 and minimal base-unit conversions on EVM tokens.


393-397: Gitleaks false positives on public token/address strings.

The detector flags SPL mint/account strings as “Generic API Key”. These are public identifiers, not secrets. Consider allowlisting:

  • Solana base58 public keys and token mints
  • Common test addresses/mints used across the suite

I can propose a gitleaks allowlist regex for Solana/EVM addresses if helpful.

Also applies to: 405-409, 417-421

src/lib/address/generateReceiveQrText.ts (3)

25-27: Input validation is correct.

Throwing EMPTY_ADDRESS_ERROR here is appropriate; callers/tests assert this.


52-61: Solana Pay BigNumber compatibility.

You’re passing our BigNumber instance to @solana/pay, whose types require a different version. While the ts-expect-error suppresses typing, there’s a small runtime risk if the lib checks instance type. Consider passing amount as a string, or constructing a BigNumber using the same version as @solana/pay.

I can adjust this to use a local helper that returns a string amount for encodeURL if the API supports it.

Also applies to: 63-69


71-79: EIP‑681 build: ensure consistent numeric formatting.

If tests rely on scientific notation (e.g., 1e18), you may need to normalize amountBaseUnit to exponential before build, or adjust tests to accept decimal strings. Please confirm current eth-url-parser behavior.

Also applies to: 96-103

src/test/mocks/portfolio.ts (1)

201-207: No action required: solAssetId correctly matches the exported symbol

Prevents ModalHeader/Body/Footer from rendering without Modal context
when isModal=false, which would break layout and accessibility.

@coderabbitai resolve #10643 (comment)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f079fab and 6d487dc.

📒 Files selected for processing (1)
  • src/components/Modals/Receive/ReceiveAmount.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: gomesalexandre
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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: gomesalexandre
PR: shapeshift/web#10418
File: src/Routes/RoutesCommon.tsx:231-267
Timestamp: 2025-09-03T21:17:27.699Z
Learning: gomesalexandre prefers to keep PR diffs focused and reasonable in size, deferring tangential improvements (like Mixpanel privacy enhancements) to separate efforts rather than expanding the scope of feature PRs.
📚 Learning: 2025-09-12T12:00:33.924Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-12T12:00:33.924Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
🧬 Code graph analysis (1)
src/components/Modals/Receive/ReceiveAmount.tsx (1)
src/components/Text/Text.tsx (1)
  • Text (19-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache

gomesalexandre and others added 6 commits September 25, 2025 23:27
Replace flawed mock chainIdToChainLabel with real implementation from
@shapeshiftoss/chain-adapters. This fixes UTXO chain detection that was
using includes() on chainId strings instead of proper CAIP parsing.

Changes:
- Import chainIdToChainLabel from @shapeshiftoss/chain-adapters
- Remove broken mock implementation using includes() logic
- Rename validateBitcoinAddress to validateUtxoAddress for clarity
- Remove AI-generated comments

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Collaborator

@NeOMakinG NeOMakinG left a comment

Choose a reason for hiding this comment

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

Sol qr code gen on mobile, scanned on desktop:
image

Generated on desktop, scanned on mobile:
image
Worked fine on mobile

Eth:
image

ERC20 on arb:
image

Seems like working fine!

Spotted that the send form sucks on mobile, will try to improve that in a follow up

Happy to leave even more tests to OPS

@gomesalexandre
Copy link
Contributor Author

@NeOMakinG will merge and reply to/tackle comments as-needed in a follow-up given the bad boy this is 🙏🏽

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: 1

🧹 Nitpick comments (3)
src/components/Modals/Receive/ReceiveAmount.tsx (2)

98-105: Localize the close button aria-label

Use translated label for consistency with the rest of the component.

-            <IconButton
-              icon={closeIcon}
-              aria-label='Close'
+            <IconButton
+              icon={closeIcon}
+              aria-label={translate('common.close')}
               variant='ghost'
               size='sm'
               onClick={onClose}
             />

40-57: Optional: avoid native number input quirks

Native type="number" can block non-dot decimals and show spinners. NumberFormat already enforces numeric input; prefer text + inputMode.

       <Input
         size='lg'
         fontSize='65px'
         lineHeight='65px'
         fontWeight='bold'
         textAlign='center'
         border='none'
         borderRadius='lg'
-        type='number'
+        type='text'
         bg='transparent'
         variant='unstyled'
         color={props.value ? 'text.base' : 'text.subtle'}
         {...props}
       />
src/components/Modals/Receive/ReceiveInfo.tsx (1)

238-246: Harden QR text generation against unexpected errors

Even with the address check, generateReceiveQrText can throw for malformed asset/chain inputs. Wrap in try/catch to avoid breaking the receive screen.

-  const qrCodeText = useMemo(() => {
-    if (!receiveAddress) return ''
-
-    return generateReceiveQrText({
-      receiveAddress,
-      asset,
-      amountCryptoPrecision: receiveAmount,
-    })
-  }, [receiveAddress, asset, receiveAmount])
+  const qrCodeText = useMemo(() => {
+    if (!receiveAddress) return ''
+    try {
+      return generateReceiveQrText({
+        receiveAddress,
+        asset,
+        amountCryptoPrecision: receiveAmount,
+      })
+    } catch {
+      // Fallback to plain address if encoding fails
+      return receiveAddress
+    }
+  }, [receiveAddress, asset, receiveAmount])

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc62f9 and 7e9fc71.

📒 Files selected for processing (3)
  • src/components/Modals/Receive/ReceiveAmount.tsx (1 hunks)
  • src/components/Modals/Receive/ReceiveInfo.tsx (11 hunks)
  • src/components/Modals/Send/views/Confirm.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Confirm.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Confirm.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Confirm.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Confirm.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
  • src/components/Modals/Send/views/Confirm.tsx
  • src/components/Modals/Receive/ReceiveInfo.tsx
🧠 Learnings (17)
📓 Common learnings
Learnt from: gomesalexandre
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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
PR: shapeshift/web#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: gomesalexandre
PR: shapeshift/web#10418
File: src/Routes/RoutesCommon.tsx:231-267
Timestamp: 2025-09-03T21:17:27.699Z
Learning: gomesalexandre prefers to keep PR diffs focused and reasonable in size, deferring tangential improvements (like Mixpanel privacy enhancements) to separate efforts rather than expanding the scope of feature PRs.
📚 Learning: 2025-09-12T12:00:33.924Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-12T12:00:33.924Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-17T22:40:30.149Z
Learnt from: gomesalexandre
PR: shapeshift/web#10569
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/WalletConnectModalSigningFooter.tsx:121-129
Timestamp: 2025-09-17T22:40:30.149Z
Learning: gomesalexandre maintains strict scope discipline even for style/UI PRs in shapeshift/web, declining functionally correct UX improvements (like keeping Cancel button enabled during gas simulation loading) when they fall outside the PR's stated styling objectives, demonstrating his consistent pattern of deferring valid but tangential improvements to separate efforts.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-25T21:24:49.415Z
Learnt from: gomesalexandre
PR: shapeshift/web#10643
File: src/components/Modals/Receive/ReceiveAmount.tsx:64-172
Timestamp: 2025-09-25T21:24:49.415Z
Learning: gomesalexandre prefers to verify component behavior through actual testing and confirmation rather than theoretical analysis, as demonstrated when he tested desktop and mobile rendering scenarios for the ReceiveAmount component to confirm the UI works correctly.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-12T13:16:27.004Z
Learnt from: gomesalexandre
PR: shapeshift/web#10461
File: src/plugins/walletConnectToDapps/components/modals/EIP712MessageDisplay.tsx:21-24
Timestamp: 2025-09-12T13:16:27.004Z
Learning: gomesalexandre declined to add error boundaries to WalletConnect modals in PR #10461, stating "no error boundaries in this pr ser", consistent with his preference to keep PR scope focused and defer tangential improvements to separate efforts.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-10T15:35:46.223Z
Learnt from: gomesalexandre
PR: shapeshift/web#10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:55-55
Timestamp: 2025-09-10T15:35:46.223Z
Learning: gomesalexandre prefers fail-fast early returns over graceful degradation when critical data is missing in WalletConnect flows (like peer metadata in EIP155SignTypedDataConfirmation.tsx). He favors "safety first, always double-wrap" approach and believes missing peer metadata indicates bigger problems that should be surfaced explicitly rather than masked with partial UI rendering.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-13T16:45:17.166Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-08T22:00:48.005Z
Learnt from: gomesalexandre
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-09-03T21:17:27.699Z
Learnt from: gomesalexandre
PR: shapeshift/web#10418
File: src/Routes/RoutesCommon.tsx:231-267
Timestamp: 2025-09-03T21:17:27.699Z
Learning: gomesalexandre prefers to keep PR diffs focused and reasonable in size, deferring tangential improvements (like Mixpanel privacy enhancements) to separate efforts rather than expanding the scope of feature PRs.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-08-27T09:47:06.275Z
Learnt from: NeOMakinG
PR: shapeshift/web#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.

Applied to files:

  • src/components/Modals/Receive/ReceiveAmount.tsx
📚 Learning: 2025-08-05T22:41:35.473Z
Learnt from: premiumjibles
PR: shapeshift/web#10187
File: src/pages/Assets/Asset.tsx:1-1
Timestamp: 2025-08-05T22:41:35.473Z
Learning: In the shapeshift/web codebase, component imports use direct file paths like '@/components/ComponentName/ComponentName' rather than barrel exports. The AssetAccountDetails component should be imported as '@/components/AssetAccountDetails/AssetAccountDetails', not from a directory index.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts expects a filter object with an assetId property, not a raw AssetId string. The selector signature is (state: ReduxState, filter) where filter should have an assetId property. This pattern is consistent across portfolio selectors that use selectAssetIdParamFromFilter. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts accepts a filter object with an assetId property (signature: (state, { assetId })), not a raw AssetId string. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
📚 Learning: 2025-08-15T07:51:16.374Z
Learnt from: gomesalexandre
PR: shapeshift/web#10278
File: src/components/AssetHeader/hooks/useQuickBuy.ts:97-99
Timestamp: 2025-08-15T07:51:16.374Z
Learning: The selectPortfolioUserCurrencyBalanceByAssetId selector in src/state/slices/portfolioSlice/selectors.ts accepts a filter object with an assetId property, not a raw AssetId string. The selector signature is (state: ReduxState, filter) where filter is expected to have an assetId property. Passing a filter object like { assetId: someAssetId } is the correct usage pattern.

Applied to files:

  • src/components/Modals/Receive/ReceiveInfo.tsx
🧬 Code graph analysis (2)
src/components/Modals/Receive/ReceiveAmount.tsx (6)
src/components/MultiHopTrade/components/TradeAmountInput.tsx (1)
  • AmountInput (83-102)
src/hooks/useLocaleFormatter/useLocaleFormatter.ts (1)
  • useLocaleFormatter (120-280)
src/components/Modal/components/DialogHeader.tsx (1)
  • DialogHeader (15-48)
src/components/Text/Text.tsx (1)
  • Text (19-83)
src/state/slices/preferencesSlice/preferencesSlice.ts (1)
  • allowedDecimalSeparators (107-107)
src/components/Modal/components/DialogFooter.tsx (1)
  • DialogFooter (9-23)
src/components/Modals/Receive/ReceiveInfo.tsx (6)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/marketDataSlice/selectors.ts (1)
  • selectMarketDataByAssetIdUserCurrency (66-72)
src/lib/address/generateReceiveQrText.ts (1)
  • generateReceiveQrText (20-108)
src/components/Modals/Receive/ReceiveAmount.tsx (1)
  • ReceiveAmount (212-242)
src/components/LogoQRCode/LogoQRCode.tsx (1)
  • LogoQRCode (18-44)
src/components/Modal/components/DialogFooter.tsx (1)
  • DialogFooter (9-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Call / Static
🔇 Additional comments (4)
src/components/Modals/Send/views/Confirm.tsx (1)

187-195: Address row wrapping looks good

Allowing the row to wrap and tightening the font size when rendering a vanity address avoids overflow while keeping standard addresses readable. Nice touch.

src/components/Modals/Receive/ReceiveInfo.tsx (3)

180-189: Guard breakpoint value for SSR/first render

useBreakpointValue can return undefined initially; click during that window could open the desktop modal on mobile. Default it explicitly or set a fallback.

Option 1:

-  const isMobile = useBreakpointValue({ base: true, md: false })
+  const isMobile = useBreakpointValue({ base: true, md: false }) ?? false

Option 2: pass Chakra fallback option if available in your version:

-  const isMobile = useBreakpointValue({ base: true, md: false })
+  const isMobile = useBreakpointValue({ base: true, md: false }, { fallback: 'base' })

Please verify Chakra’s current API in this repo version.


430-438: Nice: desktop modal path wired separately from mobile inline flow

Clean split between inline (mobile) and modal (desktop) flows; props are consistent and reuse the same content.


207-213: Fiat calc memoization looks good

bnOrZero guards undefineds; dependency list is correct.

gomesalexandre and others added 2 commits September 26, 2025 17:31
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gomesalexandre gomesalexandre enabled auto-merge (squash) September 26, 2025 15:57
@gomesalexandre gomesalexandre merged commit 54858a1 into develop Sep 26, 2025
8 checks passed
@gomesalexandre gomesalexandre deleted the feat_bip21_erc681_full_circle branch September 26, 2025 15:57
@coderabbitai coderabbitai bot mentioned this pull request Dec 4, 2025
1 task
@coderabbitai coderabbitai bot mentioned this pull request Dec 30, 2025
1 task
@coderabbitai coderabbitai bot mentioned this pull request Jan 15, 2026
1 task
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.

Generate BIP-21/ERC-681 QR codes Solana QR codes parsing

3 participants