@deserialize/multi-vm-wallet 1.4.12 → 1.5.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/.claude/settings.local.json +7 -1
- package/BUILD_OPTIMIZATION_PLAN.md +640 -0
- package/BUILD_RESULTS.md +282 -0
- package/BUN_MIGRATION.md +415 -0
- package/CHANGELOG_SECURITY.md +573 -0
- package/IMPLEMENTATION_SUMMARY.md +494 -0
- package/SECURITY_AUDIT.md +1124 -0
- package/bun.lock +553 -0
- package/dist/IChainWallet.js +0 -5
- package/dist/bip32Old.js +0 -885
- package/dist/bip32Small.js +0 -79
- package/dist/bipTest.js +0 -362
- package/dist/constant.js +0 -17
- package/dist/english.js +0 -1
- package/dist/evm/aa-service/index.d.ts +0 -5
- package/dist/evm/aa-service/index.js +0 -14
- package/dist/evm/aa-service/lib/account-adapter.d.ts +0 -22
- package/dist/evm/aa-service/lib/account-adapter.js +0 -24
- package/dist/evm/aa-service/lib/kernel-account.d.ts +0 -30
- package/dist/evm/aa-service/lib/kernel-account.js +2 -67
- package/dist/evm/aa-service/lib/kernel-modules.d.ts +0 -177
- package/dist/evm/aa-service/lib/kernel-modules.js +4 -202
- package/dist/evm/aa-service/lib/session-keys.d.ts +0 -118
- package/dist/evm/aa-service/lib/session-keys.js +7 -151
- package/dist/evm/aa-service/lib/type.d.ts +0 -55
- package/dist/evm/aa-service/lib/type.js +0 -10
- package/dist/evm/aa-service/services/account-abstraction.d.ts +0 -426
- package/dist/evm/aa-service/services/account-abstraction.js +0 -461
- package/dist/evm/aa-service/services/bundler.d.ts +0 -6
- package/dist/evm/aa-service/services/bundler.js +0 -54
- package/dist/evm/evm.d.ts +10 -67
- package/dist/evm/evm.js +339 -102
- package/dist/evm/index.js +0 -3
- package/dist/evm/script.js +3 -17
- package/dist/evm/smartWallet.d.ts +0 -173
- package/dist/evm/smartWallet.js +0 -206
- package/dist/evm/smartWallet.types.d.ts +0 -6
- package/dist/evm/smartWallet.types.js +0 -8
- package/dist/evm/transaction.utils.d.ts +0 -242
- package/dist/evm/transaction.utils.js +4 -320
- package/dist/evm/transactionParsing.d.ts +0 -11
- package/dist/evm/transactionParsing.js +28 -147
- package/dist/evm/utils.d.ts +0 -46
- package/dist/evm/utils.js +1 -57
- package/dist/helpers/index.d.ts +0 -4
- package/dist/helpers/index.js +8 -44
- package/dist/helpers/routeScan.js +0 -1
- package/dist/index.js +0 -1
- package/dist/old.js +0 -884
- package/dist/price.js +0 -1
- package/dist/price.types.js +0 -2
- package/dist/rate-limiter.d.ts +28 -0
- package/dist/rate-limiter.js +95 -0
- package/dist/retry-logic.d.ts +14 -0
- package/dist/retry-logic.js +120 -0
- package/dist/savings/index.d.ts +1 -0
- package/dist/savings/index.js +16 -2
- package/dist/savings/saving-manager.d.ts +46 -0
- package/dist/savings/saving-manager.js +176 -0
- package/dist/savings/savings-operations.d.ts +39 -0
- package/dist/savings/savings-operations.js +141 -0
- package/dist/savings/smart-savings.d.ts +0 -63
- package/dist/savings/smart-savings.js +0 -78
- package/dist/savings/types.d.ts +0 -69
- package/dist/savings/types.js +0 -7
- package/dist/savings/validation.d.ts +9 -0
- package/dist/savings/validation.js +85 -0
- package/dist/svm/constant.js +0 -1
- package/dist/svm/index.js +0 -1
- package/dist/svm/svm.d.ts +7 -13
- package/dist/svm/svm.js +262 -46
- package/dist/svm/transactionParsing.d.ts +0 -7
- package/dist/svm/transactionParsing.js +3 -41
- package/dist/svm/transactionSender.js +0 -9
- package/dist/svm/utils.d.ts +0 -12
- package/dist/svm/utils.js +9 -60
- package/dist/test.d.ts +0 -4
- package/dist/test.js +15 -95
- package/dist/transaction-utils.d.ts +38 -0
- package/dist/transaction-utils.js +168 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.js +0 -1
- package/dist/utils.js +0 -1
- package/dist/vm-validation.d.ts +11 -0
- package/dist/vm-validation.js +151 -0
- package/dist/vm.d.ts +14 -16
- package/dist/vm.js +64 -53
- package/dist/walletBip32.d.ts +2 -0
- package/dist/walletBip32.js +31 -66
- package/package.json +9 -4
- package/test-discovery.ts +235 -0
- package/test-pocket-discovery.ts +84 -0
- package/tsconfig.json +18 -11
- package/tsconfig.prod.json +10 -0
- package/utils/IChainWallet.ts +2 -0
- package/utils/evm/evm.ts +560 -39
- package/utils/rate-limiter.ts +179 -0
- package/utils/retry-logic.ts +271 -0
- package/utils/savings/EXAMPLES.md +883 -0
- package/utils/savings/SECURITY.md +731 -0
- package/utils/savings/index.ts +1 -1
- package/utils/savings/saving-manager.ts +656 -0
- package/utils/savings/savings-operations.ts +509 -0
- package/utils/savings/validation.ts +187 -0
- package/utils/svm/svm.ts +467 -20
- package/utils/test.ts +26 -3
- package/utils/transaction-utils.ts +394 -0
- package/utils/types.ts +100 -0
- package/utils/vm-validation.ts +280 -0
- package/utils/vm.ts +202 -24
- package/utils/walletBip32.ts +63 -3
- package/dist/IChainWallet.js.map +0 -1
- package/dist/bip32.d.ts +0 -9
- package/dist/bip32.js +0 -172
- package/dist/bip32.js.map +0 -1
- package/dist/bip32Old.js.map +0 -1
- package/dist/bip32Small.js.map +0 -1
- package/dist/bipTest.js.map +0 -1
- package/dist/constant.js.map +0 -1
- package/dist/english.js.map +0 -1
- package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +0 -20
- package/dist/evm/SMART_WALLET_EXAMPLES.js +0 -451
- package/dist/evm/SMART_WALLET_EXAMPLES.js.map +0 -1
- package/dist/evm/aa-service/index.js.map +0 -1
- package/dist/evm/aa-service/lib/account-adapter.js.map +0 -1
- package/dist/evm/aa-service/lib/kernel-account.js.map +0 -1
- package/dist/evm/aa-service/lib/kernel-modules.js.map +0 -1
- package/dist/evm/aa-service/lib/session-keys.js.map +0 -1
- package/dist/evm/aa-service/lib/type.js.map +0 -1
- package/dist/evm/aa-service/services/account-abstraction.js.map +0 -1
- package/dist/evm/aa-service/services/bundler.js.map +0 -1
- package/dist/evm/evm.js.map +0 -1
- package/dist/evm/index.js.map +0 -1
- package/dist/evm/script.js.map +0 -1
- package/dist/evm/smartWallet.js.map +0 -1
- package/dist/evm/smartWallet.types.js.map +0 -1
- package/dist/evm/transaction.utils.js.map +0 -1
- package/dist/evm/transactionParsing.js.map +0 -1
- package/dist/evm/utils.js.map +0 -1
- package/dist/helpers/index.js.map +0 -1
- package/dist/helpers/routeScan.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/old.js.map +0 -1
- package/dist/price.js.map +0 -1
- package/dist/price.types.js.map +0 -1
- package/dist/privacy/artifact-manager.d.ts +0 -117
- package/dist/privacy/artifact-manager.js +0 -251
- package/dist/privacy/artifact-manager.js.map +0 -1
- package/dist/privacy/broadcaster-client.d.ts +0 -166
- package/dist/privacy/broadcaster-client.js +0 -261
- package/dist/privacy/broadcaster-client.js.map +0 -1
- package/dist/privacy/index.d.ts +0 -34
- package/dist/privacy/index.js +0 -56
- package/dist/privacy/index.js.map +0 -1
- package/dist/privacy/network-config.d.ts +0 -57
- package/dist/privacy/network-config.js +0 -118
- package/dist/privacy/network-config.js.map +0 -1
- package/dist/privacy/poi-helper.d.ts +0 -161
- package/dist/privacy/poi-helper.js +0 -249
- package/dist/privacy/poi-helper.js.map +0 -1
- package/dist/privacy/railgun-engine.d.ts +0 -135
- package/dist/privacy/railgun-engine.js +0 -205
- package/dist/privacy/railgun-engine.js.map +0 -1
- package/dist/privacy/railgun-privacy-wallet.d.ts +0 -288
- package/dist/privacy/railgun-privacy-wallet.js +0 -539
- package/dist/privacy/railgun-privacy-wallet.js.map +0 -1
- package/dist/privacy/types.d.ts +0 -229
- package/dist/privacy/types.js +0 -26
- package/dist/privacy/types.js.map +0 -1
- package/dist/savings/index.js.map +0 -1
- package/dist/savings/saving-actions.d.ts +0 -0
- package/dist/savings/saving-actions.js +0 -78
- package/dist/savings/saving-actions.js.map +0 -1
- package/dist/savings/savings-manager.d.ts +0 -126
- package/dist/savings/savings-manager.js +0 -234
- package/dist/savings/savings-manager.js.map +0 -1
- package/dist/savings/smart-savings.js.map +0 -1
- package/dist/savings/types.js.map +0 -1
- package/dist/svm/constant.js.map +0 -1
- package/dist/svm/index.js.map +0 -1
- package/dist/svm/svm.js.map +0 -1
- package/dist/svm/transactionParsing.js.map +0 -1
- package/dist/svm/transactionSender.js.map +0 -1
- package/dist/svm/utils.js.map +0 -1
- package/dist/test.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/vm.js.map +0 -1
- package/dist/walletBip32.js.map +0 -1
- package/utils/savings/saving-actions.ts +0 -92
- package/utils/savings/savings-manager.ts +0 -271
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
# Security Guide for Savings Feature
|
|
2
|
+
|
|
3
|
+
This document provides security guidance for developers implementing the savings feature in their wallet applications (mobile apps, browser extensions, desktop apps).
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Security Model](#security-model)
|
|
8
|
+
2. [Two Implementation Patterns](#two-implementation-patterns)
|
|
9
|
+
3. [Secure Storage](#secure-storage)
|
|
10
|
+
4. [Authentication & Authorization](#authentication--authorization)
|
|
11
|
+
5. [Memory Management](#memory-management)
|
|
12
|
+
6. [Session Management](#session-management)
|
|
13
|
+
7. [Best Practices](#best-practices)
|
|
14
|
+
8. [Security Checklist](#security-checklist)
|
|
15
|
+
|
|
16
|
+
## Security Model
|
|
17
|
+
|
|
18
|
+
### SDK Responsibilities
|
|
19
|
+
|
|
20
|
+
This SDK provides:
|
|
21
|
+
- ✅ Cryptographic primitives for key derivation
|
|
22
|
+
- ✅ Input validation for all operations
|
|
23
|
+
- ✅ Memory cleanup utilities
|
|
24
|
+
- ✅ Two operation patterns (stateful and stateless)
|
|
25
|
+
- ✅ Security documentation and examples
|
|
26
|
+
|
|
27
|
+
### Implementer Responsibilities
|
|
28
|
+
|
|
29
|
+
Your application must handle:
|
|
30
|
+
- 🔐 **Secure Storage**: Encrypting and storing mnemonics/keys
|
|
31
|
+
- 🔐 **Authentication**: Verifying user identity (biometrics, password, PIN)
|
|
32
|
+
- 🔐 **Authorization**: Controlling access to wallet operations
|
|
33
|
+
- 🔐 **App Lifecycle**: Clearing memory when app backgrounds/closes
|
|
34
|
+
- 🔐 **User Education**: Teaching users about security best practices
|
|
35
|
+
|
|
36
|
+
## Two Implementation Patterns
|
|
37
|
+
|
|
38
|
+
Choose the pattern that best fits your application architecture:
|
|
39
|
+
|
|
40
|
+
### Pattern 1: Stateless Operations (Recommended for Extensions)
|
|
41
|
+
|
|
42
|
+
**Best for**: Browser extensions, short-lived sessions
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { SavingsOperations } from '@wallet/utils/savings/savings-operations';
|
|
46
|
+
|
|
47
|
+
// Create operations handler (no sensitive data stored)
|
|
48
|
+
const operations = new SavingsOperations();
|
|
49
|
+
|
|
50
|
+
// Each operation requires mnemonic
|
|
51
|
+
async function transferFromPocket(pocketIndex: number, to: string, amount: bigint) {
|
|
52
|
+
// Get mnemonic from secure storage (implementer handles this)
|
|
53
|
+
const mnemonic = await secureStorage.getMnemonic();
|
|
54
|
+
|
|
55
|
+
// Perform operation - mnemonic not cached
|
|
56
|
+
const result = await operations.transferFromPocket(
|
|
57
|
+
mnemonic,
|
|
58
|
+
{ accountIndex: pocketIndex, to, amount },
|
|
59
|
+
provider,
|
|
60
|
+
chain
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Mnemonic automatically cleared after operation
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Advantages**:
|
|
69
|
+
- No persistent mnemonic in memory
|
|
70
|
+
- Automatic cleanup after each operation
|
|
71
|
+
- Better for browser extension background scripts
|
|
72
|
+
|
|
73
|
+
### Pattern 2: Stateful Manager (Recommended for Mobile Apps)
|
|
74
|
+
|
|
75
|
+
**Best for**: Mobile apps, long-lived sessions
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { SavingsManager } from '@wallet/utils/savings/saving-manager';
|
|
79
|
+
|
|
80
|
+
let savingsManager: SavingsManager | null = null;
|
|
81
|
+
|
|
82
|
+
// Initialize when user authenticates
|
|
83
|
+
async function initializeWallet() {
|
|
84
|
+
const mnemonic = await secureStorage.getMnemonic();
|
|
85
|
+
savingsManager = new SavingsManager(mnemonic, chainConfig);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Use manager for operations
|
|
89
|
+
async function transferToPocket(pocketIndex: number, amount: string) {
|
|
90
|
+
if (!savingsManager) throw new Error('Wallet not initialized');
|
|
91
|
+
return await savingsManager.transferToPocket(walletClient, pocketIndex, amount);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Clean up when user locks wallet or app backgrounds
|
|
95
|
+
function lockWallet() {
|
|
96
|
+
if (savingsManager) {
|
|
97
|
+
savingsManager.dispose();
|
|
98
|
+
savingsManager = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Advantages**:
|
|
104
|
+
- Better performance (no repeated key derivation)
|
|
105
|
+
- Simpler code for multiple operations
|
|
106
|
+
- Suitable for authenticated sessions
|
|
107
|
+
|
|
108
|
+
## Secure Storage
|
|
109
|
+
|
|
110
|
+
### iOS (React Native / Swift)
|
|
111
|
+
|
|
112
|
+
Use iOS Keychain for maximum security:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// React Native example with react-native-keychain
|
|
116
|
+
import * as Keychain from 'react-native-keychain';
|
|
117
|
+
|
|
118
|
+
async function storeMnemonic(mnemonic: string) {
|
|
119
|
+
await Keychain.setGenericPassword(
|
|
120
|
+
'wallet-mnemonic',
|
|
121
|
+
mnemonic,
|
|
122
|
+
{
|
|
123
|
+
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
124
|
+
service: 'com.yourapp.wallet'
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function getMnemonic(): Promise<string> {
|
|
130
|
+
const credentials = await Keychain.getGenericPassword({
|
|
131
|
+
service: 'com.yourapp.wallet'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!credentials) {
|
|
135
|
+
throw new Error('Mnemonic not found');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return credentials.password;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Security Features**:
|
|
143
|
+
- Hardware-backed encryption (Secure Enclave on newer devices)
|
|
144
|
+
- Data protected by device passcode
|
|
145
|
+
- Automatic deletion on app uninstall
|
|
146
|
+
|
|
147
|
+
### Android (React Native / Kotlin)
|
|
148
|
+
|
|
149
|
+
Use Android Keystore:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// React Native example with react-native-keychain
|
|
153
|
+
import * as Keychain from 'react-native-keychain';
|
|
154
|
+
|
|
155
|
+
async function storeMnemonic(mnemonic: string) {
|
|
156
|
+
await Keychain.setGenericPassword(
|
|
157
|
+
'wallet-mnemonic',
|
|
158
|
+
mnemonic,
|
|
159
|
+
{
|
|
160
|
+
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
161
|
+
service: 'com.yourapp.wallet',
|
|
162
|
+
securityLevel: Keychain.SECURITY_LEVEL.SECURE_HARDWARE
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Security Features**:
|
|
169
|
+
- Hardware-backed encryption (TEE/StrongBox on supported devices)
|
|
170
|
+
- Biometric authentication integration
|
|
171
|
+
- Key material never leaves secure hardware
|
|
172
|
+
|
|
173
|
+
### Browser Extensions
|
|
174
|
+
|
|
175
|
+
Use extension storage APIs with encryption:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { encrypt, decrypt } from './crypto'; // Your encryption implementation
|
|
179
|
+
|
|
180
|
+
async function storeMnemonic(mnemonic: string, password: string) {
|
|
181
|
+
// Derive encryption key from user password
|
|
182
|
+
const encryptionKey = await deriveKey(password);
|
|
183
|
+
|
|
184
|
+
// Encrypt mnemonic
|
|
185
|
+
const encrypted = await encrypt(mnemonic, encryptionKey);
|
|
186
|
+
|
|
187
|
+
// Store in extension storage
|
|
188
|
+
await chrome.storage.local.set({
|
|
189
|
+
'encrypted-mnemonic': encrypted
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function getMnemonic(password: string): Promise<string> {
|
|
194
|
+
const { 'encrypted-mnemonic': encrypted } = await chrome.storage.local.get(
|
|
195
|
+
'encrypted-mnemonic'
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (!encrypted) {
|
|
199
|
+
throw new Error('Mnemonic not found');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Derive encryption key from password
|
|
203
|
+
const encryptionKey = await deriveKey(password);
|
|
204
|
+
|
|
205
|
+
// Decrypt mnemonic
|
|
206
|
+
return await decrypt(encrypted, encryptionKey);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Security Considerations**:
|
|
211
|
+
- Use strong encryption (AES-256-GCM)
|
|
212
|
+
- Derive encryption key using PBKDF2 or Argon2
|
|
213
|
+
- Never store encryption key in storage
|
|
214
|
+
- Clear decrypted mnemonic from memory after use
|
|
215
|
+
|
|
216
|
+
## Authentication & Authorization
|
|
217
|
+
|
|
218
|
+
### Biometric Authentication (Mobile)
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import TouchID from 'react-native-touch-id';
|
|
222
|
+
|
|
223
|
+
async function authenticateForTransaction(): Promise<boolean> {
|
|
224
|
+
try {
|
|
225
|
+
await TouchID.authenticate('Confirm transaction', {
|
|
226
|
+
title: 'Authentication Required',
|
|
227
|
+
fallbackLabel: 'Use Passcode',
|
|
228
|
+
passcodeFallback: true
|
|
229
|
+
});
|
|
230
|
+
return true;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error('Authentication failed:', error);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Use before sensitive operations
|
|
238
|
+
async function sendFromPocket(pocketIndex: number, to: string, amount: bigint) {
|
|
239
|
+
// Require authentication
|
|
240
|
+
const authenticated = await authenticateForTransaction();
|
|
241
|
+
if (!authenticated) {
|
|
242
|
+
throw new Error('Authentication required');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Proceed with operation
|
|
246
|
+
const mnemonic = await secureStorage.getMnemonic();
|
|
247
|
+
return await operations.transferFromPocket(mnemonic, { ... });
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Password Authentication (Browser Extensions)
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
// Session management for browser extensions
|
|
255
|
+
class SessionManager {
|
|
256
|
+
private decryptedMnemonic: string | null = null;
|
|
257
|
+
private sessionTimeout: number = 15 * 60 * 1000; // 15 minutes
|
|
258
|
+
private timeoutId?: NodeJS.Timeout;
|
|
259
|
+
|
|
260
|
+
async unlock(password: string): Promise<void> {
|
|
261
|
+
// Decrypt and cache mnemonic
|
|
262
|
+
this.decryptedMnemonic = await getMnemonic(password);
|
|
263
|
+
|
|
264
|
+
// Set auto-lock timer
|
|
265
|
+
this.resetTimeout();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
lock(): void {
|
|
269
|
+
// Clear mnemonic from memory
|
|
270
|
+
if (this.decryptedMnemonic) {
|
|
271
|
+
this.decryptedMnemonic = '';
|
|
272
|
+
this.decryptedMnemonic = null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.timeoutId) {
|
|
276
|
+
clearTimeout(this.timeoutId);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private resetTimeout(): void {
|
|
281
|
+
if (this.timeoutId) {
|
|
282
|
+
clearTimeout(this.timeoutId);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
this.timeoutId = setTimeout(() => {
|
|
286
|
+
this.lock();
|
|
287
|
+
}, this.sessionTimeout);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
getMnemonic(): string {
|
|
291
|
+
if (!this.decryptedMnemonic) {
|
|
292
|
+
throw new Error('Wallet locked');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Reset timeout on each access
|
|
296
|
+
this.resetTimeout();
|
|
297
|
+
|
|
298
|
+
return this.decryptedMnemonic;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Memory Management
|
|
304
|
+
|
|
305
|
+
### Using Stateful Manager
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// Initialize manager when user authenticates
|
|
309
|
+
let manager: SavingsManager | null = null;
|
|
310
|
+
|
|
311
|
+
function initManager(mnemonic: string) {
|
|
312
|
+
manager = new SavingsManager(mnemonic, chainConfig);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Clear memory when appropriate
|
|
316
|
+
function cleanup() {
|
|
317
|
+
if (manager) {
|
|
318
|
+
// Clear all cached data
|
|
319
|
+
manager.dispose();
|
|
320
|
+
manager = null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// App lifecycle hooks
|
|
325
|
+
// React Native example
|
|
326
|
+
import { AppState } from 'react-native';
|
|
327
|
+
|
|
328
|
+
AppState.addEventListener('change', (nextAppState) => {
|
|
329
|
+
if (nextAppState === 'background' || nextAppState === 'inactive') {
|
|
330
|
+
// Clear sensitive data when app backgrounds
|
|
331
|
+
cleanup();
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Using Stateless Operations
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// No persistent state - mnemonic cleared after each operation
|
|
340
|
+
const operations = new SavingsOperations();
|
|
341
|
+
|
|
342
|
+
async function transfer() {
|
|
343
|
+
// Get mnemonic only for this operation
|
|
344
|
+
const mnemonic = await secureStorage.getMnemonic();
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
return await operations.transferFromPocket(
|
|
348
|
+
mnemonic,
|
|
349
|
+
{ accountIndex: 0, to: '0x...', amount: 1000n },
|
|
350
|
+
provider,
|
|
351
|
+
chain
|
|
352
|
+
);
|
|
353
|
+
} finally {
|
|
354
|
+
// Mnemonic automatically cleared by operations class
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### JavaScript Memory Limitations
|
|
360
|
+
|
|
361
|
+
**Important**: JavaScript strings are immutable. When you "clear" a string, you're only removing your reference to it. The actual data remains in memory until garbage collected.
|
|
362
|
+
|
|
363
|
+
**Best Practices**:
|
|
364
|
+
1. Minimize the lifetime of sensitive data in memory
|
|
365
|
+
2. Call disposal methods when done
|
|
366
|
+
3. Remove all references to allow garbage collection
|
|
367
|
+
4. Consider using `ArrayBuffer` for highly sensitive operations (advanced)
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// Example with ArrayBuffer (advanced)
|
|
371
|
+
function secureStringToBuffer(str: string): ArrayBuffer {
|
|
372
|
+
const encoder = new TextEncoder();
|
|
373
|
+
return encoder.encode(str).buffer;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function clearBuffer(buffer: ArrayBuffer): void {
|
|
377
|
+
const view = new Uint8Array(buffer);
|
|
378
|
+
view.fill(0);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Session Management
|
|
383
|
+
|
|
384
|
+
### Mobile Apps
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { AppState } from 'react-native';
|
|
388
|
+
|
|
389
|
+
class WalletSession {
|
|
390
|
+
private manager: SavingsManager | null = null;
|
|
391
|
+
private lockTimer?: NodeJS.Timeout;
|
|
392
|
+
private autoLockDelay = 5 * 60 * 1000; // 5 minutes
|
|
393
|
+
|
|
394
|
+
constructor() {
|
|
395
|
+
// Listen for app state changes
|
|
396
|
+
AppState.addEventListener('change', this.handleAppStateChange);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async unlock(mnemonic: string) {
|
|
400
|
+
this.manager = new SavingsManager(mnemonic, chainConfig);
|
|
401
|
+
this.resetLockTimer();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
lock() {
|
|
405
|
+
if (this.manager) {
|
|
406
|
+
this.manager.dispose();
|
|
407
|
+
this.manager = null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (this.lockTimer) {
|
|
411
|
+
clearTimeout(this.lockTimer);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private resetLockTimer() {
|
|
416
|
+
if (this.lockTimer) {
|
|
417
|
+
clearTimeout(this.lockTimer);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.lockTimer = setTimeout(() => {
|
|
421
|
+
this.lock();
|
|
422
|
+
}, this.autoLockDelay);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private handleAppStateChange = (nextAppState: string) => {
|
|
426
|
+
if (nextAppState === 'background' || nextAppState === 'inactive') {
|
|
427
|
+
// Lock immediately when app backgrounds
|
|
428
|
+
this.lock();
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
getManager(): SavingsManager {
|
|
433
|
+
if (!this.manager) {
|
|
434
|
+
throw new Error('Wallet locked - authentication required');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Reset auto-lock timer on each access
|
|
438
|
+
this.resetLockTimer();
|
|
439
|
+
|
|
440
|
+
return this.manager;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Browser Extensions
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// Background script session management
|
|
449
|
+
class ExtensionSession {
|
|
450
|
+
private operations = new SavingsOperations();
|
|
451
|
+
private encryptedMnemonic: string | null = null;
|
|
452
|
+
private password: string | null = null;
|
|
453
|
+
|
|
454
|
+
async unlock(password: string): Promise<void> {
|
|
455
|
+
// Verify password by attempting decryption
|
|
456
|
+
const mnemonic = await this.decrypt(password);
|
|
457
|
+
|
|
458
|
+
// Store for session
|
|
459
|
+
this.password = password;
|
|
460
|
+
|
|
461
|
+
// Keep background script alive (Chrome MV3)
|
|
462
|
+
this.keepAlive();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
lock(): void {
|
|
466
|
+
this.password = null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async performOperation<T>(
|
|
470
|
+
operation: (mnemonic: string) => Promise<T>
|
|
471
|
+
): Promise<T> {
|
|
472
|
+
if (!this.password) {
|
|
473
|
+
throw new Error('Extension locked');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Decrypt mnemonic for this operation only
|
|
477
|
+
const mnemonic = await this.decrypt(this.password);
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
return await operation(mnemonic);
|
|
481
|
+
} finally {
|
|
482
|
+
// Mnemonic goes out of scope and will be garbage collected
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private async decrypt(password: string): Promise<string> {
|
|
487
|
+
// Implement decryption logic
|
|
488
|
+
// ...
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private keepAlive(): void {
|
|
492
|
+
// For Chrome MV3 extensions
|
|
493
|
+
setInterval(() => {
|
|
494
|
+
chrome.runtime.getPlatformInfo(() => {
|
|
495
|
+
// Keep background script alive
|
|
496
|
+
});
|
|
497
|
+
}, 20000);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## Best Practices
|
|
503
|
+
|
|
504
|
+
### 1. Defense in Depth
|
|
505
|
+
|
|
506
|
+
Implement multiple layers of security:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
class SecureWallet {
|
|
510
|
+
// Layer 1: Encrypted storage
|
|
511
|
+
private async getEncryptedMnemonic(): Promise<string> {
|
|
512
|
+
return await secureStorage.get('mnemonic');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Layer 2: Password/biometric authentication
|
|
516
|
+
private async authenticate(): Promise<boolean> {
|
|
517
|
+
return await biometric.authenticate();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Layer 3: Session timeout
|
|
521
|
+
private sessionManager = new SessionManager();
|
|
522
|
+
|
|
523
|
+
// Layer 4: Transaction confirmation
|
|
524
|
+
async sendTransaction(tx: Transaction): Promise<TransactionResult> {
|
|
525
|
+
// Require authentication
|
|
526
|
+
if (!await this.authenticate()) {
|
|
527
|
+
throw new Error('Authentication failed');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Check session
|
|
531
|
+
const mnemonic = this.sessionManager.getMnemonic();
|
|
532
|
+
|
|
533
|
+
// Show confirmation dialog
|
|
534
|
+
const confirmed = await this.confirmTransaction(tx);
|
|
535
|
+
if (!confirmed) {
|
|
536
|
+
throw new Error('Transaction cancelled');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Perform operation
|
|
540
|
+
return await this.operations.transferFromPocket(mnemonic, tx, ...);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### 2. User Education
|
|
546
|
+
|
|
547
|
+
Educate users about:
|
|
548
|
+
- Never sharing their mnemonic phrase
|
|
549
|
+
- Importance of secure device passcode
|
|
550
|
+
- Enabling biometric authentication
|
|
551
|
+
- Risks of rooted/jailbroken devices
|
|
552
|
+
- Importance of software updates
|
|
553
|
+
|
|
554
|
+
### 3. Rate Limiting
|
|
555
|
+
|
|
556
|
+
Implement rate limiting for failed authentication attempts:
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
class RateLimiter {
|
|
560
|
+
private attempts = 0;
|
|
561
|
+
private lockUntil: number = 0;
|
|
562
|
+
|
|
563
|
+
async checkAndIncrement(): Promise<void> {
|
|
564
|
+
if (Date.now() < this.lockUntil) {
|
|
565
|
+
const remainingMs = this.lockUntil - Date.now();
|
|
566
|
+
throw new Error(`Too many attempts. Try again in ${Math.ceil(remainingMs / 1000)}s`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
this.attempts++;
|
|
570
|
+
|
|
571
|
+
if (this.attempts >= 5) {
|
|
572
|
+
// Lock for 5 minutes after 5 failed attempts
|
|
573
|
+
this.lockUntil = Date.now() + 5 * 60 * 1000;
|
|
574
|
+
this.attempts = 0;
|
|
575
|
+
throw new Error('Too many failed attempts. Account locked for 5 minutes.');
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
reset(): void {
|
|
580
|
+
this.attempts = 0;
|
|
581
|
+
this.lockUntil = 0;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### 4. Input Validation
|
|
587
|
+
|
|
588
|
+
Always validate user inputs before operations:
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
// The SDK provides validation utilities
|
|
592
|
+
import { SavingsValidation } from '@wallet/utils/savings/validation';
|
|
593
|
+
|
|
594
|
+
async function validateAndSend(to: string, amount: string) {
|
|
595
|
+
try {
|
|
596
|
+
// Validate inputs
|
|
597
|
+
SavingsValidation.validateAddress(to, 'Recipient address');
|
|
598
|
+
SavingsValidation.validateAmountString(amount, 'Amount');
|
|
599
|
+
|
|
600
|
+
// Parse amount
|
|
601
|
+
const amountBigInt = parseUnits(amount, 18);
|
|
602
|
+
|
|
603
|
+
// Perform operation
|
|
604
|
+
return await operations.transferFromPocket(mnemonic, {
|
|
605
|
+
accountIndex: 0,
|
|
606
|
+
to,
|
|
607
|
+
amount: amountBigInt
|
|
608
|
+
}, provider, chain);
|
|
609
|
+
} catch (error) {
|
|
610
|
+
// Show user-friendly error message
|
|
611
|
+
throw new Error(`Invalid input: ${error.message}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### 5. Logging and Monitoring
|
|
617
|
+
|
|
618
|
+
Never log sensitive data:
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
// ❌ NEVER DO THIS
|
|
622
|
+
console.log('Mnemonic:', mnemonic);
|
|
623
|
+
console.log('Private key:', privateKey);
|
|
624
|
+
|
|
625
|
+
// ✅ DO THIS
|
|
626
|
+
console.log('Transaction sent:', {
|
|
627
|
+
hash: result.hash,
|
|
628
|
+
from: publicAddress,
|
|
629
|
+
to: recipientAddress,
|
|
630
|
+
// No sensitive data
|
|
631
|
+
});
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### 6. Testing
|
|
635
|
+
|
|
636
|
+
Test security features thoroughly:
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
describe('Wallet Security', () => {
|
|
640
|
+
it('should clear mnemonic on lock', async () => {
|
|
641
|
+
const manager = new SavingsManager(mnemonic, chainConfig);
|
|
642
|
+
manager.dispose();
|
|
643
|
+
|
|
644
|
+
// Verify internal state is cleared
|
|
645
|
+
expect((manager as any).mnemonic).toBe('');
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('should require authentication for transactions', async () => {
|
|
649
|
+
// Mock authentication failure
|
|
650
|
+
jest.spyOn(biometric, 'authenticate').mockRejectedValue(new Error('Failed'));
|
|
651
|
+
|
|
652
|
+
await expect(
|
|
653
|
+
wallet.sendTransaction({ ... })
|
|
654
|
+
).rejects.toThrow('Authentication failed');
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should lock after timeout', async () => {
|
|
658
|
+
const session = new SessionManager(5000); // 5s timeout
|
|
659
|
+
await session.unlock('password');
|
|
660
|
+
|
|
661
|
+
// Wait for timeout
|
|
662
|
+
await new Promise(resolve => setTimeout(resolve, 6000));
|
|
663
|
+
|
|
664
|
+
expect(() => session.getMnemonic()).toThrow('Wallet locked');
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
## Security Checklist
|
|
670
|
+
|
|
671
|
+
Before deploying your wallet application, verify:
|
|
672
|
+
|
|
673
|
+
### Secure Storage
|
|
674
|
+
- [ ] Mnemonic encrypted at rest
|
|
675
|
+
- [ ] Using platform-specific secure storage (Keychain/Keystore)
|
|
676
|
+
- [ ] Encryption key derived from strong password
|
|
677
|
+
- [ ] No sensitive data in logs or analytics
|
|
678
|
+
|
|
679
|
+
### Authentication
|
|
680
|
+
- [ ] Biometric authentication enabled (mobile)
|
|
681
|
+
- [ ] Strong password requirements (extensions)
|
|
682
|
+
- [ ] Rate limiting on failed attempts
|
|
683
|
+
- [ ] Session timeout implemented
|
|
684
|
+
|
|
685
|
+
### Memory Management
|
|
686
|
+
- [ ] Dispose methods called on lock/logout
|
|
687
|
+
- [ ] Sensitive data cleared on app background
|
|
688
|
+
- [ ] No persistent references to sensitive data
|
|
689
|
+
- [ ] Session management implemented
|
|
690
|
+
|
|
691
|
+
### Input Validation
|
|
692
|
+
- [ ] All user inputs validated
|
|
693
|
+
- [ ] Address checksums verified
|
|
694
|
+
- [ ] Amount limits enforced
|
|
695
|
+
- [ ] Transaction confirmations required
|
|
696
|
+
|
|
697
|
+
### User Experience
|
|
698
|
+
- [ ] Clear security warnings
|
|
699
|
+
- [ ] Transaction confirmation dialogs
|
|
700
|
+
- [ ] User education materials
|
|
701
|
+
- [ ] Secure backup instructions
|
|
702
|
+
|
|
703
|
+
### Testing
|
|
704
|
+
- [ ] Security features tested
|
|
705
|
+
- [ ] Attack scenarios simulated
|
|
706
|
+
- [ ] Third-party security audit completed
|
|
707
|
+
- [ ] Penetration testing performed
|
|
708
|
+
|
|
709
|
+
### Compliance
|
|
710
|
+
- [ ] Privacy policy published
|
|
711
|
+
- [ ] Terms of service published
|
|
712
|
+
- [ ] GDPR compliance (if applicable)
|
|
713
|
+
- [ ] Local regulations followed
|
|
714
|
+
|
|
715
|
+
## Additional Resources
|
|
716
|
+
|
|
717
|
+
- [OWASP Mobile Security Guide](https://owasp.org/www-project-mobile-security/)
|
|
718
|
+
- [Chrome Extension Security Best Practices](https://developer.chrome.com/docs/extensions/mv3/security/)
|
|
719
|
+
- [iOS Security Guide](https://support.apple.com/guide/security/welcome/web)
|
|
720
|
+
- [Android Security Best Practices](https://developer.android.com/privacy-and-security/security-tips)
|
|
721
|
+
|
|
722
|
+
## Reporting Security Issues
|
|
723
|
+
|
|
724
|
+
If you discover a security vulnerability in this SDK, please report it to:
|
|
725
|
+
- Email: security@yourcompany.com
|
|
726
|
+
- Use responsible disclosure practices
|
|
727
|
+
- Allow time for patching before public disclosure
|
|
728
|
+
|
|
729
|
+
## License
|
|
730
|
+
|
|
731
|
+
This security guide is provided as-is for educational purposes.
|