Conversation
Adds initial POC implementation for Seeker wallet integration using MWA protocol.
Add Seeker wallet adapter for Solana Mobile support. Features: - Message-based communication interface for platform flexibility - Full Solana transaction signing and broadcasting - Compatible with ShapeShift's hdwallet interface - Designed for React Native WebView integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove old seeker.ts and solana.ts files - Simplify adapter implementation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Create @shapeshiftoss/hdwallet-seeker package - Implements HDWallet interface for Seeker Solana Mobile wallet - Uses message handler pattern for platform flexibility - Supports Solana transaction signing and broadcasting - Add proper TypeScript declarations and build setup - Reset dependencies to use standard versions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughA new package Changes
Sequence DiagramsequenceDiagram
participant App as Application
participant Wallet as SeekerHDWallet
participant Handler as SeekerMessageHandler
App->>Wallet: solanaSignTx(msg)
Wallet->>Wallet: solanaBuildTransaction()
Wallet->>Wallet: serialize to base64
Wallet->>Handler: signTransaction(transactionBase64)
Handler->>Handler: sign transaction
Handler-->>Wallet: serialized tx + signatures
Wallet-->>App: SolanaSignedTx
App->>Wallet: solanaSendTx(msg)
Wallet->>Wallet: solanaBuildTransaction()
Wallet->>Wallet: serialize to base64
Wallet->>Handler: signAndSendTransaction(transactionBase64)
Handler->>Handler: sign and broadcast
Handler-->>Wallet: signature
Wallet-->>App: SolanaTxSignature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 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 |
Reset all package versions and internal dependencies from 1.62.42-seeker-poc.1 to 1.62.41 to match master branch. This makes the PR diff cleaner and focused only on the Seeker integration changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The gitHead field is automatically added by npm/lerna during publish and should not be committed to the repository. This keeps the git history clean and avoids unnecessary diff noise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/hdwallet-seeker/src/adapter.ts`:
- Around line 103-105: getPublicKeys currently always returns a single PublicKey
regardless of the number of GetPublicKey requests, which breaks callers
expecting one result per request; update getPublicKeys to return an array
matching _msg.length (e.g., map over _msg and return { xpub: this.pubkey } for
each entry) or, if only single-key use is supported, validate and throw a clear
error when _msg.length !== 1; refer to getPublicKeys, GetPublicKey, PublicKey
and this.pubkey when making the change.
- Around line 184-196: In solanaSignTx, instead of returning the full serialized
transaction in the signatures array, deserialize result.signedTransaction (the
base64 signed transaction) back into a Transaction, extract each signature
(Transaction.signatures or equivalent) and convert each signature's .signature
bytes to base64 strings, then return signatures as an array of those
base64-encoded signature strings; update the return to use the serialized
signedTransaction for serialized and the extracted base64 signatures for
signatures, referencing solanaSignTx, solanaBuildTransaction,
messageHandler.signTransaction, and result.signedTransaction to locate the
change.
| getPublicKeys(_msg: GetPublicKey[]): Promise<(PublicKey | null)[] | null> { | ||
| return Promise.resolve([{ xpub: this.pubkey }]) | ||
| } |
There was a problem hiding this comment.
Align getPublicKeys output length with the request.
Line 103 returns a single element regardless of input size, which can break callers expecting one result per request. Either validate single-key usage or map to the input length.
✅ Suggested fix (mirror input length)
- getPublicKeys(_msg: GetPublicKey[]): Promise<(PublicKey | null)[] | null> {
- return Promise.resolve([{ xpub: this.pubkey }])
- }
+ getPublicKeys(msg: GetPublicKey[]): Promise<(PublicKey | null)[] | null> {
+ return Promise.resolve(msg.map(() => ({ xpub: this.pubkey })))
+ }📝 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.
| getPublicKeys(_msg: GetPublicKey[]): Promise<(PublicKey | null)[] | null> { | |
| return Promise.resolve([{ xpub: this.pubkey }]) | |
| } | |
| getPublicKeys(msg: GetPublicKey[]): Promise<(PublicKey | null)[] | null> { | |
| return Promise.resolve(msg.map(() => ({ xpub: this.pubkey }))) | |
| } |
🧰 Tools
🪛 ESLint
[error] 103-103: '_msg' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 104-104: Insert ;
(prettier/prettier)
🤖 Prompt for AI Agents
In `@packages/hdwallet-seeker/src/adapter.ts` around lines 103 - 105,
getPublicKeys currently always returns a single PublicKey regardless of the
number of GetPublicKey requests, which breaks callers expecting one result per
request; update getPublicKeys to return an array matching _msg.length (e.g., map
over _msg and return { xpub: this.pubkey } for each entry) or, if only
single-key use is supported, validate and throw a clear error when _msg.length
!== 1; refer to getPublicKeys, GetPublicKey, PublicKey and this.pubkey when
making the change.
| async solanaSignTx(msg: SolanaSignTx): Promise<SolanaSignedTx | null> { | ||
| const transaction = solanaBuildTransaction(msg, this.pubkey) | ||
| const serializedTx = Buffer.from(transaction.serialize()).toString('base64') | ||
|
|
||
| const result = await this.messageHandler.signTransaction(serializedTx) | ||
| if (!result.success || !result.signedTransaction) { | ||
| throw new Error(result.error ?? 'Failed to sign transaction') | ||
| } | ||
|
|
||
| return { | ||
| serialized: result.signedTransaction, | ||
| signatures: [result.signedTransaction], | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find SolanaSignedTx definition and examine its structure
rg -n "interface SolanaSignedTx|type SolanaSignedTx|class SolanaSignedTx" packages -g '*.ts' -A 5Repository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Find all solanaSignTx implementations
rg -n "solanaSignTx" packages -g '*.ts' -B 2 -A 15Repository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Check SolanaSignTx and related types in hdwallet-core
fd -e ts packages/hdwallet-core | head -20Repository: shapeshift/hdwallet
Length of output: 456
🏁 Script executed:
# List available packages
ls -la packages/ | head -20Repository: shapeshift/hdwallet
Length of output: 1368
🏁 Script executed:
# Search for SolanaSignedTx definition
rg "SolanaSignedTx" packages --type ts -B 2 -A 5Repository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Search for solanaSignTx method implementations
rg "solanaSignTx\s*\(" packages --type ts -B 1 -A 15Repository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Continue listing packages
ls -la packages/ | tail -10Repository: shapeshift/hdwallet
Length of output: 745
🏁 Script executed:
# Search for SolanaSignedTx without context flags
rg "SolanaSignedTx" packagesRepository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Look for seeker package
ls -la packages/ | grep seekerRepository: shapeshift/hdwallet
Length of output: 127
🏁 Script executed:
# Search for Solana-related types more broadly
rg "Solana" packages --type ts | grep -i "interface\|type\|class" | head -20Repository: shapeshift/hdwallet
Length of output: 45
🏁 Script executed:
# Look at the seeker adapter file mentioned in the review
cat packages/hdwallet-seeker/src/adapter.ts | head -220 | tail -50Repository: shapeshift/hdwallet
Length of output: 1659
🏁 Script executed:
# Check hdwallet-core for Solana types
find packages/hdwallet-core -name "*.ts" | xargs grep -l "Solana" | head -5Repository: shapeshift/hdwallet
Length of output: 172
🏁 Script executed:
# Check SolanaSignedTx type definition in hdwallet-core
cat packages/hdwallet-core/src/solana.ts | head -150Repository: shapeshift/hdwallet
Length of output: 4945
🏁 Script executed:
# Look for other solanaSignTx implementations
find packages -name "*.ts" -exec grep -l "solanaSignTx" {} \;Repository: shapeshift/hdwallet
Length of output: 583
🏁 Script executed:
# Check if there are any other Solana adapter implementations
ls -la packages/hdwallet-*/src/ | grep -i solanaRepository: shapeshift/hdwallet
Length of output: 482
🏁 Script executed:
# Check phantom solanaSignTx implementation
cat packages/hdwallet-phantom/src/solana.tsRepository: shapeshift/hdwallet
Length of output: 1096
🏁 Script executed:
# Check ledger solanaSignTx implementation
cat packages/hdwallet-ledger/src/solana.tsRepository: shapeshift/hdwallet
Length of output: 1876
🏁 Script executed:
# Check native solanaSignTx implementation
cat packages/hdwallet-native/src/solana.tsRepository: shapeshift/hdwallet
Length of output: 2589
🏁 Script executed:
# Check gridplus solanaSignTx implementation
cat packages/hdwallet-gridplus/src/solana.tsRepository: shapeshift/hdwallet
Length of output: 1977
🏁 Script executed:
# Check trezor solanaSignTx implementation
cat packages/hdwallet-trezor/src/solana.tsRepository: shapeshift/hdwallet
Length of output: 2918
signatures array should contain individual signature strings, not the full serialized transaction.
The current code returns [result.signedTransaction], but downstream Solana consumers expect signatures to contain the actual signature bytes (base64-encoded). This mirrors all other adapter implementations (Phantom, Ledger, Native, GridPlus, Trezor), which extract signatures from the signed transaction and convert to base64.
Deserialize the signed transaction and extract its signatures:
Suggested fix
+import { Transaction } from '@solana/web3.js'
async solanaSignTx(msg: SolanaSignTx): Promise<SolanaSignedTx | null> {
const transaction = solanaBuildTransaction(msg, this.pubkey)
const serializedTx = Buffer.from(transaction.serialize()).toString('base64')
const result = await this.messageHandler.signTransaction(serializedTx)
if (!result.success || !result.signedTransaction) {
throw new Error(result.error ?? 'Failed to sign transaction')
}
+ const signedTx = Transaction.from(Buffer.from(result.signedTransaction, 'base64'))
+ const signatures = signedTx.signatures
+ .map(s => (s.signature ? Buffer.from(s.signature).toString('base64') : null))
+ .filter((s): s is string => Boolean(s))
+
return {
serialized: result.signedTransaction,
- signatures: [result.signedTransaction],
+ signatures,
}
}📝 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.
| async solanaSignTx(msg: SolanaSignTx): Promise<SolanaSignedTx | null> { | |
| const transaction = solanaBuildTransaction(msg, this.pubkey) | |
| const serializedTx = Buffer.from(transaction.serialize()).toString('base64') | |
| const result = await this.messageHandler.signTransaction(serializedTx) | |
| if (!result.success || !result.signedTransaction) { | |
| throw new Error(result.error ?? 'Failed to sign transaction') | |
| } | |
| return { | |
| serialized: result.signedTransaction, | |
| signatures: [result.signedTransaction], | |
| } | |
| import { Transaction } from '@solana/web3.js' | |
| async solanaSignTx(msg: SolanaSignTx): Promise<SolanaSignedTx | null> { | |
| const transaction = solanaBuildTransaction(msg, this.pubkey) | |
| const serializedTx = Buffer.from(transaction.serialize()).toString("base64"); | |
| const result = await this.messageHandler.signTransaction(serializedTx) | |
| if (!result.success || !result.signedTransaction) { | |
| throw new Error(result.error ?? "Failed to sign transaction"); | |
| } | |
| const signedTx = Transaction.from(Buffer.from(result.signedTransaction, "base64")); | |
| const signatures = signedTx.signatures | |
| .map(s => (s.signature ? Buffer.from(s.signature).toString("base64") : null)) | |
| .filter((s): s is string => Boolean(s)) | |
| return { | |
| serialized: result.signedTransaction, | |
| signatures, | |
| }; | |
| } |
🧰 Tools
🪛 ESLint
[error] 185-185: Insert ;
(prettier/prettier)
[error] 186-186: Replace 'base64') with "base64");
(prettier/prettier)
[error] 188-188: Insert ;
(prettier/prettier)
[error] 190-190: Replace 'Failed·to·sign·transaction') with "Failed·to·sign·transaction");
(prettier/prettier)
[error] 196-196: Insert ;
(prettier/prettier)
🤖 Prompt for AI Agents
In `@packages/hdwallet-seeker/src/adapter.ts` around lines 184 - 196, In
solanaSignTx, instead of returning the full serialized transaction in the
signatures array, deserialize result.signedTransaction (the base64 signed
transaction) back into a Transaction, extract each signature
(Transaction.signatures or equivalent) and convert each signature's .signature
bytes to base64 strings, then return signatures as an array of those
base64-encoded signature strings; update the return to use the serialized
signedTransaction for serialized and the extracted base64 signatures for
signatures, referencing solanaSignTx, solanaBuildTransaction,
messageHandler.signTransaction, and result.signedTransaction to locate the
change.
Summary
Adds Seeker mobile wallet support to HDWallet, enabling Solana transaction signing through React Native WebView communication using the Solana Mobile Wallet Adapter (MWA) protocol.
Seeker is a Solana-focused mobile wallet that securely stores vault files (.skr) on the user's device. This integration allows web applications to connect to Seeker vaults via WebView postMessage, providing a hardware wallet-like experience for Solana users on mobile devices.
Changes
Core Types (
packages/hdwallet-core/src/seeker.ts)SeekerWalletInfo/SeekerWallet- Wallet interface for Seeker supportSeekerMessageHandlers- Platform-agnostic message handler interfaceSeekerAuthResponse- Authorization result with vault address and labelSeekerAddress- Address derivation interfaceSeekerSignTransactionRequest/SeekerSignTransactionResponse- Transaction signing interfacesseekerDescribePath- Path description for SeekerAdapter Package (
packages/hdwallet-seeker/)SeekerHDWallet- Main wallet adapter classHDWalletinterface from@shapeshiftoss/hdwallet-coreArchitecture
The Seeker adapter uses a message handler pattern to abstract platform-specific communication:
This design allows:
Seeker Specifications
Message Flow
window.ReactNativeWebView.postMessageTesting
yarn buildyarn lintyarn testIssue (if applicable)
N/A - New wallet adapter integration
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.