solana-hardware-wallet-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +35 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/STATUS.md +130 -0
- package/apps/demo-react-native/.watchmanconfig +1 -0
- package/apps/demo-react-native/Gemfile +9 -0
- package/apps/demo-react-native/README.md +111 -0
- package/apps/demo-react-native/android/app/build.gradle +118 -0
- package/apps/demo-react-native/android/app/debug.keystore +0 -0
- package/apps/demo-react-native/android/app/proguard-rules.pro +10 -0
- package/apps/demo-react-native/android/app/src/debug/AndroidManifest.xml +9 -0
- package/apps/demo-react-native/android/app/src/main/AndroidManifest.xml +36 -0
- package/apps/demo-react-native/android/app/src/main/java/com/solanahwwalletdemo/MainActivity.kt +22 -0
- package/apps/demo-react-native/android/app/src/main/java/com/solanahwwalletdemo/MainApplication.kt +43 -0
- package/apps/demo-react-native/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/demo-react-native/android/app/src/main/res/values/strings.xml +3 -0
- package/apps/demo-react-native/android/app/src/main/res/values/styles.xml +9 -0
- package/apps/demo-react-native/android/build.gradle +21 -0
- package/apps/demo-react-native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/demo-react-native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/demo-react-native/android/gradle.properties +41 -0
- package/apps/demo-react-native/android/gradlew +249 -0
- package/apps/demo-react-native/android/gradlew.bat +92 -0
- package/apps/demo-react-native/android/settings.gradle +4 -0
- package/apps/demo-react-native/app.json +4 -0
- package/apps/demo-react-native/babel.config.js +3 -0
- package/apps/demo-react-native/index.js +9 -0
- package/apps/demo-react-native/ios/.xcode.env +11 -0
- package/apps/demo-react-native/ios/Podfile +40 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/AppDelegate.h +6 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/AppDelegate.mm +31 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/Images.xcassets/Contents.json +6 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/Info.plist +60 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/LaunchScreen.storyboard +47 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/PrivacyInfo.xcprivacy +38 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo/main.m +10 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo.xcodeproj/project.pbxproj +688 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemo.xcodeproj/xcshareddata/xcschemes/SolanaHwWalletDemo.xcscheme +88 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemoTests/Info.plist +24 -0
- package/apps/demo-react-native/ios/SolanaHwWalletDemoTests/SolanaHwWalletDemoTests.m +66 -0
- package/apps/demo-react-native/metro.config.js +48 -0
- package/apps/demo-react-native/package.json +41 -0
- package/apps/demo-react-native/src/App.tsx +65 -0
- package/apps/demo-react-native/src/screens/WalletScreen.tsx +252 -0
- package/apps/demo-react-native/tsconfig.json +16 -0
- package/apps/examples-node/README.md +61 -0
- package/apps/examples-node/package.json +29 -0
- package/apps/examples-node/src/derive-accounts.ts +64 -0
- package/apps/examples-node/src/sign-message.ts +67 -0
- package/apps/examples-node/src/sign-transaction.ts +96 -0
- package/apps/examples-node/tsconfig.json +8 -0
- package/docs/architecture/adapter-pattern.md +539 -0
- package/docs/architecture/overview.md +412 -0
- package/docs/wallets/keystone.md +220 -0
- package/docs/wallets/ledger.md +158 -0
- package/docs/wallets/safepal.md +212 -0
- package/docs/wallets/trezor.md +179 -0
- package/eslint.config.mjs +18 -0
- package/package.json +34 -0
- package/packages/adapter-keystone/package.json +40 -0
- package/packages/adapter-keystone/src/adapter.ts +328 -0
- package/packages/adapter-keystone/src/index.ts +2 -0
- package/packages/adapter-keystone/src/ur-codec.ts +85 -0
- package/packages/adapter-keystone/tsconfig.json +8 -0
- package/packages/adapter-keystone/tsup.config.ts +9 -0
- package/packages/adapter-ledger/package.json +53 -0
- package/packages/adapter-ledger/src/adapter.ts +358 -0
- package/packages/adapter-ledger/src/index.ts +2 -0
- package/packages/adapter-ledger/tsconfig.json +8 -0
- package/packages/adapter-ledger/tsup.config.ts +9 -0
- package/packages/adapter-safepal/package.json +38 -0
- package/packages/adapter-safepal/src/adapter.ts +278 -0
- package/packages/adapter-safepal/src/index.ts +2 -0
- package/packages/adapter-safepal/tsconfig.json +8 -0
- package/packages/adapter-safepal/tsup.config.ts +9 -0
- package/packages/adapter-trezor/package.json +39 -0
- package/packages/adapter-trezor/src/adapter.ts +331 -0
- package/packages/adapter-trezor/src/index.ts +2 -0
- package/packages/adapter-trezor/tsconfig.json +8 -0
- package/packages/adapter-trezor/tsup.config.ts +9 -0
- package/packages/core/package.json +33 -0
- package/packages/core/src/__tests__/errors.test.ts +70 -0
- package/packages/core/src/__tests__/sdk.test.ts +242 -0
- package/packages/core/src/adapter.ts +155 -0
- package/packages/core/src/errors.ts +115 -0
- package/packages/core/src/index.ts +38 -0
- package/packages/core/src/sdk.ts +271 -0
- package/packages/core/tsconfig.json +8 -0
- package/packages/core/tsup.config.ts +9 -0
- package/packages/react-native/package.json +44 -0
- package/packages/react-native/src/context.ts +19 -0
- package/packages/react-native/src/hooks.ts +393 -0
- package/packages/react-native/src/index.ts +38 -0
- package/packages/react-native/src/provider.tsx +31 -0
- package/packages/react-native/tsconfig.json +9 -0
- package/packages/react-native/tsup.config.ts +10 -0
- package/packages/shared/package.json +32 -0
- package/packages/shared/src/index.ts +188 -0
- package/packages/shared/tsconfig.json +8 -0
- package/packages/shared/tsup.config.ts +9 -0
- package/packages/solana/package.json +34 -0
- package/packages/solana/src/__tests__/derivation.test.ts +102 -0
- package/packages/solana/src/derivation.ts +98 -0
- package/packages/solana/src/index.ts +17 -0
- package/packages/solana/src/transaction.ts +63 -0
- package/packages/solana/tsconfig.json +8 -0
- package/packages/solana/tsup.config.ts +9 -0
- package/packages/test-utils/package.json +37 -0
- package/packages/test-utils/src/__tests__/mock-adapter.test.ts +75 -0
- package/packages/test-utils/src/index.ts +3 -0
- package/packages/test-utils/src/mock-adapter.ts +207 -0
- package/packages/test-utils/src/test-sdk.ts +13 -0
- package/packages/test-utils/tsconfig.json +8 -0
- package/packages/test-utils/tsup.config.ts +9 -0
- package/packages/transports/package.json +35 -0
- package/packages/transports/src/index.ts +163 -0
- package/packages/transports/tsconfig.json +8 -0
- package/packages/transports/tsup.config.ts +9 -0
- package/pnpm-workspace.yaml +3 -0
- package/tsconfig.base.json +23 -0
- package/turbo.json +25 -0
- package/vitest.workspace.ts +6 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(pnpm --version)",
|
|
5
|
+
"Bash(npm --version)",
|
|
6
|
+
"Bash(git init:*)",
|
|
7
|
+
"Bash(git remote:*)",
|
|
8
|
+
"Bash(pnpm install:*)",
|
|
9
|
+
"Bash(pnpm:*)",
|
|
10
|
+
"Bash(for pkg:*)",
|
|
11
|
+
"Bash(do)",
|
|
12
|
+
"Read(//Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/$pkg/**)",
|
|
13
|
+
"Bash(\"$pkg/package.json.tmp\")",
|
|
14
|
+
"Bash(mv \"$pkg/package.json.tmp\" \"$pkg/package.json\")",
|
|
15
|
+
"Bash(git add:*)",
|
|
16
|
+
"Bash(git commit:*)",
|
|
17
|
+
"Bash(git push:*)",
|
|
18
|
+
"Bash(xargs ls:*)",
|
|
19
|
+
"Read(//private/tmp/**)",
|
|
20
|
+
"Bash(rm -rf SolanaHwWalletDemo)",
|
|
21
|
+
"Bash(npx --yes @react-native-community/cli init SolanaHwWalletDemo --version 0.74.0 --skip-install --skip-git-init)",
|
|
22
|
+
"Bash(DEMO_DIR=/Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/apps/demo-react-native)",
|
|
23
|
+
"Bash(cp -R /tmp/SolanaHwWalletDemo/ios \"$DEMO_DIR/\")",
|
|
24
|
+
"Bash(cp -R /tmp/SolanaHwWalletDemo/android \"$DEMO_DIR/\")",
|
|
25
|
+
"Bash(cp /tmp/SolanaHwWalletDemo/.watchmanconfig \"$DEMO_DIR/\")",
|
|
26
|
+
"Bash(cp /tmp/SolanaHwWalletDemo/Gemfile \"$DEMO_DIR/\")",
|
|
27
|
+
"Bash(ls /Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/apps/demo-react-native/*.md /Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/apps/examples-node/*.md)",
|
|
28
|
+
"Bash(ls -la /Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/packages/adapter-*/src/)",
|
|
29
|
+
"Bash(ls -la /Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/packages/*/src/index.ts)",
|
|
30
|
+
"Bash(find /Users/mihailshumilov/sites/my/hackathones/react-native-sdk-for-solana-hardware-wallet/packages/*/dist -type f -name \"*.d.ts\")",
|
|
31
|
+
"Bash(done)",
|
|
32
|
+
"Bash(wc:*)"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Solana Hardware Wallet SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Solana Hardware Wallet SDK for React Native
|
|
2
|
+
|
|
3
|
+
A unified, open-source React Native SDK that enables Solana dApps to connect to and interact with multiple hardware wallets through one consistent API.
|
|
4
|
+
|
|
5
|
+
## Supported Wallets
|
|
6
|
+
|
|
7
|
+
| Wallet | Transport | Status | Sign Tx | Sign Msg | Multi-Account |
|
|
8
|
+
|--------|-----------|--------|---------|----------|---------------|
|
|
9
|
+
| Ledger | BLE, USB | **Full** | Yes | Yes (>= v1.3.0) | Yes |
|
|
10
|
+
| Keystone | QR | **Full** | Yes | Yes | Yes |
|
|
11
|
+
| Trezor | USB | **Full** | Yes | No* | Yes |
|
|
12
|
+
| SafePal | QR | **Partial** | Partial** | No | Yes |
|
|
13
|
+
|
|
14
|
+
\* Trezor Solana message signing is limited to certain models/firmware. Transaction signing works fully.
|
|
15
|
+
\** SafePal QR protocol is proprietary and not publicly documented. Adapter contract is complete but encoding/decoding needs protocol specs.
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
graph TB
|
|
21
|
+
App["React Native App"] --> RN["@solana-hw-wallet/react-native"]
|
|
22
|
+
App --> SDK["@solana-hw-wallet/core"]
|
|
23
|
+
RN --> SDK
|
|
24
|
+
SDK --> AL["adapter-ledger"]
|
|
25
|
+
SDK --> AK["adapter-keystone"]
|
|
26
|
+
SDK --> AT["adapter-trezor"]
|
|
27
|
+
SDK --> AS["adapter-safepal"]
|
|
28
|
+
AL --> SOL["@solana-hw-wallet/solana"]
|
|
29
|
+
AK --> SOL
|
|
30
|
+
AT --> SOL
|
|
31
|
+
AS --> SOL
|
|
32
|
+
AL --> TR["@solana-hw-wallet/transports"]
|
|
33
|
+
AK --> TR
|
|
34
|
+
SDK --> SH["@solana-hw-wallet/shared"]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Design Principles
|
|
38
|
+
|
|
39
|
+
- **Adapter pattern**: Each wallet is a pluggable adapter behind a shared `HardwareWalletAdapter` interface
|
|
40
|
+
- **Transport-agnostic**: Public SDK API doesn't expose transport details
|
|
41
|
+
- **Strongly typed**: Full TypeScript with strict mode
|
|
42
|
+
- **QR-first for air-gapped wallets**: Extended `QrWalletAdapter` interface for Keystone/SafePal
|
|
43
|
+
- **Testable**: Mock adapter included for testing without hardware
|
|
44
|
+
|
|
45
|
+
## Monorepo Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
/packages
|
|
49
|
+
/shared # Shared types, enums, event emitter
|
|
50
|
+
/core # Adapter interfaces, SDK class, errors
|
|
51
|
+
/solana # Derivation paths, transaction helpers
|
|
52
|
+
/transports # Transport abstractions (BLE, USB, QR, NFC)
|
|
53
|
+
/adapter-ledger # Ledger wallet adapter
|
|
54
|
+
/adapter-keystone # Keystone wallet adapter (QR)
|
|
55
|
+
/adapter-trezor # Trezor wallet adapter
|
|
56
|
+
/adapter-safepal # SafePal wallet adapter (QR, partial)
|
|
57
|
+
/react-native # React Native hooks & provider
|
|
58
|
+
/test-utils # Mock adapter for testing
|
|
59
|
+
|
|
60
|
+
/apps
|
|
61
|
+
/demo-react-native # Demo React Native app
|
|
62
|
+
/examples-node # Node.js example scripts
|
|
63
|
+
|
|
64
|
+
/docs
|
|
65
|
+
/architecture # Architecture documentation
|
|
66
|
+
/wallets # Per-wallet documentation
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Getting Started
|
|
70
|
+
|
|
71
|
+
### Prerequisites
|
|
72
|
+
|
|
73
|
+
- Node.js >= 18
|
|
74
|
+
- pnpm >= 9
|
|
75
|
+
- React Native >= 0.72 (bare, not Expo — BLE/USB/NFC require native modules)
|
|
76
|
+
|
|
77
|
+
### Installation
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Install core SDK
|
|
81
|
+
npm install @solana-hw-wallet/core @solana-hw-wallet/solana
|
|
82
|
+
|
|
83
|
+
# Install React Native hooks
|
|
84
|
+
npm install @solana-hw-wallet/react-native
|
|
85
|
+
|
|
86
|
+
# Install wallet adapters you need
|
|
87
|
+
npm install @solana-hw-wallet/adapter-ledger
|
|
88
|
+
npm install @solana-hw-wallet/adapter-keystone
|
|
89
|
+
npm install @solana-hw-wallet/adapter-trezor
|
|
90
|
+
npm install @solana-hw-wallet/adapter-safepal
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Development Setup
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git clone https://github.com/mihailShumilov/react-native-sdk-for-solana-hardware-wallet.git
|
|
97
|
+
cd react-native-sdk-for-solana-hardware-wallet
|
|
98
|
+
pnpm install
|
|
99
|
+
pnpm build
|
|
100
|
+
pnpm test
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Run Node.js Examples
|
|
104
|
+
|
|
105
|
+
These use a mock adapter so no hardware device is required:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Derive accounts
|
|
109
|
+
pnpm --filter @solana-hw-wallet/examples-node example:derive
|
|
110
|
+
|
|
111
|
+
# Sign a message
|
|
112
|
+
pnpm --filter @solana-hw-wallet/examples-node example:sign-message
|
|
113
|
+
|
|
114
|
+
# Sign a transaction
|
|
115
|
+
pnpm --filter @solana-hw-wallet/examples-node example:sign-transaction
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Run React Native Demo App
|
|
119
|
+
|
|
120
|
+
**Prerequisites:**
|
|
121
|
+
- Xcode (iOS) or Android Studio (Android)
|
|
122
|
+
- CocoaPods (`gem install cocoapods`)
|
|
123
|
+
- A physical device is recommended (BLE doesn't work in simulators)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# 1. Build all SDK packages first
|
|
127
|
+
pnpm build
|
|
128
|
+
|
|
129
|
+
# 2. Install iOS native dependencies
|
|
130
|
+
cd apps/demo-react-native/ios
|
|
131
|
+
pod install
|
|
132
|
+
cd ..
|
|
133
|
+
|
|
134
|
+
# 3a. Run on iOS
|
|
135
|
+
npx react-native run-ios
|
|
136
|
+
|
|
137
|
+
# 3b. Or run on Android
|
|
138
|
+
npx react-native run-android
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Starting Metro bundler separately** (useful for debugging):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
cd apps/demo-react-native
|
|
145
|
+
npx react-native start --reset-cache
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Notes:**
|
|
149
|
+
- The demo app includes all 4 wallet adapters (Ledger, Keystone, Trezor, SafePal)
|
|
150
|
+
- BLE scanning requires a physical device — iOS Simulator does not support Bluetooth
|
|
151
|
+
- QR-based wallets (Keystone, SafePal) require camera access
|
|
152
|
+
- Trezor requires Trezor Bridge running on desktop
|
|
153
|
+
- See `apps/demo-react-native/` for the full source
|
|
154
|
+
|
|
155
|
+
## API Reference
|
|
156
|
+
|
|
157
|
+
### Core SDK
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { createHardwareWalletSdk } from '@solana-hw-wallet/core';
|
|
161
|
+
import { LedgerAdapter } from '@solana-hw-wallet/adapter-ledger';
|
|
162
|
+
|
|
163
|
+
// Create SDK with adapters
|
|
164
|
+
const sdk = createHardwareWalletSdk({
|
|
165
|
+
adapters: [new LedgerAdapter({ transportFactory: () => TransportBLE.create() })],
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Discover devices
|
|
169
|
+
const devices = await sdk.discoverDevices();
|
|
170
|
+
|
|
171
|
+
// Connect
|
|
172
|
+
await sdk.connect('ledger', devices[0].id);
|
|
173
|
+
|
|
174
|
+
// Derive accounts
|
|
175
|
+
const accounts = await sdk.getAccounts({ count: 5 });
|
|
176
|
+
const account = await sdk.getPublicKey(0);
|
|
177
|
+
|
|
178
|
+
// Sign transaction
|
|
179
|
+
const { signature } = await sdk.signTransaction(serializedTx, account.derivationPath);
|
|
180
|
+
|
|
181
|
+
// Sign message
|
|
182
|
+
const { signature: msgSig } = await sdk.signMessage(messageBytes, account.derivationPath);
|
|
183
|
+
|
|
184
|
+
// Disconnect
|
|
185
|
+
await sdk.disconnect();
|
|
186
|
+
|
|
187
|
+
// Clean up
|
|
188
|
+
await sdk.dispose();
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### React Native Hooks
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import {
|
|
195
|
+
HardwareWalletProvider,
|
|
196
|
+
createHardwareWalletSdk,
|
|
197
|
+
useHardwareWallet,
|
|
198
|
+
useWalletDiscovery,
|
|
199
|
+
useWalletConnection,
|
|
200
|
+
useDerivedAccounts,
|
|
201
|
+
} from '@solana-hw-wallet/react-native';
|
|
202
|
+
|
|
203
|
+
// Wrap your app
|
|
204
|
+
function App() {
|
|
205
|
+
const sdk = useMemo(() => createHardwareWalletSdk({ adapters: [...] }), []);
|
|
206
|
+
return (
|
|
207
|
+
<HardwareWalletProvider sdk={sdk}>
|
|
208
|
+
<YourApp />
|
|
209
|
+
</HardwareWalletProvider>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Use hooks in components
|
|
214
|
+
function WalletScreen() {
|
|
215
|
+
const {
|
|
216
|
+
isConnected,
|
|
217
|
+
connect,
|
|
218
|
+
disconnect,
|
|
219
|
+
accounts,
|
|
220
|
+
loadAccounts,
|
|
221
|
+
selectedAccount,
|
|
222
|
+
selectAccount,
|
|
223
|
+
signTransaction,
|
|
224
|
+
signMessage,
|
|
225
|
+
isSigning,
|
|
226
|
+
error,
|
|
227
|
+
clearError,
|
|
228
|
+
} = useHardwareWallet();
|
|
229
|
+
|
|
230
|
+
// Or use individual hooks:
|
|
231
|
+
const { devices, discover, isDiscovering } = useWalletDiscovery();
|
|
232
|
+
const { connectionState, connect, disconnect } = useWalletConnection();
|
|
233
|
+
const { accounts, isLoading, refresh } = useDerivedAccounts(true, { count: 3 });
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Solana Helpers
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
getSolanaDerivationPath,
|
|
242
|
+
getSolanaDerivationPaths,
|
|
243
|
+
parseSolanaDerivationPath,
|
|
244
|
+
serializeTransaction,
|
|
245
|
+
addSignatureToTransaction,
|
|
246
|
+
} from '@solana-hw-wallet/solana';
|
|
247
|
+
|
|
248
|
+
// Derivation paths
|
|
249
|
+
getSolanaDerivationPath(0); // "m/44'/501'/0'/0'"
|
|
250
|
+
getSolanaDerivationPath(2, 1); // "m/44'/501'/2'/1'"
|
|
251
|
+
getSolanaDerivationPaths(5); // array of 5 paths
|
|
252
|
+
|
|
253
|
+
// Transaction helpers
|
|
254
|
+
const message = serializeTransaction(transaction);
|
|
255
|
+
const signed = addSignatureToTransaction(transaction, publicKey, signature);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Error Handling
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { SdkError, SdkErrorCode } from '@solana-hw-wallet/core';
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await sdk.signTransaction(tx, path);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
if (err instanceof SdkError) {
|
|
267
|
+
switch (err.code) {
|
|
268
|
+
case SdkErrorCode.UserCancelled:
|
|
269
|
+
console.log('User rejected on device');
|
|
270
|
+
break;
|
|
271
|
+
case SdkErrorCode.WalletAppNotOpen:
|
|
272
|
+
console.log('Please open the Solana app');
|
|
273
|
+
break;
|
|
274
|
+
case SdkErrorCode.BlindSigningRequired:
|
|
275
|
+
console.log('Enable blind signing in wallet settings');
|
|
276
|
+
break;
|
|
277
|
+
case SdkErrorCode.NotConnected:
|
|
278
|
+
console.log('Wallet disconnected');
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Helper properties
|
|
283
|
+
err.isCancellation; // true for user cancellations
|
|
284
|
+
err.isRecoverable; // true for timeout, disconnected, etc.
|
|
285
|
+
err.walletType; // which wallet caused the error
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Events
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { SdkEvent } from '@solana-hw-wallet/core';
|
|
294
|
+
|
|
295
|
+
sdk.on(SdkEvent.DeviceDiscovered, (device) => { ... });
|
|
296
|
+
sdk.on(SdkEvent.ConnectionStateChanged, ({ state, previousState, deviceId }) => { ... });
|
|
297
|
+
sdk.on(SdkEvent.Error, ({ error, context }) => { ... });
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Per-Wallet Setup Notes
|
|
301
|
+
|
|
302
|
+
### Ledger
|
|
303
|
+
- **Requires**: Solana app installed and open on device
|
|
304
|
+
- **BLE**: Requires Bluetooth permissions (iOS Info.plist, Android manifest)
|
|
305
|
+
- **Blind signing**: Must be enabled in Solana app settings for complex transactions
|
|
306
|
+
- **Message signing**: Requires Ledger Solana app >= 1.3.0
|
|
307
|
+
- **Transport**: Inject `@ledgerhq/react-native-hw-transport-ble` for mobile
|
|
308
|
+
|
|
309
|
+
### Keystone
|
|
310
|
+
- **QR-based**: Air-gapped, no persistent connection
|
|
311
|
+
- **Requires**: Camera permission for QR scanning
|
|
312
|
+
- **App must provide**: QR display and scanner UI via `qrCallbacks`
|
|
313
|
+
- **Protocol**: Uses UR (Uniform Resource) standard
|
|
314
|
+
|
|
315
|
+
### Trezor
|
|
316
|
+
- **USB only**: BLE not supported by Trezor
|
|
317
|
+
- **Requires**: `@trezor/connect` package, Trezor Bridge on desktop
|
|
318
|
+
- **React Native**: Needs WebView bridge approach (not native USB)
|
|
319
|
+
- **No message signing**: Solana message signing is unsupported on most models
|
|
320
|
+
|
|
321
|
+
### SafePal
|
|
322
|
+
- **QR-based**: Proprietary protocol (not UR standard)
|
|
323
|
+
- **Status**: Partial implementation
|
|
324
|
+
- **Blocked**: QR encoding/decoding protocol not publicly documented
|
|
325
|
+
|
|
326
|
+
## Testing
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# Run all tests
|
|
330
|
+
pnpm test
|
|
331
|
+
|
|
332
|
+
# Run specific package tests
|
|
333
|
+
pnpm --filter @solana-hw-wallet/core test
|
|
334
|
+
pnpm --filter @solana-hw-wallet/solana test
|
|
335
|
+
pnpm --filter @solana-hw-wallet/test-utils test
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Test Coverage
|
|
339
|
+
|
|
340
|
+
| Package | Tests | Description |
|
|
341
|
+
|---------|-------|-------------|
|
|
342
|
+
| core | 28 | SDK lifecycle, connection, signing, errors |
|
|
343
|
+
| solana | 13 | Derivation paths, parsing, buffer conversion |
|
|
344
|
+
| test-utils | 8 | Mock adapter behaviors, call recording |
|
|
345
|
+
|
|
346
|
+
### Testing with Hardware
|
|
347
|
+
|
|
348
|
+
For integration testing with real hardware:
|
|
349
|
+
|
|
350
|
+
1. **Ledger**: Connect via BLE, ensure Solana app is open
|
|
351
|
+
2. **Keystone**: Use QR codes with device camera
|
|
352
|
+
3. **Trezor**: Connect via USB, ensure Trezor Bridge is running
|
|
353
|
+
|
|
354
|
+
See `docs/wallets/` for per-wallet testing guides.
|
|
355
|
+
|
|
356
|
+
### Mock Adapter
|
|
357
|
+
|
|
358
|
+
Use `MockAdapter` from `@solana-hw-wallet/test-utils` for development and testing:
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
import { MockAdapter, createTestSdk } from '@solana-hw-wallet/test-utils';
|
|
362
|
+
|
|
363
|
+
const { sdk, adapters } = createTestSdk();
|
|
364
|
+
await sdk.connect('mock');
|
|
365
|
+
|
|
366
|
+
// Simulate behaviors
|
|
367
|
+
adapters[0].setBehavior({ rejectSigning: true });
|
|
368
|
+
adapters[0].setBehavior({ delayMs: 2000 }); // simulate latency
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Development
|
|
372
|
+
|
|
373
|
+
### Build
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
pnpm build # Build all packages
|
|
377
|
+
pnpm dev # Watch mode
|
|
378
|
+
pnpm typecheck # Type checking
|
|
379
|
+
pnpm lint # ESLint
|
|
380
|
+
pnpm clean # Clean all dist folders
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Package Publishing
|
|
384
|
+
|
|
385
|
+
Each package is independently publishable with:
|
|
386
|
+
- Proper `exports` field
|
|
387
|
+
- TypeScript declarations
|
|
388
|
+
- ESM and CJS builds
|
|
389
|
+
- Source maps
|
|
390
|
+
|
|
391
|
+
## Troubleshooting
|
|
392
|
+
|
|
393
|
+
**Q: BLE discovery doesn't find my Ledger**
|
|
394
|
+
Ensure Bluetooth is enabled, device is in pairing mode, and the Solana app is open. On Android, BLUETOOTH_SCAN and BLUETOOTH_CONNECT permissions are required.
|
|
395
|
+
|
|
396
|
+
**Q: "Solana app not open" error**
|
|
397
|
+
Open the Solana app on your hardware wallet before connecting. Some wallets default to the dashboard.
|
|
398
|
+
|
|
399
|
+
**Q: "Blind signing required" error**
|
|
400
|
+
In the Solana app settings on your Ledger, enable "Allow blind signing". This is needed for complex transactions.
|
|
401
|
+
|
|
402
|
+
**Q: Trezor not detected**
|
|
403
|
+
Ensure Trezor Bridge is installed and running (desktop). For React Native, a WebView bridge is needed.
|
|
404
|
+
|
|
405
|
+
**Q: Keystone QR code not recognized**
|
|
406
|
+
Ensure you're scanning the correct QR code type. Account sync and signing use different QR formats.
|
|
407
|
+
|
|
408
|
+
## Limitations & Future Work
|
|
409
|
+
|
|
410
|
+
- **SafePal**: Waiting for public protocol documentation
|
|
411
|
+
- **Expo**: Not supported — BLE/USB/NFC require native modules. Use bare React Native.
|
|
412
|
+
- **Versioned transactions**: Fully supported in the type system, but hardware wallet firmware may lag
|
|
413
|
+
- **Multi-sig**: Not currently implemented (individual account signing only)
|
|
414
|
+
- **Bonus wallets**: Tangem (NFC), Unruggable, Solflare Shield could be added as additional adapters
|
|
415
|
+
|
|
416
|
+
## License
|
|
417
|
+
|
|
418
|
+
MIT
|
|
419
|
+
|
|
420
|
+
## Contributing
|
|
421
|
+
|
|
422
|
+
Contributions welcome! Please:
|
|
423
|
+
1. Follow the adapter pattern for new wallet implementations
|
|
424
|
+
2. Add tests for new functionality
|
|
425
|
+
3. Update documentation
|
|
426
|
+
4. Keep packages publishable
|
package/STATUS.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Project Status
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
The Solana Hardware Wallet SDK is a functional, well-architected monorepo providing a unified API for React Native dApps to interact with multiple hardware wallets.
|
|
6
|
+
|
|
7
|
+
## What Works
|
|
8
|
+
|
|
9
|
+
### Core SDK (`@solana-hw-wallet/core`)
|
|
10
|
+
- `createHardwareWalletSdk()` factory
|
|
11
|
+
- Adapter registration and lookup
|
|
12
|
+
- Device discovery across adapters
|
|
13
|
+
- Connection lifecycle management (connect/disconnect)
|
|
14
|
+
- Account derivation with multi-account support
|
|
15
|
+
- Transaction signing
|
|
16
|
+
- Message signing
|
|
17
|
+
- Event system (discovery, connection state, errors)
|
|
18
|
+
- Typed error system with error codes, recovery hints, and cancellation detection
|
|
19
|
+
- Full test coverage (28 tests)
|
|
20
|
+
|
|
21
|
+
### Solana Helpers (`@solana-hw-wallet/solana`)
|
|
22
|
+
- BIP44 derivation path generation and parsing
|
|
23
|
+
- Multi-path generation for account enumeration
|
|
24
|
+
- Transaction serialization (legacy and versioned)
|
|
25
|
+
- Signature application to transactions
|
|
26
|
+
- Full test coverage (13 tests)
|
|
27
|
+
|
|
28
|
+
### Ledger Adapter (`@solana-hw-wallet/adapter-ledger`)
|
|
29
|
+
- Full adapter contract implementation
|
|
30
|
+
- BLE and USB transport support (via injected transport)
|
|
31
|
+
- Dynamic import of `@ledgerhq/hw-app-solana`
|
|
32
|
+
- Account derivation via Solana app
|
|
33
|
+
- Transaction signing
|
|
34
|
+
- Message signing (via `signOffchainMessage`, Solana app >= 1.3.0)
|
|
35
|
+
- Comprehensive Ledger error code mapping
|
|
36
|
+
- Blind signing detection
|
|
37
|
+
|
|
38
|
+
### Keystone Adapter (`@solana-hw-wallet/adapter-keystone`)
|
|
39
|
+
- Full adapter contract implementation
|
|
40
|
+
- QR-based signing flow (display request QR -> scan signed QR)
|
|
41
|
+
- Account sync via QR scan
|
|
42
|
+
- UR codec module (structured with TODOs for full UR encoding)
|
|
43
|
+
- JSON fallback for development/testing
|
|
44
|
+
|
|
45
|
+
### Trezor Adapter (`@solana-hw-wallet/adapter-trezor`)
|
|
46
|
+
- Full adapter contract implementation
|
|
47
|
+
- Dynamic import of `@trezor/connect`
|
|
48
|
+
- Account derivation via Trezor Connect Solana API
|
|
49
|
+
- Transaction signing
|
|
50
|
+
- Trezor error code mapping
|
|
51
|
+
- Message signing explicitly documented as unsupported
|
|
52
|
+
|
|
53
|
+
### SafePal Adapter (`@solana-hw-wallet/adapter-safepal`)
|
|
54
|
+
- Adapter contract implementation (partial)
|
|
55
|
+
- QR-based signing flow structure
|
|
56
|
+
- JSON fallback encoding/decoding
|
|
57
|
+
- **Blocked**: Proprietary QR protocol not publicly documented
|
|
58
|
+
|
|
59
|
+
### React Native Package (`@solana-hw-wallet/react-native`)
|
|
60
|
+
- `HardwareWalletProvider` context provider
|
|
61
|
+
- `useHardwareWallet()` - all-in-one hook
|
|
62
|
+
- `useWalletDiscovery()` - device discovery
|
|
63
|
+
- `useWalletConnection()` - connection lifecycle
|
|
64
|
+
- `useDerivedAccounts()` - account derivation with auto-load
|
|
65
|
+
|
|
66
|
+
### Test Utilities (`@solana-hw-wallet/test-utils`)
|
|
67
|
+
- `MockAdapter` with configurable behaviors
|
|
68
|
+
- Method call recording for assertions
|
|
69
|
+
- Runtime behavior changes (rejection, errors, delays)
|
|
70
|
+
- `createTestSdk()` factory
|
|
71
|
+
- Full test coverage (8 tests)
|
|
72
|
+
|
|
73
|
+
### Demo App (`@solana-hw-wallet/demo-react-native`)
|
|
74
|
+
- Complete UI showing full SDK flow
|
|
75
|
+
- Wallet discovery and selection
|
|
76
|
+
- Connection with status indicator
|
|
77
|
+
- Multi-account derivation and selection
|
|
78
|
+
- Message and transaction signing
|
|
79
|
+
- Error display with codes
|
|
80
|
+
|
|
81
|
+
### Node Examples (`@solana-hw-wallet/examples-node`)
|
|
82
|
+
- `derive-accounts.ts` - Account derivation demo (runs and passes)
|
|
83
|
+
- `sign-message.ts` - Message signing with events (runs and passes)
|
|
84
|
+
- `sign-transaction.ts` - Full Solana transaction flow with error handling (runs and passes)
|
|
85
|
+
|
|
86
|
+
## Test Results
|
|
87
|
+
|
|
88
|
+
| Package | Tests | Status |
|
|
89
|
+
|---------|-------|--------|
|
|
90
|
+
| @solana-hw-wallet/core | 28 | All passing |
|
|
91
|
+
| @solana-hw-wallet/solana | 13 | All passing |
|
|
92
|
+
| @solana-hw-wallet/test-utils | 8 | All passing |
|
|
93
|
+
| **Total** | **49** | **All passing** |
|
|
94
|
+
|
|
95
|
+
## Build Status
|
|
96
|
+
|
|
97
|
+
All 12 packages/apps build successfully.
|
|
98
|
+
|
|
99
|
+
## What Is Partially Implemented
|
|
100
|
+
|
|
101
|
+
1. **SafePal QR protocol**: The adapter contract is complete, but the proprietary QR encoding/decoding format is not documented. JSON fallback is used for development.
|
|
102
|
+
|
|
103
|
+
2. **Keystone UR encoding**: The UR codec module provides structured JSON encoding as a fallback. Full UR encoding requires `@keystonehq/sol-keyring` integration (marked with TODOs).
|
|
104
|
+
|
|
105
|
+
3. **React Native demo app**: The app code is complete but requires a bare React Native project setup with native module dependencies (`react-native-ble-plx`, etc.) to run on a device.
|
|
106
|
+
|
|
107
|
+
## What Remains
|
|
108
|
+
|
|
109
|
+
1. **Bonus wallet adapters**: Tangem (NFC), Unruggable, Solflare Shield
|
|
110
|
+
2. **Full UR encoding**: Complete Keystone UR codec with `@ngraveio/bc-ur`
|
|
111
|
+
3. **SafePal protocol**: Reverse-engineer or obtain documentation
|
|
112
|
+
4. **Integration tests**: Tests against real hardware (requires physical devices)
|
|
113
|
+
5. **React Native native module setup**: iOS/Android native configurations for BLE/USB/NFC
|
|
114
|
+
6. **Changesets**: Publishing workflow with version management
|
|
115
|
+
|
|
116
|
+
## How to Run/Demo/Test
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Setup
|
|
120
|
+
pnpm install
|
|
121
|
+
pnpm build
|
|
122
|
+
|
|
123
|
+
# Run all tests
|
|
124
|
+
pnpm test
|
|
125
|
+
|
|
126
|
+
# Run individual examples
|
|
127
|
+
pnpm --filter @solana-hw-wallet/examples-node example:derive
|
|
128
|
+
pnpm --filter @solana-hw-wallet/examples-node example:sign-message
|
|
129
|
+
pnpm --filter @solana-hw-wallet/examples-node example:sign-transaction
|
|
130
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
|
4
|
+
ruby ">= 2.6.10"
|
|
5
|
+
|
|
6
|
+
# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper
|
|
7
|
+
# bound in the template on Cocoapods with next React Native release.
|
|
8
|
+
gem 'cocoapods', '>= 1.13', '< 1.15'
|
|
9
|
+
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
|