@kasflow/passkey-wallet 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/README.md +308 -0
- package/dist/index.d.mts +893 -0
- package/dist/index.d.ts +893 -0
- package/dist/index.js +1845 -0
- package/dist/index.mjs +1798 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# @kasflow/passkey-wallet
|
|
2
|
+
|
|
3
|
+
Passkey-powered wallet SDK for [Kaspa blockchain](https://kaspa.org). Create and manage Kaspa wallets using device biometrics (Face ID, Touch ID, Windows Hello, etc.) with no seed phrases to remember.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Passkey Authentication**: Secure wallet access using WebAuthn/FIDO2 device biometrics
|
|
8
|
+
- **Transaction Support**: Full send/receive functionality with automatic UTXO management
|
|
9
|
+
- **Type-Safe**: Complete TypeScript definitions for all APIs
|
|
10
|
+
- **Network Agnostic**: Works with mainnet and testnet
|
|
11
|
+
- **Modern Browser APIs**: Uses Web Crypto API and IndexedDB for secure key storage
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @kasflow/passkey-wallet
|
|
17
|
+
# or
|
|
18
|
+
pnpm add @kasflow/passkey-wallet
|
|
19
|
+
# or
|
|
20
|
+
yarn add @kasflow/passkey-wallet
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
That's it - all dependencies are bundled.
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Create a New Wallet
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { PasskeyWallet } from '@kasflow/passkey-wallet';
|
|
31
|
+
|
|
32
|
+
// Create a new passkey-protected wallet
|
|
33
|
+
const result = await PasskeyWallet.create({
|
|
34
|
+
name: 'My Kaspa Wallet',
|
|
35
|
+
network: 'testnet-11' // or 'mainnet'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (result.success) {
|
|
39
|
+
const wallet = result.data;
|
|
40
|
+
console.log('Wallet Address:', wallet.getAddress());
|
|
41
|
+
} else {
|
|
42
|
+
console.error('Failed to create wallet:', result.error);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Unlock an Existing Wallet
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { PasskeyWallet } from '@kasflow/passkey-wallet';
|
|
50
|
+
|
|
51
|
+
// Unlock using biometric authentication
|
|
52
|
+
const result = await PasskeyWallet.unlock();
|
|
53
|
+
|
|
54
|
+
if (result.success) {
|
|
55
|
+
const wallet = result.data;
|
|
56
|
+
console.log('Welcome back! Address:', wallet.getAddress());
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Connect and Get Balance
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Connect to the Kaspa network
|
|
64
|
+
await wallet.connect();
|
|
65
|
+
|
|
66
|
+
// Get balance (in sompi - smallest unit)
|
|
67
|
+
const balance = await wallet.getBalance();
|
|
68
|
+
console.log('Available:', balance.available);
|
|
69
|
+
console.log('Total:', balance.total);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Send KAS
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { kasStringToSompi } from '@kasflow/passkey-wallet';
|
|
76
|
+
|
|
77
|
+
// Recommended: Send with per-transaction biometric authentication
|
|
78
|
+
const result = await wallet.sendWithAuth({
|
|
79
|
+
to: 'kaspatest:qz7ulu4c25dh7fzec9zjyrmlhnkzrg4wmf89q7gzr3gfrsj3uz6xjceef60sd',
|
|
80
|
+
amount: kasStringToSompi('1.5'), // Converts "1.5" to sompi
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log('Transaction ID:', result.transactionId);
|
|
84
|
+
console.log('Fee paid:', result.fee);
|
|
85
|
+
|
|
86
|
+
// Alternative: Send without additional authentication (uses session keys)
|
|
87
|
+
const result2 = await wallet.send({
|
|
88
|
+
to: 'kaspatest:qz...',
|
|
89
|
+
amount: kasStringToSompi('1.0'),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Sign Messages
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Sign a message with the wallet's private key
|
|
97
|
+
const signature = wallet.signMessage('Hello Kaspa!');
|
|
98
|
+
console.log('Signature:', signature);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API Reference
|
|
102
|
+
|
|
103
|
+
### PasskeyWallet Class
|
|
104
|
+
|
|
105
|
+
#### Static Methods
|
|
106
|
+
|
|
107
|
+
| Method | Description |
|
|
108
|
+
|--------|-------------|
|
|
109
|
+
| `PasskeyWallet.isSupported()` | Check if passkeys are supported in this browser |
|
|
110
|
+
| `PasskeyWallet.exists()` | Check if a wallet already exists in storage |
|
|
111
|
+
| `PasskeyWallet.create(options)` | Create a new passkey-protected wallet |
|
|
112
|
+
| `PasskeyWallet.unlock(options)` | Unlock an existing wallet with biometrics |
|
|
113
|
+
| `PasskeyWallet.delete()` | Delete the wallet from storage (irreversible!) |
|
|
114
|
+
|
|
115
|
+
#### Instance Methods
|
|
116
|
+
|
|
117
|
+
| Method | Description |
|
|
118
|
+
|--------|-------------|
|
|
119
|
+
| `wallet.getAddress()` | Get the wallet's Kaspa address |
|
|
120
|
+
| `wallet.getPublicKey()` | Get the public key as hex string |
|
|
121
|
+
| `wallet.getNetwork()` | Get the current network ID |
|
|
122
|
+
| `wallet.connect(options?)` | Connect to the Kaspa network |
|
|
123
|
+
| `wallet.disconnectNetwork()` | Disconnect from the network |
|
|
124
|
+
| `wallet.getBalance()` | Get wallet balance (requires connection) |
|
|
125
|
+
| `wallet.send(options)` | Send KAS to an address |
|
|
126
|
+
| `wallet.sendWithAuth(options)` | Send with per-transaction biometric auth (recommended) |
|
|
127
|
+
| `wallet.estimateFee(options)` | Estimate transaction fee |
|
|
128
|
+
| `wallet.signMessage(message)` | Sign a message |
|
|
129
|
+
| `wallet.on(handler)` | Subscribe to wallet events |
|
|
130
|
+
| `wallet.disconnect()` | Disconnect wallet and clear keys from memory |
|
|
131
|
+
|
|
132
|
+
### Unit Conversion
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import {
|
|
136
|
+
kasStringToSompi, // "1.5" => 150000000n
|
|
137
|
+
sompiToKasString, // 150000000n => "1.5"
|
|
138
|
+
sompiToKas, // 150000000n => 1.5 (number)
|
|
139
|
+
kasToSompi, // 1.5 => 150000000n
|
|
140
|
+
formatKas, // 150000000n => "1.5" (formatted)
|
|
141
|
+
SOMPI_PER_KAS, // 100_000_000n
|
|
142
|
+
} from '@kasflow/passkey-wallet';
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Address Validation
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import {
|
|
149
|
+
isValidAddress,
|
|
150
|
+
parseAddress,
|
|
151
|
+
getNetworkFromAddress,
|
|
152
|
+
} from '@kasflow/passkey-wallet';
|
|
153
|
+
|
|
154
|
+
// Validate an address
|
|
155
|
+
if (isValidAddress('kaspa:qz...')) {
|
|
156
|
+
console.log('Valid address!');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Parse address components
|
|
160
|
+
const parsed = parseAddress('kaspa:qz...');
|
|
161
|
+
console.log(parsed.prefix); // 'kaspa'
|
|
162
|
+
console.log(parsed.payload); // Uint8Array
|
|
163
|
+
|
|
164
|
+
// Get network from address
|
|
165
|
+
const network = getNetworkFromAddress('kaspatest:qz...');
|
|
166
|
+
console.log(network); // 'testnet-11'
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Events
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Subscribe to wallet events
|
|
173
|
+
const unsubscribe = wallet.on((event) => {
|
|
174
|
+
switch (event.type) {
|
|
175
|
+
case 'connected':
|
|
176
|
+
console.log('Connected:', event.address);
|
|
177
|
+
break;
|
|
178
|
+
case 'disconnected':
|
|
179
|
+
console.log('Disconnected');
|
|
180
|
+
break;
|
|
181
|
+
case 'balance_updated':
|
|
182
|
+
console.log('Balance:', event.balance);
|
|
183
|
+
break;
|
|
184
|
+
case 'transaction_sent':
|
|
185
|
+
console.log('TX sent:', event.txId);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Later: unsubscribe
|
|
191
|
+
unsubscribe();
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Advanced Usage
|
|
195
|
+
|
|
196
|
+
### Direct RPC Access
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { KaspaRpc } from '@kasflow/passkey-wallet';
|
|
200
|
+
|
|
201
|
+
const rpc = new KaspaRpc();
|
|
202
|
+
await rpc.connect({ network: 'testnet-11' });
|
|
203
|
+
|
|
204
|
+
// Get network info
|
|
205
|
+
const info = await rpc.getNetworkInfo();
|
|
206
|
+
console.log('Block count:', info.blockCount);
|
|
207
|
+
|
|
208
|
+
// Get UTXOs for any address
|
|
209
|
+
const utxos = await rpc.getUtxos('kaspatest:qz...');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Transaction Building
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import {
|
|
216
|
+
buildTransactions,
|
|
217
|
+
signTransactions,
|
|
218
|
+
submitTransactions
|
|
219
|
+
} from '@kasflow/passkey-wallet';
|
|
220
|
+
|
|
221
|
+
// Build transactions manually
|
|
222
|
+
const { transactions, summary } = await buildTransactions(
|
|
223
|
+
utxos,
|
|
224
|
+
[{ address: 'kaspatest:qz...', amount: 100000000n }],
|
|
225
|
+
changeAddress,
|
|
226
|
+
100000n, // priority fee
|
|
227
|
+
'testnet-11'
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Sign them
|
|
231
|
+
const signed = signTransactions(transactions, privateKeyHex);
|
|
232
|
+
|
|
233
|
+
// Submit
|
|
234
|
+
const txIds = await submitTransactions(signed, rpc);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### WASM SDK Access
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
RpcClient,
|
|
242
|
+
Resolver,
|
|
243
|
+
Generator,
|
|
244
|
+
PrivateKey
|
|
245
|
+
} from '@kasflow/passkey-wallet';
|
|
246
|
+
|
|
247
|
+
// Direct access to kaspa-wasm32-sdk types
|
|
248
|
+
const privateKey = new PrivateKey('...');
|
|
249
|
+
const address = privateKey.toAddress('testnet-11');
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Network Configuration
|
|
253
|
+
|
|
254
|
+
| Network | ID | Address Prefix |
|
|
255
|
+
|---------|-----|----------------|
|
|
256
|
+
| Mainnet | `mainnet` | `kaspa:` |
|
|
257
|
+
| Testnet 10 | `testnet-10` | `kaspatest:` |
|
|
258
|
+
| Testnet 11 | `testnet-11` | `kaspatest:` |
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { NETWORK_ID, DEFAULT_NETWORK } from '@kasflow/passkey-wallet';
|
|
262
|
+
|
|
263
|
+
console.log(NETWORK_ID.MAINNET); // 'mainnet'
|
|
264
|
+
console.log(NETWORK_ID.TESTNET_11); // 'testnet-11'
|
|
265
|
+
console.log(DEFAULT_NETWORK); // 'testnet-11'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Security
|
|
269
|
+
|
|
270
|
+
### How It Works
|
|
271
|
+
|
|
272
|
+
1. **Passkey Registration**: A WebAuthn credential is created and tied to device biometrics
|
|
273
|
+
2. **Deterministic Derivation**: Kaspa keys are derived from the passkey's public key using SHA-256
|
|
274
|
+
3. **No Key Storage**: Private keys are never stored - they're re-derived on each unlock
|
|
275
|
+
4. **Multi-Device Sync**: Same wallet on any device where the passkey syncs (iCloud Keychain, Google Password Manager)
|
|
276
|
+
|
|
277
|
+
### Security Considerations
|
|
278
|
+
|
|
279
|
+
- No seed phrases or passwords - authentication is biometric only
|
|
280
|
+
- Private keys exist only in memory during operations, never persisted
|
|
281
|
+
- Keys are deterministically derived, so the same passkey always produces the same wallet
|
|
282
|
+
- All cryptographic operations happen locally in the browser
|
|
283
|
+
|
|
284
|
+
### Browser Requirements
|
|
285
|
+
|
|
286
|
+
- WebAuthn support (all modern browsers)
|
|
287
|
+
- Platform authenticator (Touch ID, Face ID, Windows Hello, etc.)
|
|
288
|
+
- Secure context (HTTPS or localhost)
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Install dependencies
|
|
294
|
+
pnpm install
|
|
295
|
+
|
|
296
|
+
# Type check
|
|
297
|
+
pnpm typecheck
|
|
298
|
+
|
|
299
|
+
# Run tests
|
|
300
|
+
pnpm test
|
|
301
|
+
|
|
302
|
+
# Build
|
|
303
|
+
pnpm build
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|