-
Notifications
You must be signed in to change notification settings - Fork 198
feat: seeker vault compatibility #11786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughThis PR adds complete Seeker hardware wallet integration to the application, including npm registry configuration, wallet dependencies, UI components for connection and failure flows, message handlers for wallet communication, routing setup, and integration with the existing wallet provider infrastructure. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant App as App<br/>(React)
participant Seeker as Seeker<br/>(Native)
participant Redux as Redux<br/>Store
participant LocalStorage as Local<br/>Storage
User->>App: Click "Connect Seeker"
App->>Seeker: checkSeekerAvailability()
Seeker-->>App: availability result
App->>App: Render SeekerConnect modal
User->>App: Click "Pair Device"
App->>Seeker: seekerAuthorize(cluster)
Seeker-->>App: authorization token + deviceId
App->>Seeker: Dynamically load SeekerHDWallet
Seeker-->>App: HDWallet instance
App->>App: Initialize wallet with<br/>messageHandlers
App->>Redux: dispatch(setWallet)
Redux-->>App: ✓ wallet set
App->>Redux: dispatch(markConnected)
Redux-->>App: ✓ connected
App->>Redux: dispatch(unlock)
Redux-->>App: ✓ unlocked
App->>LocalStorage: Save deviceId & wallet type
LocalStorage-->>App: ✓ persisted
App->>App: Close modal
App-->>User: ✓ Seeker wallet connected
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add Seeker wallet to hardware wallets section in mobile app - Conditional display based on Seeker vault availability detection - Display actual vault name (.skr) instead of generic "Seeker" - Direct connection without modal step for seamless UX - Add translations for Seeker wallet - Create Seeker routes, config, and message handlers - Integrate with @shapeshiftoss/hdwallet-seeker package - Clean up Butter swap changes (use Relay for swapping) - Restore original error handling and comments - Remove temporary Seeker adapter files - Reset verdaccio versions back to ^1.62.41 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
| npmAuthToken: "${NPM_AUTH-fallback}" | ||
|
|
||
| npmRegistryServer: "https://registry.npmjs.org" | ||
| npmRegistryServer: "http://127.0.0.1:4873" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert me after review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/context/WalletProvider/NewWalletViews/sections/HardwareWalletsSection.tsx (1)
70-159: Guard against concurrent Seeker connects.This async flow doesn't prevent overlapping auth attempts. Repeated taps launch concurrent authorization flows that can race wallet state updates. Add local in-flight guard with early return and disable the option while connecting.
🔧 Suggested fix
- const [isSeekerAvailable, setIsSeekerAvailable] = useState(false) + const [isSeekerAvailable, setIsSeekerAvailable] = useState(false) + const [isSeekerConnecting, setIsSeekerConnecting] = useState(false) @@ - const handleConnectSeeker = useCallback(async () => { + const handleConnectSeeker = useCallback(async () => { + if (isSeekerConnecting) return + setIsSeekerConnecting(true) try { const { SEEKER_DEFAULT_CLUSTER } = await import('@/context/WalletProvider/Seeker/config') @@ dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) } catch (error) { console.error('Error connecting to Seeker:', error) + } finally { + setIsSeekerConnecting(false) } - }, [dispatch, localWallet]) + }, [dispatch, isSeekerConnecting, localWallet])
🤖 Fix all issues with AI agents
In @.yarnrc.yml:
- Line 5: The .yarnrc.yml currently hardcodes npmRegistryServer:
"http://127.0.0.1:4873", which breaks CI and other contributors; change this to
read from an environment-driven override with the official registry as the
default by replacing the hardcoded value for the npmRegistryServer key with an
env interpolation (e.g., use process/env variable or Yarn-compatible ${ENV_VAR}
reference) that falls back to "https://registry.yarnpkg.com" (or
"https://registry.npmjs.org") so users can override the registry via an
environment variable without forcing localhost.
In `@package.json`:
- Around line 225-226: The package `@shapeshiftoss/hdwallet-seeker`@^1.62.42
cannot be found in the public registry; either pin it to the known working
version (^1.62.41) to match the other hdwallet packages or confirm the intended
source and add the registry configuration; update package.json to replace
"@shapeshiftoss/hdwallet-seeker": "^1.62.42" with the verified version (e.g.,
"^1.62.41") or, if the newer private version is intentional, document the
private registry and add the corresponding .npmrc/registry config and a short
comment in package.json/PR explaining why this package differs.
In `@src/context/WalletProvider/Seeker/components/Connect.tsx`:
- Around line 37-78: The auth-failure throw is hiding the translation set by
setErrorLoading and the catch types are overly broad; after calling
seekerAuthorize and detecting !authResult.success || !authResult.address, return
early (do not throw) so the existing
setErrorLoading('walletProvider.errors.walletNotFound') is preserved; in the
try/catch change the catch signature to catch (e: unknown) and when handling the
error use a safe extraction (e.g. inspect e for message or fallback) and
setErrorLoading('walletProvider.errors.walletNotFound') as the fallback
translation key, while still logging the raw error object (console.error) and
then navigate('/seeker/failure').
In `@src/context/WalletProvider/Seeker/components/Failure.tsx`:
- Around line 1-15: The SeekerFailure component lacks an explicit TypeScript
return type; update the declaration of SeekerFailure to include a return type
(e.g., annotate SeekerFailure as (): JSX.Element or : React.ReactElement) so the
function signature is explicit, leaving the implementation using ModalHeader and
ModalBody unchanged (reference symbol: SeekerFailure).
In `@src/context/WalletProvider/Seeker/config.ts`:
- Around line 1-12: SeekerConfig currently types SeekerConfigType as
Omit<SupportedWalletInfo<any>, 'routes'> which uses any; change it to use the
concrete SeekerHDWallet type to match other configs by replacing any with typeof
SeekerHDWallet (i.e., make SeekerConfigType = Omit<SupportedWalletInfo<typeof
SeekerHDWallet>, 'routes'>), and ensure the dynamic import in SeekerConfig’s
loadAdapter still returns SeekerHDWallet so the types line up with
SupportedWalletInfo and the central registry.
In `@src/context/WalletProvider/Seeker/seekerMessageHandlers.ts`:
- Around line 10-18: Replace all uses of `any` in the message contract types by
`unknown` (e.g., change type Message { cmd: string; [key: string]: any } to use
unknown for payloads and change MessageFromMobileApp.result: any to result:
unknown); then update the code path in `postMessage` to perform explicit runtime
narrowing/type-guarding before resolving or using fields (validate `cmd`, `id`,
and the shape of `result`, use type predicates or runtime checks and only cast
after validation). Ensure `Message`, `MessageFromMobileApp`, and `postMessage`
are updated consistently so no `any` leaks into the public API.
| npmAuthToken: "${NPM_AUTH-fallback}" | ||
|
|
||
| npmRegistryServer: "https://registry.npmjs.org" | ||
| npmRegistryServer: "http://127.0.0.1:4873" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid hardcoding a localhost npm registry.
Pointing to 127.0.0.1:4873 will break installs in CI and for contributors without a local registry. Prefer an env-driven override with the official registry as the default.
🛠️ Proposed fix
-npmRegistryServer: "http://127.0.0.1:4873"
+npmRegistryServer: "${NPM_REGISTRY_SERVER:-https://registry.npmjs.org}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| npmRegistryServer: "http://127.0.0.1:4873" | |
| npmRegistryServer: "${NPM_REGISTRY_SERVER:-https://registry.npmjs.org}" |
🤖 Prompt for AI Agents
In @.yarnrc.yml at line 5, The .yarnrc.yml currently hardcodes
npmRegistryServer: "http://127.0.0.1:4873", which breaks CI and other
contributors; change this to read from an environment-driven override with the
official registry as the default by replacing the hardcoded value for the
npmRegistryServer key with an env interpolation (e.g., use process/env variable
or Yarn-compatible ${ENV_VAR} reference) that falls back to
"https://registry.yarnpkg.com" (or "https://registry.npmjs.org") so users can
override the registry via an environment variable without forcing localhost.
| "wouter": "^3.6.0", | ||
| "@shapeshiftoss/hdwallet-seeker": "^1.62.42" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for all hdwallet packages in package.json to see their versions
rg "@shapeshiftoss/hdwallet" --type json -A 0 -B 0Repository: shapeshift/web
Length of output: 1573
🌐 Web query:
@shapeshiftoss/hdwallet-seeker npm registry version 1.62.42
💡 Result:
I couldn’t find any public npm registry listing for @shapeshiftoss/hdwallet-seeker (and therefore no evidence that version 1.62.42 exists/published). The ShapeShift maintainer profile that lists many @shapeshiftoss/hdwallet-* packages does not show hdwallet-seeker. [1]
If you have access to the registry you expect it on (public npm vs a private/proxy registry), the fastest verification is:
npm view `@shapeshiftoss/hdwallet-seeker` versions --json
# or (for that exact version)
npm view `@shapeshiftoss/hdwallet-seeker`@1.62.42 versionSource(s):
[1] (npmjs.com)
Citations:
@shapeshiftoss/hdwallet-seeker@1.62.42 cannot be verified in the public npm registry—this package may not exist or may require a private registry configuration.
All 18 other hdwallet packages are pinned to ^1.62.41, but this new dependency is at ^1.62.42. The web search found no public npm listing for @shapeshiftoss/hdwallet-seeker, and the version cannot be verified. Either this package is in a private registry (which requires configuration), it's a typo, or the version doesn't exist. Clarify the package source and ensure yarn install will succeed. If this is intentional, confirm it exists and explain why it differs from the rest of the hdwallet suite.
🤖 Prompt for AI Agents
In `@package.json` around lines 225 - 226, The package
`@shapeshiftoss/hdwallet-seeker`@^1.62.42 cannot be found in the public registry;
either pin it to the known working version (^1.62.41) to match the other
hdwallet packages or confirm the intended source and add the registry
configuration; update package.json to replace "@shapeshiftoss/hdwallet-seeker":
"^1.62.42" with the verified version (e.g., "^1.62.41") or, if the newer private
version is intentional, document the private registry and add the corresponding
.npmrc/registry config and a short comment in package.json/PR explaining why
this package differs.
| try { | ||
| const authResult = await seekerAuthorize(SEEKER_DEFAULT_CLUSTER) | ||
|
|
||
| if (!authResult.success || !authResult.address) { | ||
| setErrorLoading('walletProvider.errors.walletNotFound') | ||
| throw new Error('Seeker authorization failed or returned no address') | ||
| } | ||
|
|
||
| const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker') | ||
|
|
||
| const messageHandler = { | ||
| checkAvailability: checkSeekerAvailability, | ||
| authorize: seekerAuthorize, | ||
| deauthorize: seekerDeauthorize, | ||
| getAddress: seekerGetAddress, | ||
| getStatus: seekerGetStatus, | ||
| signTransaction: seekerSignTransaction, | ||
| signAndSendTransaction: seekerSignAndSendTransaction, | ||
| } | ||
|
|
||
| const deviceId = `seeker-${Date.now()}` | ||
| const wallet = new SeekerHDWallet(deviceId, authResult.address, messageHandler) | ||
|
|
||
| const { icon } = SeekerConfig | ||
| const name = authResult.label || SeekerConfig.name | ||
| await wallet.initialize() | ||
|
|
||
| dispatch({ | ||
| type: WalletActions.SET_WALLET, | ||
| payload: { wallet, name, icon, deviceId, connectedType: KeyManager.Seeker }, | ||
| }) | ||
| dispatch({ | ||
| type: WalletActions.SET_IS_CONNECTED, | ||
| payload: true, | ||
| }) | ||
| dispatch({ type: WalletActions.SET_IS_LOCKED, payload: false }) | ||
| localWallet.setLocalWallet({ type: KeyManager.Seeker, deviceId }) | ||
| dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) | ||
| } catch (e: any) { | ||
| console.error(e, 'Seeker Connect: There was an error initializing the wallet') | ||
| setErrorLoading(e.message) | ||
| navigate('/seeker/failure') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check for translation keys in main.json
rg -A 5 '"walletProvider"' src/assets/translations/en/main.json | head -50Repository: shapeshift/web
Length of output: 347
🏁 Script executed:
# Find all Connect.tsx files in WalletProvider to compare patterns
fd -n "Connect.tsx" src/context/WalletProvider/Repository: shapeshift/web
Length of output: 288
🏁 Script executed:
# Check the specific Seeker Connect.tsx file structure
cat -n src/context/WalletProvider/Seeker/components/Connect.tsx | head -100Repository: shapeshift/web
Length of output: 3846
🏁 Script executed:
# Check for generic error key
rg "walletProvider.*generic|errors.*generic" src/assets/translations/en/main.jsonRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Look at other Connect.tsx implementations for comparison
fd "Connect.tsx" src/context/WalletProvider/ --exec cat {} +Repository: shapeshift/web
Length of output: 38130
🏁 Script executed:
# Check for seeker-specific error keys
rg "walletProvider.*seeker" src/assets/translations/en/main.json -iRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Check what error keys exist for wallet providers in general
rg '"(walletProvider|errors)"' src/assets/translations/en/main.json | head -30Repository: shapeshift/web
Length of output: 373
🏁 Script executed:
# Check all available walletProvider.errors keys
rg '"walletProvider":\s*{\s*"errors"' src/assets/translations/en/main.json -A 10Repository: shapeshift/web
Length of output: 193
🏁 Script executed:
# Check if there's a generic error key elsewhere or what patterns other wallets use for unknown errors
rg 'walletProvider\.(.*?)\.errors\.(unknown|generic)' src/assets/translations/en/main.jsonRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Get the full walletProvider.errors section
rg -A 20 '"walletProvider": {' src/assets/translations/en/main.json | grep -A 15 '"errors"'Repository: shapeshift/web
Length of output: 159
🏁 Script executed:
# Search for all unknown error keys pattern
rg "\.errors\.unknown" src/assets/translations/en/main.jsonRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Read the translations file and find the walletProvider.errors section
cat src/assets/translations/en/main.json | jq '.walletProvider.errors' 2>/dev/null || head -100 src/assets/translations/en/main.json | grep -A 10 "walletProvider"Repository: shapeshift/web
Length of output: 281
🏁 Script executed:
# Search for unknown errors in all wallet providers
grep -r "unknown" src/assets/translations/en/main.jsonRepository: shapeshift/web
Length of output: 688
🏁 Script executed:
# Check if there are seeker-specific translations for errors
rg "seeker" src/assets/translations/en/main.json -iRepository: shapeshift/web
Length of output: 295
Use early return to preserve auth failure error and type catch as unknown.
The auth failure correctly sets walletProvider.errors.walletNotFound, but the throw funnels into the catch and overwrites it with e.message. Instead, return early after the auth failure check. For the catch block, type the error as unknown and use a valid translation key for the fallback—walletProvider.errors.generic does not exist in the translation file. Since Seeker has no wallet-specific error namespace, reuse walletProvider.errors.walletNotFound as the fallback when catching unexpected errors.
🛠️ Suggested fix
if (!authResult.success || !authResult.address) {
setErrorLoading('walletProvider.errors.walletNotFound')
+ navigate('/seeker/failure')
+ return
}
- const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker')
+ const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker')
@@
- } catch (e: any) {
+ } catch (e: unknown) {
console.error(e, 'Seeker Connect: There was an error initializing the wallet')
- setErrorLoading(e.message)
+ setErrorLoading(e instanceof Error ? e.message : 'walletProvider.errors.walletNotFound')
navigate('/seeker/failure')🤖 Prompt for AI Agents
In `@src/context/WalletProvider/Seeker/components/Connect.tsx` around lines 37 -
78, The auth-failure throw is hiding the translation set by setErrorLoading and
the catch types are overly broad; after calling seekerAuthorize and detecting
!authResult.success || !authResult.address, return early (do not throw) so the
existing setErrorLoading('walletProvider.errors.walletNotFound') is preserved;
in the try/catch change the catch signature to catch (e: unknown) and when
handling the error use a safe extraction (e.g. inspect e for message or
fallback) and setErrorLoading('walletProvider.errors.walletNotFound') as the
fallback translation key, while still logging the raw error object
(console.error) and then navigate('/seeker/failure').
| import { ModalBody, ModalHeader } from '@chakra-ui/react' | ||
| import { Text } from '@/components/Text' | ||
|
|
||
| export const SeekerFailure = () => { | ||
| return ( | ||
| <> | ||
| <ModalHeader> | ||
| <Text translation='walletProvider.seeker.failure.header' /> | ||
| </ModalHeader> | ||
| <ModalBody> | ||
| <Text translation='walletProvider.seeker.failure.body' /> | ||
| </ModalBody> | ||
| </> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check the SeekerFailure component file itself
cat -n src/context/WalletProvider/Seeker/components/Failure.tsxRepository: shapeshift/web
Length of output: 543
🏁 Script executed:
# Find where SeekerFailure is used/imported
rg -n "SeekerFailure" src/Repository: shapeshift/web
Length of output: 778
🏁 Script executed:
# Check for error boundaries in the wallet provider/routes structure
rg -n "ErrorBoundary" src/context/WalletProvider/Repository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Check the SeekerRoutes file structure
cat -n src/context/WalletProvider/Seeker/routes/SeekerRoutes.tsx 2>/dev/null || rg -n "." src/context/WalletProvider/Seeker/routes/ --type tsx --type tsRepository: shapeshift/web
Length of output: 85
🏁 Script executed:
# Search for ErrorBoundary in the routes file
rg -A 10 "SeekerRoutes" src/context/WalletProvider/NewWalletViews/routes/SeekerRoutes.tsx | head -40Repository: shapeshift/web
Length of output: 414
🏁 Script executed:
# Check if error boundaries exist anywhere in the wallet provider structure
fd "ErrorBoundary" src/context/WalletProvider/ 2>/dev/null || rg "ErrorBoundary|error.*boundary" src/context/WalletProvider/ -i --type ts --type tsx -lRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Check the full SeekerRoutes file structure
head -50 src/context/WalletProvider/NewWalletViews/routes/SeekerRoutes.tsxRepository: shapeshift/web
Length of output: 625
🏁 Script executed:
# Look at how seekerFailureElement is used in the routes
rg -B 5 -A 5 "seekerFailureElement" src/context/WalletProvider/NewWalletViews/routes/SeekerRoutes.tsxRepository: shapeshift/web
Length of output: 543
🏁 Script executed:
# Check if SeekerConnect has similar structure
cat -n src/context/WalletProvider/Seeker/components/Connect.tsx | head -20Repository: shapeshift/web
Length of output: 882
🏁 Script executed:
# Check if any other wallet Connect components have explicit return types
rg "export const.*Connect = \(\)" src/context/WalletProvider/ -A 2 | head -30Repository: shapeshift/web
Length of output: 2516
🏁 Script executed:
# Verify the coding guidelines requirement - check if this is enforced elsewhere
rg ": (JSX\.Element|React\.FC|React\.ReactNode)" src/context/WalletProvider/ --type tsx -c | head -10Repository: shapeshift/web
Length of output: 85
🏁 Script executed:
# Check if explicit return types are actually used in similar presentational components
rg "export const.*= \(\): (JSX\.Element|React\.FC)" src/context/WalletProvider/ --type ts -cRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Verify SeekerFailure is only a presentational component with no complex logic
cat -n src/context/WalletProvider/Seeker/components/Failure.tsxRepository: shapeshift/web
Length of output: 543
🏁 Script executed:
# Check if the parent route/modal wrapper has error handling
rg -B 10 -A 10 "seekerFailureElement\|/seeker/failure" src/context/WalletProvider/config.ts | head -50Repository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Check if SeekerConnect (similar component) also lacks explicit return type
head -10 src/context/WalletProvider/Seeker/components/Connect.tsx | grep -A 2 "export const"Repository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Verify the broader pattern - check random other presentational components
rg "export const.*= \(\)" src/components/ -A 1 | head -30Repository: shapeshift/web
Length of output: 2177
🏁 Script executed:
# Check if lazy loaded components have error boundaries at the config level
grep -B 5 -A 15 "SeekerFailure" src/context/WalletProvider/config.tsRepository: shapeshift/web
Length of output: 1280
Add explicit return type annotation.
The component should include an explicit return type following TypeScript guidelines.
Fix
-export const SeekerFailure = () => {
+export const SeekerFailure = (): JSX.Element => {🤖 Prompt for AI Agents
In `@src/context/WalletProvider/Seeker/components/Failure.tsx` around lines 1 -
15, The SeekerFailure component lacks an explicit TypeScript return type; update
the declaration of SeekerFailure to include a return type (e.g., annotate
SeekerFailure as (): JSX.Element or : React.ReactElement) so the function
signature is explicit, leaving the implementation using ModalHeader and
ModalBody unchanged (reference symbol: SeekerFailure).
| import type { SupportedWalletInfo } from '@/context/WalletProvider/config' | ||
| import { SeekerIcon } from '@/components/Icons/SeekerIcon' | ||
|
|
||
| type SeekerConfigType = Omit<SupportedWalletInfo<any>, 'routes'> | ||
|
|
||
| export const SeekerConfig: SeekerConfigType = { | ||
| adapters: [ | ||
| { | ||
| loadAdapter: async () => { | ||
| const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker') | ||
| return SeekerHDWallet as any | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check how other wallet configs type SupportedWalletInfo
rg -n "SupportedWalletInfo<" src/context/WalletProvider --max-count 20 -A 2 -B 1Repository: shapeshift/web
Length of output: 6633
🏁 Script executed:
# Check if other wallet config files exist and how they structure adapter loading
fd "config.ts" src/context/WalletProvider -x cat -n {}Repository: shapeshift/web
Length of output: 38459
🏁 Script executed:
# Search for loadAdapter patterns in other wallet configs
rg -n "loadAdapter" src/context/WalletProvider -g 'config.ts' -A 3 -B 1Repository: shapeshift/web
Length of output: 5687
Replace any in SeekerConfigType with typeof SeekerHDWallet to match other wallet configs.
Seeker is the only wallet config using SupportedWalletInfo<any>. All other wallet adapters (Coinbase, Keplr, Phantom, Trezor, MetaMask, WalletConnectV2, Vultisig, KeepKey, GridPlus, Native, Ledger) use explicit types like typeof AdapterName. Additionally, the central config registry at src/context/WalletProvider/config.ts:388 already declares the expected type as SupportedWalletInfo<typeof SeekerHDWallet>, so this is a straightforward consistency fix.
♻️ Suggested refactor
-import type { SupportedWalletInfo } from '@/context/WalletProvider/config'
+import type { SeekerHDWallet } from '@shapeshiftoss/hdwallet-seeker'
+import type { SupportedWalletInfo } from '@/context/WalletProvider/config'
@@
-type SeekerConfigType = Omit<SupportedWalletInfo<any>, 'routes'>
+type SeekerConfigType = Omit<SupportedWalletInfo<typeof SeekerHDWallet>, 'routes'>
@@
- return SeekerHDWallet as any
+ return SeekerHDWallet📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import type { SupportedWalletInfo } from '@/context/WalletProvider/config' | |
| import { SeekerIcon } from '@/components/Icons/SeekerIcon' | |
| type SeekerConfigType = Omit<SupportedWalletInfo<any>, 'routes'> | |
| export const SeekerConfig: SeekerConfigType = { | |
| adapters: [ | |
| { | |
| loadAdapter: async () => { | |
| const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker') | |
| return SeekerHDWallet as any | |
| }, | |
| import type { SeekerHDWallet } from '@shapeshiftoss/hdwallet-seeker' | |
| import type { SupportedWalletInfo } from '@/context/WalletProvider/config' | |
| import { SeekerIcon } from '@/components/Icons/SeekerIcon' | |
| type SeekerConfigType = Omit<SupportedWalletInfo<typeof SeekerHDWallet>, 'routes'> | |
| export const SeekerConfig: SeekerConfigType = { | |
| adapters: [ | |
| { | |
| loadAdapter: async () => { | |
| const { SeekerHDWallet } = await import('@shapeshiftoss/hdwallet-seeker') | |
| return SeekerHDWallet | |
| }, |
🤖 Prompt for AI Agents
In `@src/context/WalletProvider/Seeker/config.ts` around lines 1 - 12,
SeekerConfig currently types SeekerConfigType as Omit<SupportedWalletInfo<any>,
'routes'> which uses any; change it to use the concrete SeekerHDWallet type to
match other configs by replacing any with typeof SeekerHDWallet (i.e., make
SeekerConfigType = Omit<SupportedWalletInfo<typeof SeekerHDWallet>, 'routes'>),
and ensure the dynamic import in SeekerConfig’s loadAdapter still returns
SeekerHDWallet so the types line up with SupportedWalletInfo and the central
registry.
| type Message = { | ||
| cmd: string | ||
| [key: string]: any | ||
| } | ||
|
|
||
| type MessageFromMobileApp = { | ||
| id: string | ||
| result: any | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name "seekerMessageHandlers.ts" -o -name "mobileMessageHandlers.ts" | head -20Repository: shapeshift/web
Length of output: 185
🏁 Script executed:
cat -n src/context/WalletProvider/Seeker/seekerMessageHandlers.ts 2>/dev/null | head -50Repository: shapeshift/web
Length of output: 1791
🏁 Script executed:
cat -n src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts 2>/dev/null | head -50Repository: shapeshift/web
Length of output: 1515
Avoid any in message contracts.
any leaks type-safety into the public API. Use unknown and narrow inside postMessage before resolving.
🔧 Suggested update
type Message = {
cmd: string
- [key: string]: any
+ [key: string]: unknown
}
type MessageFromMobileApp = {
id: string
- result: any
+ result: unknown
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| type Message = { | |
| cmd: string | |
| [key: string]: any | |
| } | |
| type MessageFromMobileApp = { | |
| id: string | |
| result: any | |
| } | |
| type Message = { | |
| cmd: string | |
| [key: string]: unknown | |
| } | |
| type MessageFromMobileApp = { | |
| id: string | |
| result: unknown | |
| } |
🤖 Prompt for AI Agents
In `@src/context/WalletProvider/Seeker/seekerMessageHandlers.ts` around lines 10 -
18, Replace all uses of `any` in the message contract types by `unknown` (e.g.,
change type Message { cmd: string; [key: string]: any } to use unknown for
payloads and change MessageFromMobileApp.result: any to result: unknown); then
update the code path in `postMessage` to perform explicit runtime
narrowing/type-guarding before resolving or using fields (validate `cmd`, `id`,
and the shape of `result`, use type predicates or runtime checks and only cast
after validation). Ensure `Message`, `MessageFromMobileApp`, and `postMessage`
are updated consistently so no `any` leaks into the public API.
Description
Review me with shapeshift/hdwallet#786 and shapeshift/mobile-app#161
Integrates Seeker mobile wallet vault support into ShapeShift Web, enabling users to connect their Seeker mobile wallet vaults (.skr files) directly to the web application. Seeker is a Solana-focused mobile wallet that uses the Solana Mobile Wallet Adapter (MWA) protocol for secure communication between the mobile app and web interface via React Native WebView postMessage.
Key Features
Hardware Wallet Section on Mobile - Added hardware wallet support section to mobile wallet connection view, making Seeker and other hardware wallets accessible on mobile devices.
Direct Connection Flow - Seeker connection now launches instantly without modal navigation, providing a streamlined UX by directly invoking authorization when the user clicks the Seeker option.
Vault Name Display - The wallet connection now displays the actual Seeker vault name (.skr file label) instead of the generic "Seeker" label, giving users better identification of which vault they're connecting.
Platform Abstraction - Implemented message handler interface that abstracts Solana Mobile Wallet Adapter protocol behind a clean API, allowing the HDWallet adapter to work independently of the web environment.
Implementation Details
@shapeshiftoss/hdwallet-seekerpackage dependencyHardwareWalletsSection.tsxSeekerHDWalletin adapter type mappingsIssue (if applicable)
N/A - New feature integration
Risk
Medium - Introduces new wallet provider integration
This PR adds a new wallet connection method and introduces dependencies on the React Native WebView communication layer. While isolated to the Seeker wallet flow, it touches wallet provider infrastructure.
Seeker wallet connection and Solana transaction signing via Solana Mobile Wallet Adapter (MWA) protocol.
Testing
Engineering
Operations
Note: Testing requires Seeker mobile app with configured vault. Seeker availability detection will show/hide the option automatically based on WebView postMessage availability.
Screenshots (if applicable)
Quite hard to me to show my seeker as I don't have jam installed on it but:
yarn android --devicewith our mobile app🤖 Generated with Claude Code