w3pk 0.3.0 β 0.4.1
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 +458 -67
- package/dist/index.d.mts +111 -1
- package/dist/index.d.ts +111 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# w3pk
|
|
2
2
|
|
|
3
|
-
WebAuthn SDK for passwordless authentication
|
|
3
|
+
WebAuthn SDK for passwordless authentication, encrypted Ethereum wallets, and privacy-preserving stealth addresses.
|
|
4
|
+
|
|
5
|
+
Live demo: **https://d2u.w3hc.org/voting**
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -8,53 +10,39 @@ WebAuthn SDK for passwordless authentication with client-side encrypted Ethereum
|
|
|
8
10
|
npm install w3pk
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **π Passwordless Authentication**: WebAuthn/FIDO2 biometric authentication
|
|
16
|
+
- **π° Encrypted Wallet Management**: Client-side AES-GCM-256 encrypted wallets
|
|
17
|
+
- **π± HD Wallet Generation**: BIP39/BIP44 compliant wallet derivation
|
|
18
|
+
- **π₯· Stealth Addresses**: Privacy-preserving stealth address generation with unlinkable transactions
|
|
19
|
+
- **π‘οΈ Network Agnostic**: Works with any blockchain - you handle the transactions
|
|
20
|
+
|
|
11
21
|
## Quick Start
|
|
12
22
|
|
|
13
23
|
```typescript
|
|
14
24
|
import { createWeb3Passkey } from 'w3pk'
|
|
15
|
-
import { startRegistration, startAuthentication } from '@simplewebauthn/browser'
|
|
16
25
|
|
|
17
26
|
const w3pk = createWeb3Passkey({
|
|
18
27
|
apiBaseUrl: 'https://webauthn.w3hc.org'
|
|
19
28
|
})
|
|
20
29
|
|
|
21
|
-
//
|
|
22
|
-
const
|
|
23
|
-
console.log('
|
|
24
|
-
|
|
25
|
-
// Register
|
|
26
|
-
const beginRes = await fetch('https://webauthn.w3hc.org/webauthn/register/begin', {
|
|
27
|
-
method: 'POST',
|
|
28
|
-
headers: { 'Content-Type': 'application/json' },
|
|
29
|
-
body: JSON.stringify({ username: 'alice', ethereumAddress: wallet.address })
|
|
30
|
-
})
|
|
31
|
-
const { data } = await beginRes.json()
|
|
32
|
-
const credential = await startRegistration(data.options)
|
|
33
|
-
|
|
34
|
-
await w3pk.register({
|
|
35
|
-
username: 'alice',
|
|
36
|
-
ethereumAddress: wallet.address,
|
|
37
|
-
mnemonic: wallet.mnemonic,
|
|
38
|
-
credentialId: credential.id,
|
|
39
|
-
challenge: data.options.challenge
|
|
40
|
-
})
|
|
30
|
+
// Register new user (wallet generated automatically)
|
|
31
|
+
const result = await w3pk.register({ username: 'alice' })
|
|
32
|
+
console.log('β οΈ BACKUP THIS MNEMONIC:', result.mnemonic)
|
|
33
|
+
console.log('Ethereum address:', result.ethereumAddress)
|
|
41
34
|
|
|
42
|
-
// Login
|
|
43
|
-
const
|
|
44
|
-
console.log('Logged in:',
|
|
35
|
+
// Login (usernameless)
|
|
36
|
+
const loginResult = await w3pk.login()
|
|
37
|
+
console.log('Logged in:', loginResult.user?.username)
|
|
38
|
+
console.log('Address:', loginResult.user?.ethereumAddress)
|
|
45
39
|
|
|
46
|
-
// Sign message
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
})
|
|
50
|
-
const authData = await authBeginRes.json()
|
|
51
|
-
const authCredential = await startAuthentication(authData.data.options)
|
|
40
|
+
// Sign message (handles fresh auth automatically)
|
|
41
|
+
const signature = await w3pk.signMessage('Hello, Web3!')
|
|
42
|
+
console.log('Signature:', signature)
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
authCredential.id,
|
|
56
|
-
authData.data.options.challenge
|
|
57
|
-
)
|
|
44
|
+
// Logout
|
|
45
|
+
w3pk.logout()
|
|
58
46
|
```
|
|
59
47
|
|
|
60
48
|
## API
|
|
@@ -67,52 +55,314 @@ createWeb3Passkey({
|
|
|
67
55
|
timeout?: number, // Optional: Request timeout (default: 30000ms)
|
|
68
56
|
debug?: boolean, // Optional: Enable logs (default: false)
|
|
69
57
|
onError?: (error) => void, // Optional: Error handler
|
|
70
|
-
onAuthStateChanged?: (isAuth, user?) => void // Optional: Auth callback
|
|
58
|
+
onAuthStateChanged?: (isAuth, user?) => void, // Optional: Auth callback
|
|
59
|
+
stealthAddresses?: {} // Optional: Enable stealth address generation
|
|
71
60
|
})
|
|
72
61
|
```
|
|
73
62
|
|
|
74
63
|
### Methods
|
|
75
64
|
|
|
65
|
+
#### Wallet Management
|
|
66
|
+
|
|
76
67
|
```typescript
|
|
77
68
|
// Generate BIP39 wallet (12-word mnemonic)
|
|
78
69
|
await w3pk.generateWallet()
|
|
79
70
|
// Returns: { address: string, mnemonic: string }
|
|
80
71
|
|
|
81
|
-
//
|
|
82
|
-
await w3pk.
|
|
72
|
+
// Check if wallet exists for current user
|
|
73
|
+
await w3pk.hasWallet()
|
|
74
|
+
// Returns: boolean
|
|
75
|
+
|
|
76
|
+
// Derive HD wallet at specific index (requires authentication)
|
|
77
|
+
await w3pk.deriveWallet(0) // First address (default)
|
|
78
|
+
await w3pk.deriveWallet(1) // Second address
|
|
79
|
+
await w3pk.deriveWallet(5) // Sixth address
|
|
80
|
+
// Returns: { address: string, privateKey: string }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Authentication
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Register new user (auto-generates wallet)
|
|
87
|
+
await w3pk.register({ username: string })
|
|
88
|
+
// Returns: { ethereumAddress: string, mnemonic?: string }
|
|
89
|
+
|
|
90
|
+
// Register with existing wallet
|
|
91
|
+
await w3pk.register({
|
|
83
92
|
username: string,
|
|
84
93
|
ethereumAddress: string,
|
|
85
|
-
mnemonic: string
|
|
86
|
-
credentialId: string,
|
|
87
|
-
challenge: string
|
|
94
|
+
mnemonic: string
|
|
88
95
|
})
|
|
89
96
|
|
|
90
97
|
// Login (usernameless)
|
|
91
98
|
await w3pk.login()
|
|
92
|
-
// Returns: { verified: boolean, user?:
|
|
99
|
+
// Returns: { verified: boolean, user?: UserInfo }
|
|
100
|
+
|
|
101
|
+
// Logout
|
|
102
|
+
w3pk.logout()
|
|
103
|
+
```
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
await w3pk.authenticate(ethereumAddress: string)
|
|
105
|
+
#### Message Signing
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
```typescript
|
|
108
|
+
// Sign message (handles WebAuthn authentication internally)
|
|
109
|
+
await w3pk.signMessage(message: string)
|
|
99
110
|
// Returns: string (signature)
|
|
111
|
+
```
|
|
100
112
|
|
|
101
|
-
|
|
102
|
-
w3pk.logout()
|
|
113
|
+
### Properties
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
```typescript
|
|
116
|
+
w3pk.isAuthenticated // boolean - Current authentication state
|
|
117
|
+
w3pk.user // UserInfo | null - Current user data
|
|
118
|
+
w3pk.version // string - SDK version
|
|
119
|
+
w3pk.isBrowserEnvironment // boolean - Browser environment detection
|
|
120
|
+
w3pk.stealth // StealthAddressModule | null - Stealth address module
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Error Handling
|
|
124
|
+
|
|
125
|
+
The SDK provides specific error types for better error handling:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import {
|
|
129
|
+
Web3PasskeyError,
|
|
130
|
+
AuthenticationError,
|
|
131
|
+
RegistrationError,
|
|
132
|
+
WalletError,
|
|
133
|
+
CryptoError,
|
|
134
|
+
StorageError,
|
|
135
|
+
ApiError
|
|
136
|
+
} from 'w3pk'
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await w3pk.register({ username: 'alice' })
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error instanceof AuthenticationError) {
|
|
142
|
+
console.error('WebAuthn authentication failed:', error.message)
|
|
143
|
+
} else if (error instanceof WalletError) {
|
|
144
|
+
console.error('Wallet operation failed:', error.message)
|
|
145
|
+
} else if (error instanceof ApiError) {
|
|
146
|
+
console.error('Backend API error:', error.message)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Stealth Address API
|
|
152
|
+
|
|
153
|
+
When configured with `stealthAddresses` option, the SDK provides privacy-preserving stealth address generation:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// Generate a fresh stealth address for privacy-preserving transactions
|
|
157
|
+
await w3pk.stealth.generateStealthAddress()
|
|
158
|
+
// Returns: { stealthAddress, stealthPrivateKey, ephemeralPublicKey }
|
|
159
|
+
|
|
160
|
+
// Get stealth keys for advanced operations
|
|
161
|
+
await w3pk.stealth.getKeys()
|
|
162
|
+
// Returns: { metaAddress, viewingKey, spendingKey }
|
|
163
|
+
|
|
164
|
+
// Check if a stealth address belongs to you (from crypto utils)
|
|
165
|
+
import { canControlStealthAddress } from 'w3pk'
|
|
166
|
+
canControlStealthAddress(viewingKey, ephemeralPublicKey, targetAddress)
|
|
106
167
|
// Returns: boolean
|
|
107
168
|
```
|
|
108
169
|
|
|
109
|
-
###
|
|
170
|
+
### Utility Functions
|
|
171
|
+
|
|
172
|
+
For direct wallet operations without SDK authentication:
|
|
110
173
|
|
|
111
174
|
```typescript
|
|
112
|
-
w3pk
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
175
|
+
import { generateBIP39Wallet, createWalletFromMnemonic, deriveWalletFromMnemonic } from 'w3pk'
|
|
176
|
+
|
|
177
|
+
// Generate new BIP39 wallet
|
|
178
|
+
const wallet = generateBIP39Wallet()
|
|
179
|
+
// Returns: { address: string, mnemonic: string }
|
|
180
|
+
|
|
181
|
+
// Create ethers wallet from mnemonic
|
|
182
|
+
const ethersWallet = createWalletFromMnemonic(mnemonic)
|
|
183
|
+
// Returns: ethers.HDNodeWallet
|
|
184
|
+
|
|
185
|
+
// Derive HD wallet at specific index
|
|
186
|
+
const derived = deriveWalletFromMnemonic(mnemonic, 2)
|
|
187
|
+
// Returns: { address: string, privateKey: string }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Types
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
interface UserInfo {
|
|
194
|
+
id: string
|
|
195
|
+
username: string
|
|
196
|
+
displayName: string
|
|
197
|
+
ethereumAddress: string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
interface WalletInfo {
|
|
201
|
+
address: string
|
|
202
|
+
mnemonic: string
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface AuthResult {
|
|
206
|
+
verified: boolean
|
|
207
|
+
user?: UserInfo
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface StealthKeys {
|
|
211
|
+
metaAddress: string
|
|
212
|
+
viewingKey: string
|
|
213
|
+
spendingKey: string
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
interface StealthAddressResult {
|
|
217
|
+
stealthAddress: string
|
|
218
|
+
stealthPrivateKey: string
|
|
219
|
+
ephemeralPublicKey: string
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## HD Wallet Example
|
|
224
|
+
|
|
225
|
+
Access multiple addresses from a single mnemonic for advanced wallet management:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { createWeb3Passkey } from 'w3pk'
|
|
229
|
+
import { ethers } from 'ethers'
|
|
230
|
+
|
|
231
|
+
// Initialize and authenticate
|
|
232
|
+
const w3pk = createWeb3Passkey({
|
|
233
|
+
apiBaseUrl: 'https://webauthn.w3hc.org'
|
|
234
|
+
})
|
|
235
|
+
await w3pk.login()
|
|
236
|
+
|
|
237
|
+
// Generate multiple wallet addresses from the same mnemonic
|
|
238
|
+
const mainWallet = await w3pk.deriveWallet(0) // Main wallet
|
|
239
|
+
const savingsWallet = await w3pk.deriveWallet(1) // Savings wallet
|
|
240
|
+
const tradingWallet = await w3pk.deriveWallet(2) // Trading wallet
|
|
241
|
+
|
|
242
|
+
console.log('Main address:', mainWallet.address)
|
|
243
|
+
console.log('Savings address:', savingsWallet.address)
|
|
244
|
+
console.log('Trading address:', tradingWallet.address)
|
|
245
|
+
|
|
246
|
+
// Use private keys directly with ethers.js
|
|
247
|
+
const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com')
|
|
248
|
+
const mainSigner = new ethers.Wallet(mainWallet.privateKey, provider)
|
|
249
|
+
const savingsSigner = new ethers.Wallet(savingsWallet.privateKey, provider)
|
|
250
|
+
|
|
251
|
+
// Send transactions from different derived addresses
|
|
252
|
+
const tx1 = await mainSigner.sendTransaction({
|
|
253
|
+
to: savingsWallet.address,
|
|
254
|
+
value: ethers.parseEther('0.1')
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const tx2 = await savingsSigner.sendTransaction({
|
|
258
|
+
to: tradingWallet.address,
|
|
259
|
+
value: ethers.parseEther('0.05')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
console.log('Transfer to savings:', tx1.hash)
|
|
263
|
+
console.log('Transfer to trading:', tx2.hash)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Stealth Address Example
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createWeb3Passkey } from 'w3pk'
|
|
270
|
+
import { ethers } from 'ethers'
|
|
271
|
+
|
|
272
|
+
// Initialize SDK with stealth addresses enabled
|
|
273
|
+
const w3pk = createWeb3Passkey({
|
|
274
|
+
apiBaseUrl: 'https://webauthn.w3hc.org',
|
|
275
|
+
stealthAddresses: {}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// 1. Login with w3pk
|
|
279
|
+
await w3pk.login()
|
|
280
|
+
|
|
281
|
+
// 2. Generate a fresh stealth address
|
|
282
|
+
const stealthResult = await w3pk.stealth.generateStealthAddress()
|
|
283
|
+
console.log('Stealth address:', stealthResult.stealthAddress)
|
|
284
|
+
console.log('Private key:', stealthResult.stealthPrivateKey)
|
|
285
|
+
console.log('Ephemeral public key:', stealthResult.ephemeralPublicKey)
|
|
286
|
+
|
|
287
|
+
// 3. Use the private key with any blockchain library
|
|
288
|
+
const stealthWallet = new ethers.Wallet(stealthResult.stealthPrivateKey)
|
|
289
|
+
|
|
290
|
+
// 4. Sign transactions with any provider
|
|
291
|
+
const provider = new ethers.JsonRpcProvider('https://ethereum-sepolia-rpc.publicnode.com')
|
|
292
|
+
const connectedWallet = stealthWallet.connect(provider)
|
|
293
|
+
|
|
294
|
+
// 5. Send transactions normally - now unlinkable!
|
|
295
|
+
const tx = await connectedWallet.sendTransaction({
|
|
296
|
+
to: '0x742d35Cc6139FE1C2f1234567890123456789014',
|
|
297
|
+
value: ethers.parseEther('0.001')
|
|
298
|
+
})
|
|
299
|
+
console.log('Transaction sent from stealth address:', tx.hash)
|
|
300
|
+
|
|
301
|
+
// 6. Get stealth keys for advanced operations
|
|
302
|
+
const keys = await w3pk.stealth.getKeys()
|
|
303
|
+
console.log('Your stealth meta address:', keys.metaAddress)
|
|
304
|
+
console.log('Your viewing key (keep private):', keys.viewingKey)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Complete Example
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { createWeb3Passkey } from 'w3pk'
|
|
311
|
+
|
|
312
|
+
// Initialize SDK
|
|
313
|
+
const w3pk = createWeb3Passkey({
|
|
314
|
+
apiBaseUrl: 'https://webauthn.w3hc.org',
|
|
315
|
+
debug: true,
|
|
316
|
+
onError: (error) => {
|
|
317
|
+
console.error('SDK Error:', error.message)
|
|
318
|
+
},
|
|
319
|
+
onAuthStateChanged: (isAuth, user) => {
|
|
320
|
+
console.log('Auth changed:', isAuth, user?.username)
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
// 1. Register new user
|
|
325
|
+
try {
|
|
326
|
+
const result = await w3pk.register({ username: 'alice' })
|
|
327
|
+
|
|
328
|
+
// User MUST backup this mnemonic!
|
|
329
|
+
if (result.mnemonic) {
|
|
330
|
+
alert(`β οΈ SAVE THIS: ${result.mnemonic}`)
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.error('Registration failed:', error)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 2. Login existing user
|
|
337
|
+
try {
|
|
338
|
+
const result = await w3pk.login()
|
|
339
|
+
|
|
340
|
+
if (result.verified) {
|
|
341
|
+
console.log('Welcome back,', result.user?.username)
|
|
342
|
+
|
|
343
|
+
// Check if wallet is available on this device
|
|
344
|
+
const hasWallet = await w3pk.hasWallet()
|
|
345
|
+
console.log('Wallet available:', hasWallet)
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('Login failed:', error)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 3. Sign a message
|
|
352
|
+
if (w3pk.isAuthenticated) {
|
|
353
|
+
try {
|
|
354
|
+
// This will prompt for WebAuthn authentication
|
|
355
|
+
const signature = await w3pk.signMessage('Hello, Web3!')
|
|
356
|
+
console.log('Signature:', signature)
|
|
357
|
+
|
|
358
|
+
// Verify on Etherscan: https://etherscan.io/verifiedSignatures
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error('Signing failed:', error)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 4. Logout
|
|
365
|
+
w3pk.logout()
|
|
116
366
|
```
|
|
117
367
|
|
|
118
368
|
## Backend
|
|
@@ -128,24 +378,161 @@ pnpm start:dev
|
|
|
128
378
|
|
|
129
379
|
## Security
|
|
130
380
|
|
|
131
|
-
|
|
132
|
-
- β
|
|
133
|
-
- β
|
|
134
|
-
- β
|
|
135
|
-
-
|
|
381
|
+
### Encryption & Storage
|
|
382
|
+
- β
**Client-side AES-GCM-256 encryption** - All sensitive data encrypted in browser
|
|
383
|
+
- β
**PBKDF2 key derivation** (100,000 iterations) from WebAuthn credentials
|
|
384
|
+
- β
**Private keys never leave device** - Zero server-side key storage
|
|
385
|
+
- β
**IndexedDB encrypted storage** - Separate encrypted storage per device
|
|
386
|
+
- β
**WebAuthn/FIDO2 authentication** - Hardware-backed biometric security
|
|
387
|
+
|
|
388
|
+
### Wallet Standards
|
|
389
|
+
- β
**BIP39 standard mnemonic** - Industry-standard 12-word recovery phrase
|
|
390
|
+
- β
**BIP44 HD derivation** - Standard path `m/44'/60'/0'/0/{index}` for Ethereum
|
|
391
|
+
- β
**Deterministic addresses** - Same mnemonic always produces same addresses
|
|
392
|
+
- β
**Multiple address support** - Derive unlimited addresses from one mnemonic
|
|
393
|
+
|
|
394
|
+
### Privacy Features
|
|
395
|
+
- β
**Stealth addresses** - Unlinkable transaction privacy (optional)
|
|
396
|
+
- β
**Zero-knowledge proofs** - Privacy-preserving stealth address generation
|
|
397
|
+
- β
**Ephemeral keys** - Fresh keys for each stealth transaction
|
|
398
|
+
- β
**Unlinkable transactions** - No on-chain connection between stealth addresses
|
|
399
|
+
|
|
400
|
+
### Security Notes & Best Practices
|
|
401
|
+
|
|
402
|
+
#### β οΈ Critical Security Requirements
|
|
403
|
+
- **MUST backup mnemonic** - The 12-word phrase is shown only once during registration
|
|
404
|
+
- **MUST secure mnemonic** - Store it offline, never share or store digitally
|
|
405
|
+
- **Cannot recover without mnemonic** - Lost device + lost mnemonic = lost wallet forever
|
|
406
|
+
|
|
407
|
+
#### π Device Security
|
|
408
|
+
- Your wallet is protected by device biometrics (fingerprint, Face ID, Windows Hello)
|
|
409
|
+
- Each device stores its own encrypted copy of the wallet
|
|
410
|
+
- WebAuthn credentials are bound to your specific device hardware
|
|
411
|
+
- Fresh authentication required for sensitive operations (signing, key derivation)
|
|
412
|
+
|
|
413
|
+
#### π Network Security
|
|
414
|
+
- SDK works entirely client-side - no private keys sent to servers
|
|
415
|
+
- Backend only stores WebAuthn public key credentials (no wallet data)
|
|
416
|
+
- All wallet encryption/decryption happens in your browser
|
|
417
|
+
- Compatible with any Ethereum-compatible network
|
|
418
|
+
|
|
419
|
+
#### π‘ Operational Security Tips
|
|
420
|
+
- Test wallet functionality with small amounts first
|
|
421
|
+
- Verify signatures on Etherscan before sending large transactions
|
|
422
|
+
- Use different derived addresses for different purposes (privacy by design)
|
|
423
|
+
- Consider using stealth addresses for maximum transaction privacy
|
|
424
|
+
|
|
425
|
+
## React Integration
|
|
426
|
+
|
|
427
|
+
View live example: **https://d2u.w3hc.org/web3**
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import { createWeb3Passkey } from 'w3pk'
|
|
431
|
+
import { useState, useEffect } from 'react'
|
|
432
|
+
|
|
433
|
+
function App() {
|
|
434
|
+
const [w3pk, setW3pk] = useState(null)
|
|
435
|
+
const [user, setUser] = useState(null)
|
|
436
|
+
|
|
437
|
+
useEffect(() => {
|
|
438
|
+
const sdk = createWeb3Passkey({
|
|
439
|
+
apiBaseUrl: 'https://webauthn.w3hc.org',
|
|
440
|
+
onAuthStateChanged: (isAuth, user) => {
|
|
441
|
+
setUser(isAuth ? user : null)
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
setW3pk(sdk)
|
|
445
|
+
}, [])
|
|
446
|
+
|
|
447
|
+
const handleRegister = async () => {
|
|
448
|
+
const result = await w3pk.register({ username: 'alice' })
|
|
449
|
+
alert(`Save this mnemonic: ${result.mnemonic}`)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const handleLogin = async () => {
|
|
453
|
+
await w3pk.login()
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const handleSign = async () => {
|
|
457
|
+
const sig = await w3pk.signMessage('Hello!')
|
|
458
|
+
console.log('Signature:', sig)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return (
|
|
462
|
+
<div>
|
|
463
|
+
{!user ? (
|
|
464
|
+
<>
|
|
465
|
+
<button onClick={handleRegister}>Register</button>
|
|
466
|
+
<button onClick={handleLogin}>Login</button>
|
|
467
|
+
</>
|
|
468
|
+
) : (
|
|
469
|
+
<>
|
|
470
|
+
<p>Welcome {user.username}!</p>
|
|
471
|
+
<button onClick={handleSign}>Sign Message</button>
|
|
472
|
+
<button onClick={() => w3pk.logout()}>Logout</button>
|
|
473
|
+
</>
|
|
474
|
+
)}
|
|
475
|
+
</div>
|
|
476
|
+
)
|
|
477
|
+
}
|
|
478
|
+
```
|
|
136
479
|
|
|
137
|
-
##
|
|
480
|
+
## Development
|
|
138
481
|
|
|
139
482
|
```bash
|
|
140
|
-
#
|
|
141
|
-
pnpm
|
|
483
|
+
# Install dependencies
|
|
484
|
+
pnpm install
|
|
142
485
|
|
|
143
|
-
# Build
|
|
486
|
+
# Build for production
|
|
144
487
|
pnpm build
|
|
145
488
|
|
|
146
|
-
#
|
|
489
|
+
# Development mode with watch
|
|
147
490
|
pnpm dev
|
|
491
|
+
|
|
492
|
+
# Run tests
|
|
493
|
+
pnpm test # Run basic + comprehensive test suite
|
|
494
|
+
pnpm test:basic # Run basic functionality tests only
|
|
495
|
+
pnpm test:comprehensive # Run full 23-test comprehensive suite
|
|
496
|
+
|
|
497
|
+
# Publish to npm
|
|
498
|
+
pnpm prepublishOnly # Builds before publishing
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Test Coverage
|
|
502
|
+
|
|
503
|
+
The SDK includes a comprehensive test suite with **23 test cases** covering:
|
|
504
|
+
|
|
505
|
+
- β
**Core SDK functionality** - Constructor, configuration, environment detection
|
|
506
|
+
- β
**Wallet generation** - BIP39/BIP44 compliance, HD derivation, consistency
|
|
507
|
+
- β
**Encryption/decryption** - AES-GCM-256, key derivation, data roundtrips
|
|
508
|
+
- β
**Storage operations** - IndexedDB CRUD, multiple wallets, cleanup
|
|
509
|
+
- β
**Message signing** - Signature generation, address verification
|
|
510
|
+
- β
**HD wallet derivation** - Multi-index support, validation, consistency
|
|
511
|
+
- β
**Error handling** - Graceful failure scenarios
|
|
512
|
+
- β
**Integration testing** - End-to-end workflows
|
|
513
|
+
|
|
514
|
+
### Architecture
|
|
515
|
+
|
|
148
516
|
```
|
|
517
|
+
w3pk/
|
|
518
|
+
βββ src/
|
|
519
|
+
β βββ core/ # Main SDK class and configuration
|
|
520
|
+
β βββ wallet/ # Wallet generation, signing, storage
|
|
521
|
+
β βββ auth/ # WebAuthn authentication flows
|
|
522
|
+
β βββ stealth/ # Privacy-preserving stealth addresses
|
|
523
|
+
β βββ utils/ # API client, validation utilities
|
|
524
|
+
β βββ types/ # TypeScript type definitions
|
|
525
|
+
βββ test/ # Comprehensive test suite
|
|
526
|
+
βββ dist/ # Built output (CJS + ESM + types)
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Browser Compatibility
|
|
530
|
+
|
|
531
|
+
Requires browsers with WebAuthn support:
|
|
532
|
+
- Chrome/Edge 67+
|
|
533
|
+
- Firefox 60+
|
|
534
|
+
- Safari 13+
|
|
535
|
+
- All modern mobile browsers
|
|
149
536
|
|
|
150
537
|
## Support
|
|
151
538
|
|
|
@@ -155,4 +542,8 @@ Contact [Julien BΓ©ranger](https://github.com/julienbrg):
|
|
|
155
542
|
- Telegram: [@julienbrg](https://t.me/julienbrg)
|
|
156
543
|
- Twitter: [@julienbrg](https://twitter.com/julienbrg)
|
|
157
544
|
|
|
158
|
-
|
|
545
|
+
## License
|
|
546
|
+
|
|
547
|
+
GPL-3.0-or-later
|
|
548
|
+
|
|
549
|
+
<img src="https://bafkreid5xwxz4bed67bxb2wjmwsec4uhlcjviwy7pkzwoyu5oesjd3sp64.ipfs.w3s.link" alt="built-with-ethereum-w3hc" width="100"/>
|