dop-wallet-v6 1.2.11 → 1.2.13
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/DOP_WALLET_V6_REACT_NATIVE_INTEGRATION_GUIDE.md +2174 -0
- package/SELECTIVE_TRANSPARENCY.md +207 -0
- package/WEB_WORKER_TROUBLESHOOTING.md +180 -0
- package/issuev3.md +78 -0
- package/metro.config.react-native.example.js +98 -0
- package/node-polyfills/os-polyfill.js +130 -0
- package/node-polyfills/package.json +9 -0
- package/node-polyfills/wasm-fallback.js +35 -0
- package/node-polyfills/web-worker-polyfill.js +77 -0
- package/package-rn-integration.json +30 -0
- package/package.json +6 -2
|
@@ -0,0 +1,2174 @@
|
|
|
1
|
+
# DOP Wallet v6 React Native Integration Guide
|
|
2
|
+
|
|
3
|
+
This comprehensive guide covers the integration of DOP Wallet v6 (dop-wallet-v6) into React Native applications, providing privacy-focused features for both ERC-20 tokens and ERC-721 NFTs on Ethereum, Polygon, and BNB Chain.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Prerequisites](#prerequisites)
|
|
8
|
+
2. [Installation](#installation)
|
|
9
|
+
3. [Initialize Engine](#initialize-engine)
|
|
10
|
+
4. [Wallet Management](#wallet-management)
|
|
11
|
+
- [Wallet Creation Functions](#wallet-creation-functions)
|
|
12
|
+
- [Input Requirements](#input-requirements)
|
|
13
|
+
- [React Native Wallet Creation](#react-native-wallet-creation)
|
|
14
|
+
- [Complete Examples](#complete-examples)
|
|
15
|
+
- [Error Handling](#error-handling)
|
|
16
|
+
- [Best Practices for React Native](#best-practices-for-react-native)
|
|
17
|
+
- [Migration from Old API](#migration-from-old-api)
|
|
18
|
+
5. [ERC-20 Token Features](#erc-20-token-features)
|
|
19
|
+
- [Balances](#erc-20-balances)
|
|
20
|
+
- [Syncing](#erc-20-syncing)
|
|
21
|
+
- [Encrypt, Send, Decrypt](#erc-20-encrypt-send-decrypt)
|
|
22
|
+
- [Transaction History](#erc-20-transaction-history)
|
|
23
|
+
- [Selective Transparency and Proof Verification](#erc-20-selective-transparency)
|
|
24
|
+
6. [ERC-721 NFT Features](#erc-721-nft-features)
|
|
25
|
+
- [Balances](#erc-721-balances)
|
|
26
|
+
- [Syncing](#erc-721-syncing)
|
|
27
|
+
- [Encrypt, Send, Decrypt](#erc-721-encrypt-send-decrypt)
|
|
28
|
+
- [Transaction History](#erc-721-transaction-history)
|
|
29
|
+
- [Selective Transparency and Proof Verification](#erc-721-selective-transparency)
|
|
30
|
+
7. [Advanced Features](#advanced-features)
|
|
31
|
+
- [Transaction History with Wallet Visibility](#transaction-history-visibility)
|
|
32
|
+
8. [Complete Example](#complete-example)
|
|
33
|
+
9. [Testing](#testing)
|
|
34
|
+
10. [Troubleshooting](#troubleshooting)
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
- React Native 0.65+
|
|
39
|
+
- Node.js 16+
|
|
40
|
+
- TypeScript support
|
|
41
|
+
- React Native async storage or compatible storage solution
|
|
42
|
+
- LevelDB compatible database for React Native
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
### 1. Install the DOP Wallet SDK
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install dop-wallet-v6
|
|
50
|
+
# or
|
|
51
|
+
yarn add dop-wallet-v6
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Install Required Peer Dependencies
|
|
55
|
+
|
|
56
|
+
The SDK requires AsyncStorage for React Native persistence:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install @react-native-async-storage/async-storage
|
|
60
|
+
# or
|
|
61
|
+
yarn add @react-native-async-storage/async-storage
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Note**: If you encounter issues with `startDopEngineReactNative` being undefined, ensure all dependencies are properly installed:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Force reinstall dependencies if needed
|
|
68
|
+
npm install --legacy-peer-deps
|
|
69
|
+
# or
|
|
70
|
+
yarn install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Install React Native Dependencies
|
|
74
|
+
|
|
75
|
+
Install the required React Native-specific dependencies:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install @react-native-async-storage/async-storage
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Or with Yarn:**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
yarn add @react-native-async-storage/async-storage
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
> **Important**: The SDK now uses an in-memory database (memdown) for React Native compatibility. AsyncStorage is used only for artifact caching and user preferences.
|
|
88
|
+
|
|
89
|
+
> **Important**: These polyfills are essential for the SDK to function properly. Missing them will cause errors like "Cannot read property 'call' of undefined" or other runtime failures.
|
|
90
|
+
|
|
91
|
+
### 3. Metro Configuration
|
|
92
|
+
|
|
93
|
+
Create or update your `metro.config.js` file to include Node.js core module aliases:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
97
|
+
|
|
98
|
+
const config = getDefaultConfig(__dirname);
|
|
99
|
+
|
|
100
|
+
// Add Node.js core module aliases
|
|
101
|
+
config.resolver.alias = {
|
|
102
|
+
...config.resolver.alias,
|
|
103
|
+
'stream': require.resolve('stream-browserify'),
|
|
104
|
+
'crypto': require.resolve('crypto-browserify'),
|
|
105
|
+
'http': require.resolve('stream-http'),
|
|
106
|
+
'https': require.resolve('https-browserify'),
|
|
107
|
+
'url': require.resolve('url'),
|
|
108
|
+
'util': require.resolve('util'),
|
|
109
|
+
'zlib': require.resolve('browserify-zlib'),
|
|
110
|
+
'path': require.resolve('path-browserify'),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Add required packages to Metro's resolver
|
|
114
|
+
config.resolver.extraNodeModules = {
|
|
115
|
+
...config.resolver.extraNodeModules,
|
|
116
|
+
'stream': require.resolve('stream-browserify'),
|
|
117
|
+
'crypto': require.resolve('crypto-browserify'),
|
|
118
|
+
'http': require.resolve('stream-http'),
|
|
119
|
+
'https': require.resolve('https-browserify'),
|
|
120
|
+
'url': require.resolve('url'),
|
|
121
|
+
'util': require.resolve('util'),
|
|
122
|
+
'zlib': require.resolve('browserify-zlib'),
|
|
123
|
+
'path': require.resolve('path-browserify'),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
module.exports = config;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Install the additional browserify modules:**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm install stream-browserify crypto-browserify stream-http https-browserify url util browserify-zlib path-browserify
|
|
133
|
+
|
|
134
|
+
# or with yarn
|
|
135
|
+
yarn add stream-browserify crypto-browserify stream-http https-browserify url util browserify-zlib path-browserify
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
> **Note**: We removed `react-native-level-fs` from Metro aliases as the SDK now uses `asyncstorage-down` directly for better React Native compatibility.
|
|
139
|
+
|
|
140
|
+
### 4. Essential Polyfills Setup
|
|
141
|
+
|
|
142
|
+
Import the required polyfills at the **very top** of your app's entry point (usually `App.js` or `index.js`) **before** importing the DOP Wallet SDK:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// CRITICAL: These imports must come FIRST, before any other imports
|
|
146
|
+
import 'react-native-get-random-values';
|
|
147
|
+
import 'react-native-url-polyfill/auto';
|
|
148
|
+
import { Buffer } from 'buffer';
|
|
149
|
+
|
|
150
|
+
// Make Buffer available globally
|
|
151
|
+
global.Buffer = global.Buffer || Buffer;
|
|
152
|
+
|
|
153
|
+
// Now import the DOP Wallet shims
|
|
154
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
155
|
+
|
|
156
|
+
// Your other imports...
|
|
157
|
+
import React from 'react';
|
|
158
|
+
import { AppRegistry } from 'react-native';
|
|
159
|
+
// ... rest of your app
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
> **⚠️ Critical Order**: The polyfills must be imported in this exact order before any other imports, including the DOP Wallet SDK imports. This prevents "Cannot read property 'call' of undefined" and similar errors.
|
|
163
|
+
|
|
164
|
+
### 5. Complete Dependencies List
|
|
165
|
+
|
|
166
|
+
Your `package.json` should include all these dependencies. Here's a complete reference:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"dependencies": {
|
|
171
|
+
"dop-wallet-v6": "^1.2.10",
|
|
172
|
+
"asyncstorage-down": "^4.2.0",
|
|
173
|
+
"@react-native-async-storage/async-storage": "^1.19.0",
|
|
174
|
+
"react-native-get-random-values": "^1.9.0",
|
|
175
|
+
"react-native-url-polyfill": "^2.0.0",
|
|
176
|
+
"buffer": "^6.0.3",
|
|
177
|
+
"util": "^0.12.5",
|
|
178
|
+
"stream-browserify": "^3.0.0",
|
|
179
|
+
"crypto-browserify": "^3.12.0",
|
|
180
|
+
"stream-http": "^3.2.0",
|
|
181
|
+
"https-browserify": "^1.0.0",
|
|
182
|
+
"url": "^0.11.3",
|
|
183
|
+
"browserify-zlib": "^0.2.0",
|
|
184
|
+
"path-browserify": "^1.0.1"
|
|
185
|
+
},
|
|
186
|
+
"devDependencies": {
|
|
187
|
+
"metro-config": "^0.76.0"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Install all at once:**
|
|
193
|
+
```bash
|
|
194
|
+
npm install dop-wallet-v6 asyncstorage-down @react-native-async-storage/async-storage react-native-get-random-values react-native-url-polyfill buffer util stream-browserify crypto-browserify stream-http https-browserify url browserify-zlib path-browserify
|
|
195
|
+
|
|
196
|
+
npm install --save-dev metro-config
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 6. React Native Shims
|
|
200
|
+
|
|
201
|
+
The SDK provides additional shims that handle crypto operations and other Node.js specific functionality. These are automatically loaded when you import the polyfill above.
|
|
202
|
+
|
|
203
|
+
### 7. Platform-specific Setup
|
|
204
|
+
|
|
205
|
+
#### iOS
|
|
206
|
+
Add to `ios/Podfile`:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
pod 'RNFS', :path => '../node_modules/react-native-fs'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Then run:
|
|
213
|
+
```bash
|
|
214
|
+
cd ios && pod install && cd ..
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Android
|
|
218
|
+
Add to `android/app/build.gradle` in the `android` section:
|
|
219
|
+
|
|
220
|
+
```gradle
|
|
221
|
+
android {
|
|
222
|
+
...
|
|
223
|
+
packagingOptions {
|
|
224
|
+
pickFirst '**/libc++_shared.so'
|
|
225
|
+
pickFirst '**/libjsc.so'
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 8. Verification Setup
|
|
231
|
+
|
|
232
|
+
You can verify your setup is working by creating a simple test:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// TestSetup.js
|
|
236
|
+
import 'react-native-get-random-values';
|
|
237
|
+
import 'react-native-url-polyfill/auto';
|
|
238
|
+
import { Buffer } from 'buffer';
|
|
239
|
+
|
|
240
|
+
global.Buffer = global.Buffer || Buffer;
|
|
241
|
+
|
|
242
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
243
|
+
|
|
244
|
+
console.log('✓ Polyfills loaded successfully');
|
|
245
|
+
console.log('✓ Buffer available:', typeof Buffer !== 'undefined');
|
|
246
|
+
console.log('✓ Crypto available:', typeof global.crypto !== 'undefined');
|
|
247
|
+
|
|
248
|
+
export const testSetup = () => {
|
|
249
|
+
try {
|
|
250
|
+
// Test crypto.getRandomValues
|
|
251
|
+
const array = new Uint8Array(16);
|
|
252
|
+
crypto.getRandomValues(array);
|
|
253
|
+
console.log('✓ crypto.getRandomValues working');
|
|
254
|
+
|
|
255
|
+
// Test Buffer
|
|
256
|
+
const buffer = Buffer.from('test');
|
|
257
|
+
console.log('✓ Buffer working');
|
|
258
|
+
|
|
259
|
+
return true;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error('❌ Setup verification failed:', error);
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Import and run this test in your App component to verify everything is working.
|
|
268
|
+
|
|
269
|
+
## Initialize Engine
|
|
270
|
+
|
|
271
|
+
The DOP Engine must be initialized before any wallet operations can be performed.
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { startDopEngineReactNative, ArtifactStore } from 'dop-wallet-v6';
|
|
275
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
276
|
+
|
|
277
|
+
// Configure artifact store for React Native
|
|
278
|
+
class ReactNativeArtifactStore implements ArtifactStore {
|
|
279
|
+
async getItem(key: string): Promise<string | null> {
|
|
280
|
+
return AsyncStorage.getItem(`artifact_${key}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async setItem(key: string, value: string): Promise<void> {
|
|
284
|
+
await AsyncStorage.setItem(`artifact_${key}`, value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async removeItem(key: string): Promise<void> {
|
|
288
|
+
await AsyncStorage.removeItem(`artifact_${key}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async clear(): Promise<void> {
|
|
292
|
+
const keys = await AsyncStorage.getAllKeys();
|
|
293
|
+
const artifactKeys = keys.filter(key => key.startsWith('artifact_'));
|
|
294
|
+
await AsyncStorage.multiRemove(artifactKeys);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export const initializeDopEngine = async () => {
|
|
299
|
+
try {
|
|
300
|
+
const walletSource = 'myapp'; // Your app identifier (max 16 chars, lowercase)
|
|
301
|
+
const shouldDebug = __DEV__; // Enable debug in development
|
|
302
|
+
const artifactStore = new ReactNativeArtifactStore();
|
|
303
|
+
const useNativeArtifacts = true; // TRUE for mobile
|
|
304
|
+
const skipMerkletreeScans = false; // FALSE to enable full functionality
|
|
305
|
+
const verboseScanLogging = __DEV__;
|
|
306
|
+
|
|
307
|
+
// Simplified React Native initialization - database handled internally
|
|
308
|
+
await startDopEngineReactNative(
|
|
309
|
+
walletSource,
|
|
310
|
+
shouldDebug,
|
|
311
|
+
artifactStore,
|
|
312
|
+
useNativeArtifacts,
|
|
313
|
+
skipMerkletreeScans,
|
|
314
|
+
verboseScanLogging
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
console.log('DOP Engine initialized successfully');
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error('Failed to initialize DOP Engine:', error);
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
> **Note**: The `startDopEngineReactNative` function automatically handles database setup with persistence using AsyncStorage. Data is stored in memory for fast access and automatically persisted to AsyncStorage for data retention between app sessions.
|
|
326
|
+
|
|
327
|
+
## Wallet Management
|
|
328
|
+
|
|
329
|
+
This section covers all wallet creation and management options available in DOP Wallet v6, including automatic mnemonic generation and importing existing mnemonics.
|
|
330
|
+
|
|
331
|
+
### Wallet Creation Functions
|
|
332
|
+
|
|
333
|
+
#### 1. `createOrImportDopWallet` - **Recommended** ⭐
|
|
334
|
+
|
|
335
|
+
This is the most comprehensive and user-friendly function that supports both creating new wallets and importing existing ones with React Native compatibility.
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { createOrImportDopWallet, testCircomlibjs } from 'dop-wallet-v6';
|
|
339
|
+
import { NetworkName } from 'dop-sharedmodels-v3';
|
|
340
|
+
|
|
341
|
+
// Create a new wallet with auto-generated mnemonic
|
|
342
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey);
|
|
343
|
+
|
|
344
|
+
// Create a new wallet with 24-word mnemonic
|
|
345
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
346
|
+
mnemonicStrength: 256 // 256 bits = 24 words
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Import an existing wallet
|
|
350
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
351
|
+
mnemonic: 'your existing twelve word mnemonic phrase goes here like this example'
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Parameters:**
|
|
356
|
+
- `encryptionKey`: string | Buffer | Uint8Array (32 bytes)
|
|
357
|
+
- `options.mnemonic?`: string - Import this mnemonic (if undefined, generates new)
|
|
358
|
+
- `options.creationBlockNumbers?`: Optional block numbers for faster sync
|
|
359
|
+
- `options.dopWalletDerivationIndex?`: number (default: 0)
|
|
360
|
+
- `options.timeout?`: number (default: 60000ms, recommended: 90000ms for React Native)
|
|
361
|
+
- `options.mnemonicStrength?`: 128 | 192 | 256 (default: 128 = 12 words)
|
|
362
|
+
|
|
363
|
+
**Returns:**
|
|
364
|
+
```typescript
|
|
365
|
+
{
|
|
366
|
+
walletInfo: DopWalletInfo, // Wallet details (ID, address, etc.)
|
|
367
|
+
mnemonic: string // The mnemonic (generated or imported)
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### 2. `createDopWalletSafe` - Safe Creation with Timeout
|
|
372
|
+
|
|
373
|
+
For when you already have a mnemonic and want safe creation with React Native compatibility.
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { createDopWalletSafe } from 'dop-wallet-v6';
|
|
377
|
+
|
|
378
|
+
const walletInfo = await createDopWalletSafe(
|
|
379
|
+
encryptionKey,
|
|
380
|
+
mnemonic,
|
|
381
|
+
creationBlockNumbers,
|
|
382
|
+
derivationIndex,
|
|
383
|
+
timeout // Recommended: 90000ms for React Native
|
|
384
|
+
);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### 3. `createDopWallet` - Basic Creation
|
|
388
|
+
|
|
389
|
+
The original function for creating wallets from existing mnemonics.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { createDopWallet } from 'dop-wallet-v6';
|
|
393
|
+
|
|
394
|
+
const walletInfo = await createDopWallet(
|
|
395
|
+
encryptionKey,
|
|
396
|
+
mnemonic,
|
|
397
|
+
creationBlockNumbers,
|
|
398
|
+
derivationIndex
|
|
399
|
+
);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Input Requirements
|
|
403
|
+
|
|
404
|
+
#### Encryption Key
|
|
405
|
+
The encryption key must be a **32-byte** value in one of these formats:
|
|
406
|
+
- `string`: Hex string (64 characters)
|
|
407
|
+
- `Buffer`: 32-byte buffer
|
|
408
|
+
- `Uint8Array`: 32-byte array
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
import { randomBytes } from 'crypto';
|
|
412
|
+
|
|
413
|
+
// Examples of valid encryption keys
|
|
414
|
+
const encryptionKey1 = randomBytes(32).toString('hex'); // string
|
|
415
|
+
const encryptionKey2 = randomBytes(32); // Buffer
|
|
416
|
+
const encryptionKey3 = new Uint8Array(32); // Uint8Array
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Mnemonic Options
|
|
420
|
+
|
|
421
|
+
| Strength | Bits | Words | Security Level |
|
|
422
|
+
|----------|------|-------|----------------|
|
|
423
|
+
| 128 | 128 | 12 | Standard ✅ |
|
|
424
|
+
| 192 | 192 | 18 | High |
|
|
425
|
+
| 256 | 256 | 24 | Maximum 🔒 |
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// Generate different mnemonic lengths
|
|
429
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
430
|
+
mnemonicStrength: 128 // 12 words (default)
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
434
|
+
mnemonicStrength: 192 // 18 words
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
438
|
+
mnemonicStrength: 256 // 24 words (most secure)
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### React Native Wallet Creation
|
|
443
|
+
|
|
444
|
+
For React Native apps, use these functions which include circomlibjs compatibility testing:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { createOrImportDopWallet, testCircomlibjs } from 'dop-wallet-v6';
|
|
448
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
449
|
+
import { NetworkName } from 'dop-sharedmodels-v3';
|
|
450
|
+
|
|
451
|
+
// Test compatibility first (optional but recommended)
|
|
452
|
+
const isCompatible = await testCircomlibjs();
|
|
453
|
+
if (!isCompatible) {
|
|
454
|
+
throw new Error('Cryptography not compatible with this environment');
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Create wallet with React Native optimizations
|
|
458
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
459
|
+
timeout: 90000, // Increase timeout for React Native
|
|
460
|
+
mnemonicStrength: 256 // 24 words for maximum security
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Complete Examples
|
|
465
|
+
|
|
466
|
+
#### Example 1: Create New Wallet (Auto-Generate Mnemonic)
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import { createOrImportDopWallet } from 'dop-wallet-v6';
|
|
470
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
471
|
+
import { randomBytes } from 'crypto';
|
|
472
|
+
import { NetworkName } from 'dop-sharedmodels-v3';
|
|
473
|
+
|
|
474
|
+
export const createNewWallet = async () => {
|
|
475
|
+
try {
|
|
476
|
+
// Generate encryption key
|
|
477
|
+
const encryptionKey = randomBytes(32);
|
|
478
|
+
|
|
479
|
+
// Optional: Specify creation block numbers for faster initial sync
|
|
480
|
+
const creationBlockNumbers = {
|
|
481
|
+
[NetworkName.Ethereum]: 18000000, // Recent block number
|
|
482
|
+
[NetworkName.Polygon]: 45000000,
|
|
483
|
+
[NetworkName.BNBChain]: 30000000,
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Create wallet with auto-generated mnemonic
|
|
487
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
488
|
+
creationBlockNumbers,
|
|
489
|
+
mnemonicStrength: 256, // 24 words for maximum security
|
|
490
|
+
timeout: 90000 // 90 seconds timeout for React Native
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
console.log('✅ New wallet created!');
|
|
494
|
+
console.log('Wallet ID:', walletInfo.id);
|
|
495
|
+
console.log('Address:', walletInfo.dopAddress);
|
|
496
|
+
console.log('Mnemonic (SAVE THIS!):', mnemonic);
|
|
497
|
+
|
|
498
|
+
// Store securely in AsyncStorage
|
|
499
|
+
await AsyncStorage.multiSet([
|
|
500
|
+
['encryption_key', encryptionKey.toString('hex')],
|
|
501
|
+
['wallet_mnemonic', mnemonic],
|
|
502
|
+
['wallet_id', walletInfo.id],
|
|
503
|
+
['wallet_address', walletInfo.dopAddress]
|
|
504
|
+
]);
|
|
505
|
+
|
|
506
|
+
return walletInfo;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error('Failed to create wallet:', error);
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### Example 2: Import Existing Wallet
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { createOrImportDopWallet } from 'dop-wallet-v6';
|
|
518
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
519
|
+
|
|
520
|
+
export const importWalletFromMnemonic = async (userMnemonic: string) => {
|
|
521
|
+
try {
|
|
522
|
+
// Get stored encryption key or create new one
|
|
523
|
+
let encryptionKey = await AsyncStorage.getItem('encryption_key');
|
|
524
|
+
if (!encryptionKey) {
|
|
525
|
+
encryptionKey = randomBytes(32).toString('hex');
|
|
526
|
+
await AsyncStorage.setItem('encryption_key', encryptionKey);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Import wallet from mnemonic
|
|
530
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
531
|
+
mnemonic: userMnemonic.trim().toLowerCase(),
|
|
532
|
+
timeout: 90000 // Increase timeout for React Native
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
console.log('✅ Wallet imported successfully!');
|
|
536
|
+
console.log('Wallet ID:', walletInfo.id);
|
|
537
|
+
console.log('Address:', walletInfo.dopAddress);
|
|
538
|
+
|
|
539
|
+
// Store wallet info
|
|
540
|
+
await AsyncStorage.multiSet([
|
|
541
|
+
['wallet_mnemonic', mnemonic],
|
|
542
|
+
['wallet_id', walletInfo.id],
|
|
543
|
+
['wallet_address', walletInfo.dopAddress]
|
|
544
|
+
]);
|
|
545
|
+
|
|
546
|
+
return walletInfo;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (error.message.includes('Invalid mnemonic')) {
|
|
549
|
+
throw new Error('Please check your mnemonic phrase and try again');
|
|
550
|
+
}
|
|
551
|
+
throw error;
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### Example 3: Export Wallet Mnemonic
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
import { getWalletMnemonic } from 'dop-wallet-v6';
|
|
560
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
561
|
+
|
|
562
|
+
export const exportWalletMnemonic = async (walletId: string) => {
|
|
563
|
+
try {
|
|
564
|
+
const encryptionKey = await AsyncStorage.getItem('encryption_key');
|
|
565
|
+
if (!encryptionKey) {
|
|
566
|
+
throw new Error('No encryption key found');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const mnemonic = await getWalletMnemonic(encryptionKey, walletId);
|
|
570
|
+
return mnemonic;
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error('Failed to export mnemonic:', error);
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Error Handling
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
try {
|
|
582
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
583
|
+
mnemonic: userInputMnemonic,
|
|
584
|
+
timeout: 90000
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
console.log('Wallet created:', walletInfo.id);
|
|
588
|
+
console.log('Address:', walletInfo.dopAddress);
|
|
589
|
+
|
|
590
|
+
// IMPORTANT: Securely store the mnemonic
|
|
591
|
+
await AsyncStorage.setItem('wallet_mnemonic', mnemonic);
|
|
592
|
+
|
|
593
|
+
} catch (error) {
|
|
594
|
+
if (error.message.includes('Invalid mnemonic')) {
|
|
595
|
+
// Handle invalid mnemonic input
|
|
596
|
+
console.error('The provided mnemonic is invalid');
|
|
597
|
+
} else if (error.message.includes('timed out')) {
|
|
598
|
+
// Handle timeout (usually circomlibjs hanging)
|
|
599
|
+
console.error('Wallet creation timed out, try restarting the app');
|
|
600
|
+
} else if (error.message.includes('Cryptography not compatible')) {
|
|
601
|
+
// Handle circomlibjs compatibility issues
|
|
602
|
+
console.error('Crypto library not compatible, check shims and polyfills');
|
|
603
|
+
} else {
|
|
604
|
+
// Handle other errors
|
|
605
|
+
console.error('Wallet creation failed:', error);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Best Practices for React Native
|
|
611
|
+
|
|
612
|
+
1. **Always test circomlibjs compatibility** before wallet creation:
|
|
613
|
+
```typescript
|
|
614
|
+
const isCompatible = await testCircomlibjs();
|
|
615
|
+
if (!isCompatible) {
|
|
616
|
+
throw new Error('Cryptography not compatible');
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
2. **Use increased timeouts** for React Native (90+ seconds):
|
|
621
|
+
```typescript
|
|
622
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
623
|
+
timeout: 90000
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
3. **Handle timeout errors gracefully** - suggest app restart:
|
|
628
|
+
```typescript
|
|
629
|
+
if (error.message.includes('timed out')) {
|
|
630
|
+
Alert.alert(
|
|
631
|
+
'Wallet Creation Timeout',
|
|
632
|
+
'Please restart the app and try again.',
|
|
633
|
+
[{ text: 'OK' }]
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
4. **Use secure storage** for encryption keys and mnemonics:
|
|
639
|
+
```typescript
|
|
640
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
641
|
+
|
|
642
|
+
// Store securely
|
|
643
|
+
await AsyncStorage.multiSet([
|
|
644
|
+
['encryption_key', encryptionKey.toString('hex')],
|
|
645
|
+
['wallet_mnemonic', mnemonic]
|
|
646
|
+
]);
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
5. **Validate user input mnemonics** before importing:
|
|
650
|
+
```typescript
|
|
651
|
+
if (userMnemonic && !Mnemonic.validate(userMnemonic)) {
|
|
652
|
+
throw new Error('Invalid mnemonic phrase');
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
6. **Use 24-word mnemonics** for maximum security:
|
|
657
|
+
```typescript
|
|
658
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
659
|
+
mnemonicStrength: 256 // 24 words
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Migration from Old API
|
|
664
|
+
|
|
665
|
+
If you're upgrading from older wallet creation methods:
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
// OLD WAY ❌
|
|
669
|
+
const walletInfo = await createDopWallet(encryptionKey, mnemonic, creationBlockNumbers);
|
|
670
|
+
|
|
671
|
+
// NEW WAY ✅
|
|
672
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
673
|
+
mnemonic: existingMnemonic, // or leave undefined to generate
|
|
674
|
+
creationBlockNumbers,
|
|
675
|
+
timeout: 90000 // Add timeout for React Native
|
|
676
|
+
});
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
The new API provides the mnemonic in the response, making it easier to handle both wallet creation and mnemonic storage in one step.
|
|
680
|
+
|
|
681
|
+
## ERC-20 Token Features
|
|
682
|
+
|
|
683
|
+
### ERC-20 Balances
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
import {
|
|
687
|
+
refreshBalances,
|
|
688
|
+
getSerializedERC20Balances,
|
|
689
|
+
getSerializedNFTBalances
|
|
690
|
+
} from 'dop-wallet-v6';
|
|
691
|
+
import { NetworkName, NETWORK_CONFIG, Chain } from 'dop-sharedmodels-v3';
|
|
692
|
+
|
|
693
|
+
export const refreshERC20Balances = async (
|
|
694
|
+
walletId: string,
|
|
695
|
+
networkName: NetworkName
|
|
696
|
+
) => {
|
|
697
|
+
try {
|
|
698
|
+
const network = NETWORK_CONFIG[networkName];
|
|
699
|
+
const chain: Chain = network.chain;
|
|
700
|
+
|
|
701
|
+
// Refresh balances for the wallet
|
|
702
|
+
await refreshBalances(chain, [walletId]);
|
|
703
|
+
|
|
704
|
+
console.log('Balance refresh initiated successfully');
|
|
705
|
+
return true;
|
|
706
|
+
} catch (error) {
|
|
707
|
+
console.error('Failed to refresh balances:', error);
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
export const getERC20Balances = async (tokenBalances: any) => {
|
|
713
|
+
try {
|
|
714
|
+
// Get serialized ERC20 balances from wallet token balances
|
|
715
|
+
const erc20Balances = getSerializedERC20Balances(tokenBalances);
|
|
716
|
+
|
|
717
|
+
return erc20Balances.map(balance => ({
|
|
718
|
+
tokenAddress: balance.tokenAddress,
|
|
719
|
+
amount: balance.amount.toString(),
|
|
720
|
+
symbol: balance.symbol,
|
|
721
|
+
decimals: balance.decimals,
|
|
722
|
+
}));
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error('Failed to get ERC20 balances:', error);
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
networkName: NetworkName
|
|
729
|
+
) => {
|
|
730
|
+
try {
|
|
731
|
+
const network = NETWORK_CONFIG[networkName];
|
|
732
|
+
const chain = network.chain;
|
|
733
|
+
|
|
734
|
+
await refreshBalances(chain, [walletId]);
|
|
735
|
+
console.log('Balance refresh initiated');
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error('Failed to refresh balances:', error);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### ERC-20 Syncing
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
import {
|
|
747
|
+
setOnUTXOMerkletreeScanCallback,
|
|
748
|
+
setOnTXIDMerkletreeScanCallback,
|
|
749
|
+
rescanFullUTXOMerkletreesAndWallets
|
|
750
|
+
} from 'dop-wallet-v6';
|
|
751
|
+
import { MerkletreeScanUpdateEvent } from 'dop-sharedmodels-v3';
|
|
752
|
+
|
|
753
|
+
export const setupSyncCallbacks = () => {
|
|
754
|
+
// Monitor UTXO scan progress
|
|
755
|
+
setOnUTXOMerkletreeScanCallback((scanData: MerkletreeScanUpdateEvent) => {
|
|
756
|
+
console.log('UTXO Scan Progress:', {
|
|
757
|
+
chain: scanData.chain,
|
|
758
|
+
status: scanData.scanStatus,
|
|
759
|
+
progress: scanData.progress,
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// Update UI with scan progress
|
|
763
|
+
// Example: setScanProgress(scanData.progress);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Monitor TXID scan progress
|
|
767
|
+
setOnTXIDMerkletreeScanCallback((scanData: MerkletreeScanUpdateEvent) => {
|
|
768
|
+
console.log('TXID Scan Progress:', {
|
|
769
|
+
chain: scanData.chain,
|
|
770
|
+
status: scanData.scanStatus,
|
|
771
|
+
progress: scanData.progress,
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
export const performFullSync = async (
|
|
777
|
+
walletId: string,
|
|
778
|
+
networkName: NetworkName
|
|
779
|
+
) => {
|
|
780
|
+
try {
|
|
781
|
+
const network = NETWORK_CONFIG[networkName];
|
|
782
|
+
const chain = network.chain;
|
|
783
|
+
|
|
784
|
+
await rescanFullUTXOMerkletreesAndWallets(chain, [walletId]);
|
|
785
|
+
console.log('Full sync initiated');
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error('Failed to perform full sync:', error);
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### ERC-20 Encrypt, Send, Decrypt
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
import {
|
|
797
|
+
generateTransferProofInternal,
|
|
798
|
+
populateEncrypt,
|
|
799
|
+
generateDecryptToOriginProof,
|
|
800
|
+
populateProvedTransfer,
|
|
801
|
+
gasEstimateForEncrypt,
|
|
802
|
+
gasEstimateForUnprovenTransfer,
|
|
803
|
+
mnemonicTo0xPKey
|
|
804
|
+
} from 'dop-wallet-v6';
|
|
805
|
+
import {
|
|
806
|
+
NetworkName,
|
|
807
|
+
NETWORK_CONFIG,
|
|
808
|
+
TXIDVersion,
|
|
809
|
+
DopERC20AmountRecipient,
|
|
810
|
+
TransactionGasDetails,
|
|
811
|
+
EVMGasType
|
|
812
|
+
} from 'dop-sharedmodels-v3';
|
|
813
|
+
import { Wallet, JsonRpcProvider } from 'ethers';
|
|
814
|
+
|
|
815
|
+
export const encryptERC20Token = async (
|
|
816
|
+
walletId: string,
|
|
817
|
+
networkName: NetworkName,
|
|
818
|
+
tokenAddress: string,
|
|
819
|
+
amount: string,
|
|
820
|
+
mnemonic: string
|
|
821
|
+
) => {
|
|
822
|
+
try {
|
|
823
|
+
const network = NETWORK_CONFIG[networkName];
|
|
824
|
+
const txidVersion = TXIDVersion.V2_PoseidonMerkle; // or V3_PoseidonMerkle
|
|
825
|
+
|
|
826
|
+
const erc20AmountRecipients: DopERC20AmountRecipient[] = [
|
|
827
|
+
{
|
|
828
|
+
tokenAddress,
|
|
829
|
+
amount: BigInt(amount),
|
|
830
|
+
recipientAddress: walletId, // Self-encrypt
|
|
831
|
+
}
|
|
832
|
+
];
|
|
833
|
+
|
|
834
|
+
// Get encrypt private key (simplified - in practice this should be derived securely)
|
|
835
|
+
const encryptPrivateKey = mnemonicTo0xPKey(mnemonic);
|
|
836
|
+
|
|
837
|
+
// Generate encrypt transaction
|
|
838
|
+
const encryptResponse = await populateEncrypt(
|
|
839
|
+
txidVersion,
|
|
840
|
+
networkName,
|
|
841
|
+
encryptPrivateKey,
|
|
842
|
+
erc20AmountRecipients,
|
|
843
|
+
[], // nftAmountRecipients
|
|
844
|
+
undefined // gasDetails - add if needed
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
// Broadcast using ethers (you'll need to implement this based on your provider setup)
|
|
848
|
+
const provider = new JsonRpcProvider(network.rpcUrl);
|
|
849
|
+
const wallet = new Wallet(encryptPrivateKey, provider);
|
|
850
|
+
const txResponse = await wallet.sendTransaction(encryptResponse.transaction);
|
|
851
|
+
|
|
852
|
+
console.log('Encryption transaction:', txResponse.hash);
|
|
853
|
+
return txResponse;
|
|
854
|
+
} catch (error) {
|
|
855
|
+
console.error('Failed to encrypt token:', error);
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
export const sendERC20Token = async (
|
|
861
|
+
walletId: string,
|
|
862
|
+
networkName: NetworkName,
|
|
863
|
+
tokenAddress: string,
|
|
864
|
+
amount: string,
|
|
865
|
+
recipientAddress: string,
|
|
866
|
+
encryptionKey: string,
|
|
867
|
+
mnemonic: string,
|
|
868
|
+
memoText?: string
|
|
869
|
+
) => {
|
|
870
|
+
try {
|
|
871
|
+
const network = NETWORK_CONFIG[networkName];
|
|
872
|
+
const txidVersion = TXIDVersion.V2_PoseidonMerkle; // or V3_PoseidonMerkle
|
|
873
|
+
|
|
874
|
+
const erc20AmountRecipients: DopERC20AmountRecipient[] = [
|
|
875
|
+
{
|
|
876
|
+
tokenAddress,
|
|
877
|
+
amount: BigInt(amount),
|
|
878
|
+
recipientAddress,
|
|
879
|
+
}
|
|
880
|
+
];
|
|
881
|
+
|
|
882
|
+
// First generate the proof
|
|
883
|
+
await generateTransferProofInternal(
|
|
884
|
+
txidVersion,
|
|
885
|
+
networkName,
|
|
886
|
+
walletId,
|
|
887
|
+
encryptionKey,
|
|
888
|
+
false, // showSenderAddressToRecipient
|
|
889
|
+
memoText,
|
|
890
|
+
erc20AmountRecipients,
|
|
891
|
+
[], // nftAmountRecipients
|
|
892
|
+
undefined, // broadcasterFeeERC20AmountRecipient
|
|
893
|
+
true, // sendWithPublicWallet
|
|
894
|
+
undefined, // overallBatchMinGasPrice
|
|
895
|
+
(progress) => console.log('Proof generation progress:', progress)
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
// Get gas details
|
|
899
|
+
const gasDetails: TransactionGasDetails = {
|
|
900
|
+
evmGasType: EVMGasType.Type2,
|
|
901
|
+
gasEstimate: 200000n,
|
|
902
|
+
gasPrice: 20000000000n, // 20 gwei
|
|
903
|
+
maxFeePerGas: 30000000000n, // 30 gwei
|
|
904
|
+
maxPriorityFeePerGas: 2000000000n, // 2 gwei
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
// Populate the proved transaction
|
|
908
|
+
const populateResponse = await populateProvedTransfer(
|
|
909
|
+
txidVersion,
|
|
910
|
+
networkName,
|
|
911
|
+
walletId,
|
|
912
|
+
false, // showSenderAddressToRecipient
|
|
913
|
+
memoText,
|
|
914
|
+
erc20AmountRecipients,
|
|
915
|
+
[], // nftAmountRecipients
|
|
916
|
+
undefined, // broadcasterFeeERC20AmountRecipient
|
|
917
|
+
true, // sendWithPublicWallet
|
|
918
|
+
undefined, // overallBatchMinGasPrice
|
|
919
|
+
gasDetails
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
// Broadcast using ethers
|
|
923
|
+
const privateKey = mnemonicTo0xPKey(mnemonic);
|
|
924
|
+
const provider = new JsonRpcProvider(network.rpcUrl);
|
|
925
|
+
const wallet = new Wallet(privateKey, provider);
|
|
926
|
+
const txResponse = await wallet.sendTransaction(populateResponse.transaction);
|
|
927
|
+
|
|
928
|
+
console.log('Transfer transaction:', txResponse.hash);
|
|
929
|
+
return txResponse;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
console.error('Failed to send token:', error);
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
export const decryptERC20Token = async (
|
|
937
|
+
walletId: string,
|
|
938
|
+
networkName: NetworkName,
|
|
939
|
+
originalEncryptTxid: string,
|
|
940
|
+
encryptionKey: string,
|
|
941
|
+
mnemonic: string,
|
|
942
|
+
tokenAddress: string,
|
|
943
|
+
amount: string
|
|
944
|
+
) => {
|
|
945
|
+
try {
|
|
946
|
+
const network = NETWORK_CONFIG[networkName];
|
|
947
|
+
const txidVersion = TXIDVersion.V2_PoseidonMerkle;
|
|
948
|
+
|
|
949
|
+
const erc20AmountRecipients: DopERC20AmountRecipient[] = [
|
|
950
|
+
{
|
|
951
|
+
tokenAddress,
|
|
952
|
+
amount: BigInt(amount),
|
|
953
|
+
recipientAddress: walletId, // Decrypt to self
|
|
954
|
+
}
|
|
955
|
+
];
|
|
956
|
+
|
|
957
|
+
// Generate decrypt proof
|
|
958
|
+
await generateDecryptToOriginProof(
|
|
959
|
+
originalEncryptTxid,
|
|
960
|
+
txidVersion,
|
|
961
|
+
networkName,
|
|
962
|
+
walletId,
|
|
963
|
+
encryptionKey,
|
|
964
|
+
erc20AmountRecipients,
|
|
965
|
+
[], // nftAmountRecipients
|
|
966
|
+
(progress) => console.log('Decrypt proof generation progress:', progress),
|
|
967
|
+
BigInt(amount)
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
// Note: You'll need to implement the transaction broadcasting part
|
|
971
|
+
// similar to the above examples using ethers and the cached proved transaction
|
|
972
|
+
|
|
973
|
+
console.log('Decrypt proof generated successfully');
|
|
974
|
+
return { success: true };
|
|
975
|
+
} catch (error) {
|
|
976
|
+
console.error('Failed to decrypt token:', error);
|
|
977
|
+
throw error;
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
### ERC-20 Transaction History
|
|
983
|
+
|
|
984
|
+
```typescript
|
|
985
|
+
import { getWalletTransactionHistory } from 'dop-wallet-v6';
|
|
986
|
+
import {
|
|
987
|
+
TransactionHistoryItem,
|
|
988
|
+
TransactionHistoryItemCategory,
|
|
989
|
+
NetworkName,
|
|
990
|
+
NETWORK_CONFIG,
|
|
991
|
+
Chain
|
|
992
|
+
} from 'dop-sharedmodels-v3';
|
|
993
|
+
|
|
994
|
+
export const getERC20TransactionHistory = async (
|
|
995
|
+
walletId: string,
|
|
996
|
+
networkName: NetworkName,
|
|
997
|
+
startIndex = 0
|
|
998
|
+
) => {
|
|
999
|
+
try {
|
|
1000
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1001
|
+
const chain: Chain = network.chain;
|
|
1002
|
+
|
|
1003
|
+
const history = await getWalletTransactionHistory(
|
|
1004
|
+
chain,
|
|
1005
|
+
walletId,
|
|
1006
|
+
startIndex
|
|
1007
|
+
);
|
|
1008
|
+
|
|
1009
|
+
return history;
|
|
1010
|
+
const chain = network.chain;
|
|
1011
|
+
|
|
1012
|
+
const history = await getTransactionHistory(
|
|
1013
|
+
walletId,
|
|
1014
|
+
chain,
|
|
1015
|
+
startIndex,
|
|
1016
|
+
maxCount
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
// Filter for ERC20 transactions
|
|
1020
|
+
const erc20History = history.filter(item =>
|
|
1021
|
+
item.category === TransactionHistoryItemCategory.TransferERC20Send ||
|
|
1022
|
+
item.category === TransactionHistoryItemCategory.TransferERC20Receive ||
|
|
1023
|
+
item.category === TransactionHistoryItemCategory.EncryptERC20 ||
|
|
1024
|
+
item.category === TransactionHistoryItemCategory.DecryptERC20
|
|
1025
|
+
);
|
|
1026
|
+
|
|
1027
|
+
return erc20History.map(item => ({
|
|
1028
|
+
id: item.id,
|
|
1029
|
+
timestamp: item.timestamp,
|
|
1030
|
+
category: item.category,
|
|
1031
|
+
txHash: item.txHash,
|
|
1032
|
+
blockNumber: item.blockNumber,
|
|
1033
|
+
tokenAddress: item.tokenAddress,
|
|
1034
|
+
amount: item.amount,
|
|
1035
|
+
recipientAddress: item.recipientAddress,
|
|
1036
|
+
senderAddress: item.senderAddress,
|
|
1037
|
+
memoText: item.memoText,
|
|
1038
|
+
status: item.status,
|
|
1039
|
+
}));
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
console.error('Failed to get transaction history:', error);
|
|
1042
|
+
throw error;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### ERC-20 Selective Transparency
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import {
|
|
1051
|
+
getDopWalletPrivateViewingKey,
|
|
1052
|
+
getWalletShareableViewingKey,
|
|
1053
|
+
createViewOnlyDopWallet,
|
|
1054
|
+
signWithWalletViewingKey
|
|
1055
|
+
} from 'dop-wallet-v6';
|
|
1056
|
+
|
|
1057
|
+
export const generateViewingKey = async (walletId: string) => {
|
|
1058
|
+
try {
|
|
1059
|
+
const shareableViewingKey = await getWalletShareableViewingKey(walletId);
|
|
1060
|
+
return shareableViewingKey;
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
console.error('Failed to generate viewing key:', error);
|
|
1063
|
+
throw error;
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
export const createViewOnlyWallet = async (
|
|
1068
|
+
encryptionKey: string,
|
|
1069
|
+
shareableViewingKey: string
|
|
1070
|
+
) => {
|
|
1071
|
+
try {
|
|
1072
|
+
const walletInfo = await createViewOnlyDopWallet(
|
|
1073
|
+
encryptionKey,
|
|
1074
|
+
shareableViewingKey,
|
|
1075
|
+
undefined
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
console.log('View-only wallet created:', walletInfo);
|
|
1079
|
+
return walletInfo;
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
console.error('Failed to create view-only wallet:', error);
|
|
1082
|
+
throw error;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
export const verifyTransactionProof = async (
|
|
1087
|
+
walletId: string,
|
|
1088
|
+
message: string
|
|
1089
|
+
) => {
|
|
1090
|
+
try {
|
|
1091
|
+
const signature = await signWithWalletViewingKey(walletId, message);
|
|
1092
|
+
return signature;
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
console.error('Failed to verify proof:', error);
|
|
1095
|
+
throw error;
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
## ERC-721 NFT Features
|
|
1101
|
+
|
|
1102
|
+
### ERC-721 Balances
|
|
1103
|
+
|
|
1104
|
+
```typescript
|
|
1105
|
+
export const getERC721Balances = async (tokenBalances: any) => {
|
|
1106
|
+
try {
|
|
1107
|
+
// Get serialized NFT balances from wallet token balances
|
|
1108
|
+
const nftBalances = getSerializedNFTBalances(tokenBalances);
|
|
1109
|
+
|
|
1110
|
+
return nftBalances.map(balance => ({
|
|
1111
|
+
contractAddress: balance.nftAddress,
|
|
1112
|
+
tokenId: balance.tokenSubID,
|
|
1113
|
+
tokenURI: balance.tokenURI,
|
|
1114
|
+
}));
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
console.error('Failed to get NFT balances:', error);
|
|
1117
|
+
throw error;
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
amount: balance.amount.toString(),
|
|
1121
|
+
nftTokenType: balance.nftTokenType,
|
|
1122
|
+
}));
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
console.error('Failed to get NFT balances:', error);
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
### ERC-721 Syncing
|
|
1131
|
+
|
|
1132
|
+
```typescript
|
|
1133
|
+
export const refreshNFTBalances = async (
|
|
1134
|
+
walletId: string,
|
|
1135
|
+
networkName: NetworkName
|
|
1136
|
+
) => {
|
|
1137
|
+
try {
|
|
1138
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1139
|
+
const chain = network.chain;
|
|
1140
|
+
|
|
1141
|
+
await refreshBalances(chain, [walletId]);
|
|
1142
|
+
console.log('NFT balance refresh initiated');
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
console.error('Failed to refresh NFT balances:', error);
|
|
1145
|
+
throw error;
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### ERC-721 Encrypt, Send, Decrypt
|
|
1151
|
+
|
|
1152
|
+
```typescript
|
|
1153
|
+
export const encryptNFT = async (
|
|
1154
|
+
walletId: string,
|
|
1155
|
+
networkName: NetworkName,
|
|
1156
|
+
contractAddress: string,
|
|
1157
|
+
tokenId: string
|
|
1158
|
+
) => {
|
|
1159
|
+
try {
|
|
1160
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1161
|
+
|
|
1162
|
+
const encryptTxRequest = {
|
|
1163
|
+
walletId,
|
|
1164
|
+
tokenAddress: contractAddress,
|
|
1165
|
+
tokenId,
|
|
1166
|
+
recipientAddress: null, // Self-encrypt
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
const proof = await generateEncryptProof(
|
|
1170
|
+
network.chain,
|
|
1171
|
+
encryptTxRequest
|
|
1172
|
+
);
|
|
1173
|
+
|
|
1174
|
+
const txResponse = await broadcastTransaction(
|
|
1175
|
+
network.chain,
|
|
1176
|
+
proof.transaction
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
console.log('NFT encryption transaction:', txResponse.hash);
|
|
1180
|
+
return txResponse;
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
console.error('Failed to encrypt NFT:', error);
|
|
1183
|
+
throw error;
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
export const sendNFT = async (
|
|
1188
|
+
walletId: string,
|
|
1189
|
+
networkName: NetworkName,
|
|
1190
|
+
contractAddress: string,
|
|
1191
|
+
tokenId: string,
|
|
1192
|
+
recipientAddress: string,
|
|
1193
|
+
memoText?: string
|
|
1194
|
+
) => {
|
|
1195
|
+
try {
|
|
1196
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1197
|
+
|
|
1198
|
+
const transferTxRequest = {
|
|
1199
|
+
walletId,
|
|
1200
|
+
tokenAddress: contractAddress,
|
|
1201
|
+
tokenId,
|
|
1202
|
+
recipientAddress,
|
|
1203
|
+
memoText,
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
const proof = await generateTransferProof(
|
|
1207
|
+
network.chain,
|
|
1208
|
+
transferTxRequest
|
|
1209
|
+
);
|
|
1210
|
+
|
|
1211
|
+
const txResponse = await broadcastTransaction(
|
|
1212
|
+
network.chain,
|
|
1213
|
+
proof.transaction
|
|
1214
|
+
);
|
|
1215
|
+
|
|
1216
|
+
console.log('NFT transfer transaction:', txResponse.hash);
|
|
1217
|
+
return txResponse;
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
console.error('Failed to send NFT:', error);
|
|
1220
|
+
throw error;
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
### ERC-721 Transaction History
|
|
1226
|
+
|
|
1227
|
+
```typescript
|
|
1228
|
+
export const getNFTTransactionHistory = async (
|
|
1229
|
+
walletId: string,
|
|
1230
|
+
networkName: NetworkName,
|
|
1231
|
+
startIndex = 0,
|
|
1232
|
+
maxCount = 50
|
|
1233
|
+
) => {
|
|
1234
|
+
try {
|
|
1235
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1236
|
+
const chain = network.chain;
|
|
1237
|
+
|
|
1238
|
+
const history = await getTransactionHistory(
|
|
1239
|
+
walletId,
|
|
1240
|
+
chain,
|
|
1241
|
+
startIndex,
|
|
1242
|
+
maxCount
|
|
1243
|
+
);
|
|
1244
|
+
|
|
1245
|
+
// Filter for NFT transactions
|
|
1246
|
+
const nftHistory = history.filter(item =>
|
|
1247
|
+
item.category === TransactionHistoryItemCategory.TransferNFTSend ||
|
|
1248
|
+
item.category === TransactionHistoryItemCategory.TransferNFTReceive ||
|
|
1249
|
+
item.category === TransactionHistoryItemCategory.EncryptNFT ||
|
|
1250
|
+
item.category === TransactionHistoryItemCategory.DecryptNFT
|
|
1251
|
+
);
|
|
1252
|
+
|
|
1253
|
+
return nftHistory.map(item => ({
|
|
1254
|
+
id: item.id,
|
|
1255
|
+
timestamp: item.timestamp,
|
|
1256
|
+
category: item.category,
|
|
1257
|
+
txHash: item.txHash,
|
|
1258
|
+
blockNumber: item.blockNumber,
|
|
1259
|
+
contractAddress: item.tokenAddress,
|
|
1260
|
+
tokenId: item.tokenId,
|
|
1261
|
+
recipientAddress: item.recipientAddress,
|
|
1262
|
+
senderAddress: item.senderAddress,
|
|
1263
|
+
memoText: item.memoText,
|
|
1264
|
+
status: item.status,
|
|
1265
|
+
}));
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
console.error('Failed to get NFT transaction history:', error);
|
|
1268
|
+
throw error;
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
### ERC-721 Selective Transparency
|
|
1274
|
+
|
|
1275
|
+
NFT selective transparency works the same way as ERC-20 tokens, using the same viewing key mechanisms described in the ERC-20 section.
|
|
1276
|
+
|
|
1277
|
+
## Advanced Features
|
|
1278
|
+
|
|
1279
|
+
### Transaction History with Wallet Visibility
|
|
1280
|
+
|
|
1281
|
+
```typescript
|
|
1282
|
+
export const getAllTransactionHistory = async (
|
|
1283
|
+
walletId: string,
|
|
1284
|
+
networkName: NetworkName,
|
|
1285
|
+
includeVisible = false,
|
|
1286
|
+
startIndex = 0,
|
|
1287
|
+
maxCount = 100
|
|
1288
|
+
) => {
|
|
1289
|
+
try {
|
|
1290
|
+
const network = NETWORK_CONFIG[networkName];
|
|
1291
|
+
const chain = network.chain;
|
|
1292
|
+
|
|
1293
|
+
const history = await getTransactionHistory(
|
|
1294
|
+
walletId,
|
|
1295
|
+
chain,
|
|
1296
|
+
startIndex,
|
|
1297
|
+
maxCount
|
|
1298
|
+
);
|
|
1299
|
+
|
|
1300
|
+
// Option to filter visible transactions
|
|
1301
|
+
const filteredHistory = includeVisible
|
|
1302
|
+
? history
|
|
1303
|
+
: history.filter(item => !item.isVisible);
|
|
1304
|
+
|
|
1305
|
+
return filteredHistory.map(item => ({
|
|
1306
|
+
id: item.id,
|
|
1307
|
+
timestamp: item.timestamp,
|
|
1308
|
+
category: item.category,
|
|
1309
|
+
txHash: item.txHash,
|
|
1310
|
+
blockNumber: item.blockNumber,
|
|
1311
|
+
tokenType: item.tokenType,
|
|
1312
|
+
tokenAddress: item.tokenAddress,
|
|
1313
|
+
tokenId: item.tokenId,
|
|
1314
|
+
amount: item.amount,
|
|
1315
|
+
recipientAddress: item.recipientAddress,
|
|
1316
|
+
senderAddress: item.senderAddress,
|
|
1317
|
+
memoText: item.memoText,
|
|
1318
|
+
status: item.status,
|
|
1319
|
+
isVisible: item.isVisible,
|
|
1320
|
+
gasFee: item.gasFee,
|
|
1321
|
+
}));
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
console.error('Failed to get transaction history:', error);
|
|
1324
|
+
throw error;
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
export const makeWalletVisible = async (
|
|
1329
|
+
walletId: string,
|
|
1330
|
+
networkName: NetworkName
|
|
1331
|
+
) => {
|
|
1332
|
+
try {
|
|
1333
|
+
// Implementation depends on specific visibility requirements
|
|
1334
|
+
// This might involve generating proofs or updating wallet settings
|
|
1335
|
+
console.log('Making wallet visible for network:', networkName);
|
|
1336
|
+
|
|
1337
|
+
// Add specific implementation based on DOP requirements
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
console.error('Failed to make wallet visible:', error);
|
|
1340
|
+
throw error;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
## Complete Example
|
|
1346
|
+
|
|
1347
|
+
Here's a complete setup showing the proper import order and React Native component:
|
|
1348
|
+
|
|
1349
|
+
### App.js (Entry Point)
|
|
1350
|
+
|
|
1351
|
+
```typescript
|
|
1352
|
+
// CRITICAL: Import polyfills FIRST, in this exact order
|
|
1353
|
+
import 'react-native-get-random-values';
|
|
1354
|
+
import 'react-native-url-polyfill/auto';
|
|
1355
|
+
import { Buffer } from 'buffer';
|
|
1356
|
+
|
|
1357
|
+
// Make Buffer available globally
|
|
1358
|
+
global.Buffer = global.Buffer || Buffer;
|
|
1359
|
+
|
|
1360
|
+
// Import DOP Wallet shims
|
|
1361
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
1362
|
+
|
|
1363
|
+
// Now safe to import React and other modules
|
|
1364
|
+
import React from 'react';
|
|
1365
|
+
import { AppRegistry } from 'react-native';
|
|
1366
|
+
import DopWalletExample from './DopWalletExample';
|
|
1367
|
+
|
|
1368
|
+
const App = () => {
|
|
1369
|
+
return <DopWalletExample />;
|
|
1370
|
+
};
|
|
1371
|
+
|
|
1372
|
+
AppRegistry.registerComponent('YourAppName', () => App);
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
### DopWalletExample.js (Main Component)
|
|
1376
|
+
|
|
1377
|
+
```typescript
|
|
1378
|
+
import React, { useState, useEffect } from 'react';
|
|
1379
|
+
import { View, Text, Button, Alert, StyleSheet } from 'react-native';
|
|
1380
|
+
import { NetworkName } from 'dop-sharedmodels-v3';
|
|
1381
|
+
import {
|
|
1382
|
+
initializeDopEngine,
|
|
1383
|
+
createNewWallet,
|
|
1384
|
+
getERC20Balances,
|
|
1385
|
+
sendERC20Token,
|
|
1386
|
+
setupSyncCallbacks,
|
|
1387
|
+
getAllTransactionHistory
|
|
1388
|
+
} from './dopWalletService'; // Your service file
|
|
1389
|
+
|
|
1390
|
+
const DopWalletExample = () => {
|
|
1391
|
+
const [walletId, setWalletId] = useState<string | null>(null);
|
|
1392
|
+
const [balances, setBalances] = useState<any[]>([]);
|
|
1393
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1394
|
+
const [syncProgress, setSyncProgress] = useState(0);
|
|
1395
|
+
|
|
1396
|
+
useEffect(() => {
|
|
1397
|
+
initializeWallet();
|
|
1398
|
+
setupSyncCallbacks();
|
|
1399
|
+
}, []);
|
|
1400
|
+
|
|
1401
|
+
const initializeWallet = async () => {
|
|
1402
|
+
try {
|
|
1403
|
+
setIsLoading(true);
|
|
1404
|
+
|
|
1405
|
+
// Initialize DOP Engine
|
|
1406
|
+
await initializeDopEngine();
|
|
1407
|
+
|
|
1408
|
+
// Create or load wallet
|
|
1409
|
+
const encryptionKey = 'your-secure-encryption-key';
|
|
1410
|
+
const wallet = await createNewWallet(encryptionKey);
|
|
1411
|
+
setWalletId(wallet.id);
|
|
1412
|
+
|
|
1413
|
+
console.log('Wallet initialized:', wallet.dopAddress);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
Alert.alert('Error', 'Failed to initialize wallet');
|
|
1416
|
+
console.error(error);
|
|
1417
|
+
} finally {
|
|
1418
|
+
setIsLoading(false);
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1422
|
+
const loadBalances = async () => {
|
|
1423
|
+
if (!walletId) return;
|
|
1424
|
+
|
|
1425
|
+
try {
|
|
1426
|
+
setIsLoading(true);
|
|
1427
|
+
const erc20Balances = await getERC20Balances(walletId, NetworkName.Ethereum);
|
|
1428
|
+
setBalances(erc20Balances);
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
Alert.alert('Error', 'Failed to load balances');
|
|
1431
|
+
console.error(error);
|
|
1432
|
+
} finally {
|
|
1433
|
+
setIsLoading(false);
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
const handleSendToken = async () => {
|
|
1438
|
+
if (!walletId) return;
|
|
1439
|
+
|
|
1440
|
+
try {
|
|
1441
|
+
setIsLoading(true);
|
|
1442
|
+
|
|
1443
|
+
const result = await sendERC20Token(
|
|
1444
|
+
walletId,
|
|
1445
|
+
NetworkName.Ethereum,
|
|
1446
|
+
'0x...', // Token contract address
|
|
1447
|
+
'1000000000000000000', // 1 token (18 decimals)
|
|
1448
|
+
'0x...', // Recipient address
|
|
1449
|
+
'Hello from DOP!'
|
|
1450
|
+
);
|
|
1451
|
+
|
|
1452
|
+
Alert.alert('Success', `Transaction sent: ${result.hash}`);
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
Alert.alert('Error', 'Failed to send token');
|
|
1455
|
+
console.error(error);
|
|
1456
|
+
} finally {
|
|
1457
|
+
setIsLoading(false);
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
return (
|
|
1462
|
+
<View style={styles.container}>
|
|
1463
|
+
<Text style={styles.title}>DOP Wallet Integration</Text>
|
|
1464
|
+
|
|
1465
|
+
{walletId && (
|
|
1466
|
+
<Text style={styles.walletId}>Wallet ID: {walletId}</Text>
|
|
1467
|
+
)}
|
|
1468
|
+
|
|
1469
|
+
{syncProgress > 0 && (
|
|
1470
|
+
<Text style={styles.progress}>
|
|
1471
|
+
Sync Progress: {(syncProgress * 100).toFixed(1)}%
|
|
1472
|
+
</Text>
|
|
1473
|
+
)}
|
|
1474
|
+
|
|
1475
|
+
<Button
|
|
1476
|
+
title="Load Balances"
|
|
1477
|
+
onPress={loadBalances}
|
|
1478
|
+
disabled={isLoading || !walletId}
|
|
1479
|
+
/>
|
|
1480
|
+
|
|
1481
|
+
<Button
|
|
1482
|
+
title="Send Token"
|
|
1483
|
+
onPress={handleSendToken}
|
|
1484
|
+
disabled={isLoading || !walletId}
|
|
1485
|
+
/>
|
|
1486
|
+
|
|
1487
|
+
{balances.length > 0 && (
|
|
1488
|
+
<View style={styles.balanceContainer}>
|
|
1489
|
+
<Text style={styles.balanceTitle}>Balances:</Text>
|
|
1490
|
+
{balances.map((balance, index) => (
|
|
1491
|
+
<Text key={index} style={styles.balanceItem}>
|
|
1492
|
+
{balance.tokenAddress}: {balance.spendable}
|
|
1493
|
+
</Text>
|
|
1494
|
+
))}
|
|
1495
|
+
</View>
|
|
1496
|
+
)}
|
|
1497
|
+
</View>
|
|
1498
|
+
);
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
const styles = StyleSheet.create({
|
|
1502
|
+
container: {
|
|
1503
|
+
flex: 1,
|
|
1504
|
+
padding: 20,
|
|
1505
|
+
justifyContent: 'center',
|
|
1506
|
+
},
|
|
1507
|
+
title: {
|
|
1508
|
+
fontSize: 24,
|
|
1509
|
+
fontWeight: 'bold',
|
|
1510
|
+
marginBottom: 20,
|
|
1511
|
+
textAlign: 'center',
|
|
1512
|
+
},
|
|
1513
|
+
walletId: {
|
|
1514
|
+
fontSize: 12,
|
|
1515
|
+
marginBottom: 10,
|
|
1516
|
+
textAlign: 'center',
|
|
1517
|
+
},
|
|
1518
|
+
progress: {
|
|
1519
|
+
fontSize: 14,
|
|
1520
|
+
marginBottom: 10,
|
|
1521
|
+
textAlign: 'center',
|
|
1522
|
+
},
|
|
1523
|
+
balanceContainer: {
|
|
1524
|
+
marginTop: 20,
|
|
1525
|
+
},
|
|
1526
|
+
balanceTitle: {
|
|
1527
|
+
fontSize: 18,
|
|
1528
|
+
fontWeight: 'bold',
|
|
1529
|
+
marginBottom: 10,
|
|
1530
|
+
},
|
|
1531
|
+
balanceItem: {
|
|
1532
|
+
fontSize: 14,
|
|
1533
|
+
marginBottom: 5,
|
|
1534
|
+
},
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
export default DopWalletExample;
|
|
1538
|
+
```
|
|
1539
|
+
|
|
1540
|
+
## Testing
|
|
1541
|
+
|
|
1542
|
+
### Unit Testing
|
|
1543
|
+
|
|
1544
|
+
Create test files to validate each integration step:
|
|
1545
|
+
|
|
1546
|
+
```typescript
|
|
1547
|
+
// __tests__/dopWallet.test.ts
|
|
1548
|
+
import { initializeDopEngine, createNewWallet } from '../dopWalletService';
|
|
1549
|
+
|
|
1550
|
+
describe('DOP Wallet Integration', () => {
|
|
1551
|
+
beforeAll(async () => {
|
|
1552
|
+
await initializeDopEngine();
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
test('should create a new wallet', async () => {
|
|
1556
|
+
const encryptionKey = 'test-encryption-key';
|
|
1557
|
+
const wallet = await createNewWallet(encryptionKey);
|
|
1558
|
+
|
|
1559
|
+
expect(wallet.id).toBeDefined();
|
|
1560
|
+
expect(wallet.dopAddress).toBeDefined();
|
|
1561
|
+
});
|
|
1562
|
+
|
|
1563
|
+
test('should load balances', async () => {
|
|
1564
|
+
// Add balance loading tests
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
test('should handle transactions', async () => {
|
|
1568
|
+
// Add transaction tests
|
|
1569
|
+
});
|
|
1570
|
+
});
|
|
1571
|
+
```
|
|
1572
|
+
|
|
1573
|
+
### Integration Testing
|
|
1574
|
+
|
|
1575
|
+
1. Test wallet creation and import
|
|
1576
|
+
2. Verify balance loading and syncing
|
|
1577
|
+
3. Test ERC-20 token operations
|
|
1578
|
+
4. Test ERC-721 NFT operations
|
|
1579
|
+
5. Verify transaction history
|
|
1580
|
+
6. Test selective transparency features
|
|
1581
|
+
|
|
1582
|
+
## Troubleshooting
|
|
1583
|
+
|
|
1584
|
+
### Wallet Creation Issues
|
|
1585
|
+
|
|
1586
|
+
#### ⚠️ **MOST COMMON ISSUE**: Wallet Creation Hangs Indefinitely
|
|
1587
|
+
|
|
1588
|
+
**Problem**: `createDopWallet()` hangs without errors, never completes in React Native.
|
|
1589
|
+
|
|
1590
|
+
**Root Cause**: `circomlibjs` (cryptographic library) hangs when trying to use Web Workers or WebAssembly in React Native.
|
|
1591
|
+
|
|
1592
|
+
**✅ SOLUTION**: Use the new safe wallet creation functions:
|
|
1593
|
+
|
|
1594
|
+
```typescript
|
|
1595
|
+
import { testCircomlibjs, createOrImportDopWallet } from 'dop-wallet-v6';
|
|
1596
|
+
|
|
1597
|
+
export const createNewWalletSafely = async (encryptionKey: string) => {
|
|
1598
|
+
try {
|
|
1599
|
+
// First test if circomlibjs is working
|
|
1600
|
+
const isCompatible = await testCircomlibjs();
|
|
1601
|
+
if (!isCompatible) {
|
|
1602
|
+
throw new Error('Cryptography not compatible with this environment');
|
|
1603
|
+
}
|
|
1604
|
+
console.log('✅ CircomLibJS test passed');
|
|
1605
|
+
|
|
1606
|
+
// Use the comprehensive creation method with timeout
|
|
1607
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1608
|
+
timeout: 90000, // 90 second timeout for React Native
|
|
1609
|
+
mnemonicStrength: 256 // 24 words for maximum security
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
console.log('✅ Wallet created successfully');
|
|
1613
|
+
console.log('Generated mnemonic:', mnemonic);
|
|
1614
|
+
return { walletInfo, mnemonic };
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
console.error('❌ Safe wallet creation failed:', error);
|
|
1617
|
+
|
|
1618
|
+
if (error.message.includes('timed out')) {
|
|
1619
|
+
throw new Error('Wallet creation timed out. This indicates circomlibjs compatibility issues. Please restart the app and try again.');
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
throw error;
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
```
|
|
1626
|
+
|
|
1627
|
+
#### "Invalid mnemonic phrase" Errors
|
|
1628
|
+
|
|
1629
|
+
**Problem**: Getting "Invalid mnemonic phrase provided" when importing wallet.
|
|
1630
|
+
|
|
1631
|
+
**Solutions**:
|
|
1632
|
+
1. **Check word count**: Ensure mnemonic has exactly 12, 18, or 24 words
|
|
1633
|
+
2. **Remove extra spaces**: Trim and normalize the mnemonic
|
|
1634
|
+
3. **Verify BIP39 wordlist**: Check if all words are valid BIP39 words
|
|
1635
|
+
|
|
1636
|
+
```typescript
|
|
1637
|
+
import { createOrImportDopWallet } from 'dop-wallet-v6';
|
|
1638
|
+
|
|
1639
|
+
const importWalletSafely = async (userMnemonic: string, encryptionKey: string) => {
|
|
1640
|
+
try {
|
|
1641
|
+
// Clean and validate the mnemonic
|
|
1642
|
+
const cleanMnemonic = userMnemonic.trim().toLowerCase();
|
|
1643
|
+
const words = cleanMnemonic.split(/\s+/);
|
|
1644
|
+
|
|
1645
|
+
if (![12, 18, 24].includes(words.length)) {
|
|
1646
|
+
throw new Error(`Invalid mnemonic: expected 12, 18, or 24 words, got ${words.length}`);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
// Import with validation
|
|
1650
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1651
|
+
mnemonic: cleanMnemonic,
|
|
1652
|
+
timeout: 90000
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
return { walletInfo, mnemonic };
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
if (error.message.includes('Invalid mnemonic')) {
|
|
1658
|
+
throw new Error('Please check your mnemonic phrase. Ensure it has 12, 18, or 24 valid words with no typos.');
|
|
1659
|
+
}
|
|
1660
|
+
throw error;
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
```
|
|
1664
|
+
|
|
1665
|
+
#### "Wallet creation timed out" Errors
|
|
1666
|
+
|
|
1667
|
+
**Problem**: Wallet creation times out after 60-90 seconds.
|
|
1668
|
+
|
|
1669
|
+
**Causes & Solutions**:
|
|
1670
|
+
|
|
1671
|
+
1. **CircomLibJS hanging** (most common):
|
|
1672
|
+
```typescript
|
|
1673
|
+
// Solution: Test circomlibjs first
|
|
1674
|
+
const isCompatible = await testCircomlibjs();
|
|
1675
|
+
if (!isCompatible) {
|
|
1676
|
+
// Restart the app and try again
|
|
1677
|
+
Alert.alert('Compatibility Issue', 'Please restart the app and try again.');
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
```
|
|
1681
|
+
|
|
1682
|
+
2. **Insufficient timeout for React Native**:
|
|
1683
|
+
```typescript
|
|
1684
|
+
// Solution: Increase timeout
|
|
1685
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1686
|
+
timeout: 120000 // 2 minutes for slower devices
|
|
1687
|
+
});
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
3. **Missing shims or polyfills**:
|
|
1691
|
+
```typescript
|
|
1692
|
+
// Solution: Ensure proper import order in App.js/index.js
|
|
1693
|
+
import 'react-native-get-random-values';
|
|
1694
|
+
import 'react-native-url-polyfill/auto';
|
|
1695
|
+
import { Buffer } from 'buffer';
|
|
1696
|
+
global.Buffer = global.Buffer || Buffer;
|
|
1697
|
+
import 'dop-wallet-v6/react-native-shims'; // Critical!
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
#### "Cryptography not compatible" Errors
|
|
1701
|
+
|
|
1702
|
+
**Problem**: `testCircomlibjs()` returns false or throws an error.
|
|
1703
|
+
|
|
1704
|
+
**Solutions**:
|
|
1705
|
+
|
|
1706
|
+
1. **Check shim loading**:
|
|
1707
|
+
```typescript
|
|
1708
|
+
// Ensure react-native-shims.js is imported BEFORE any wallet operations
|
|
1709
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
1710
|
+
import { testCircomlibjs } from 'dop-wallet-v6';
|
|
1711
|
+
```
|
|
1712
|
+
|
|
1713
|
+
2. **Verify Metro configuration**:
|
|
1714
|
+
```javascript
|
|
1715
|
+
// metro.config.js
|
|
1716
|
+
config.resolver.alias = {
|
|
1717
|
+
'crypto': require.resolve('crypto-browserify'),
|
|
1718
|
+
'stream': require.resolve('stream-browserify'),
|
|
1719
|
+
// ... other aliases
|
|
1720
|
+
};
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
3. **Force restart Metro**:
|
|
1724
|
+
```bash
|
|
1725
|
+
npx react-native start --reset-cache
|
|
1726
|
+
```
|
|
1727
|
+
|
|
1728
|
+
#### "Cannot read property 'call' of undefined" Errors
|
|
1729
|
+
|
|
1730
|
+
**Problem**: Getting undefined property errors during wallet operations.
|
|
1731
|
+
|
|
1732
|
+
**Root Cause**: Missing or incorrectly loaded polyfills.
|
|
1733
|
+
|
|
1734
|
+
**Solution**: Ensure correct polyfill order:
|
|
1735
|
+
|
|
1736
|
+
```typescript
|
|
1737
|
+
// At the very top of App.js or index.js (FIRST imports)
|
|
1738
|
+
import 'react-native-get-random-values';
|
|
1739
|
+
import 'react-native-url-polyfill/auto';
|
|
1740
|
+
import { Buffer } from 'buffer';
|
|
1741
|
+
|
|
1742
|
+
// Make Buffer global
|
|
1743
|
+
global.Buffer = global.Buffer || Buffer;
|
|
1744
|
+
|
|
1745
|
+
// Load DOP Wallet shims
|
|
1746
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
1747
|
+
|
|
1748
|
+
// Then your app imports
|
|
1749
|
+
import React from 'react';
|
|
1750
|
+
import { AppRegistry } from 'react-native';
|
|
1751
|
+
// ... rest of app
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1754
|
+
#### Performance Issues (Slow Wallet Creation)
|
|
1755
|
+
|
|
1756
|
+
**Problem**: Wallet creation takes 30+ seconds but eventually succeeds.
|
|
1757
|
+
|
|
1758
|
+
**Solutions**:
|
|
1759
|
+
|
|
1760
|
+
1. **Use creation block numbers** for faster sync:
|
|
1761
|
+
```typescript
|
|
1762
|
+
import { NetworkName } from 'dop-sharedmodels-v3';
|
|
1763
|
+
|
|
1764
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1765
|
+
creationBlockNumbers: {
|
|
1766
|
+
[NetworkName.Ethereum]: 18500000, // Recent block
|
|
1767
|
+
[NetworkName.Polygon]: 50000000,
|
|
1768
|
+
[NetworkName.BNBChain]: 35000000,
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
2. **Show loading UI** with progress indication:
|
|
1774
|
+
```typescript
|
|
1775
|
+
const [creationProgress, setCreationProgress] = useState('Initializing...');
|
|
1776
|
+
|
|
1777
|
+
const createWallet = async () => {
|
|
1778
|
+
try {
|
|
1779
|
+
setCreationProgress('Testing cryptography...');
|
|
1780
|
+
await testCircomlibjs();
|
|
1781
|
+
|
|
1782
|
+
setCreationProgress('Creating wallet...');
|
|
1783
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1784
|
+
timeout: 120000
|
|
1785
|
+
});
|
|
1786
|
+
|
|
1787
|
+
setCreationProgress('Wallet created successfully!');
|
|
1788
|
+
return { walletInfo, mnemonic };
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
setCreationProgress('Creation failed');
|
|
1791
|
+
throw error;
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
```
|
|
1795
|
+
|
|
1796
|
+
#### Debug Mode for Wallet Creation
|
|
1797
|
+
|
|
1798
|
+
When troubleshooting wallet creation issues, use this debug approach:
|
|
1799
|
+
|
|
1800
|
+
```typescript
|
|
1801
|
+
import { testCircomlibjs, createOrImportDopWallet } from 'dop-wallet-v6';
|
|
1802
|
+
|
|
1803
|
+
const debugWalletCreation = async (encryptionKey: string) => {
|
|
1804
|
+
console.log('🔍 Starting wallet creation debug...');
|
|
1805
|
+
|
|
1806
|
+
try {
|
|
1807
|
+
// Step 1: Test environment
|
|
1808
|
+
console.log('Step 1: Testing React Native environment...');
|
|
1809
|
+
console.log('- Buffer available:', typeof Buffer !== 'undefined');
|
|
1810
|
+
console.log('- Crypto available:', typeof crypto !== 'undefined');
|
|
1811
|
+
console.log('- TextEncoder available:', typeof TextEncoder !== 'undefined');
|
|
1812
|
+
|
|
1813
|
+
// Step 2: Test circomlibjs
|
|
1814
|
+
console.log('Step 2: Testing circomlibjs...');
|
|
1815
|
+
const startTime = Date.now();
|
|
1816
|
+
const isCompatible = await testCircomlibjs();
|
|
1817
|
+
const testDuration = Date.now() - startTime;
|
|
1818
|
+
console.log(`- CircomLibJS test: ${isCompatible ? '✅ PASS' : '❌ FAIL'} (${testDuration}ms)`);
|
|
1819
|
+
|
|
1820
|
+
if (!isCompatible) {
|
|
1821
|
+
throw new Error('CircomLibJS compatibility test failed');
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Step 3: Create wallet with debug logging
|
|
1825
|
+
console.log('Step 3: Creating wallet...');
|
|
1826
|
+
const creationStart = Date.now();
|
|
1827
|
+
|
|
1828
|
+
const { walletInfo, mnemonic } = await createOrImportDopWallet(encryptionKey, {
|
|
1829
|
+
timeout: 120000, // 2 minute timeout
|
|
1830
|
+
mnemonicStrength: 128 // Start with 12 words for faster creation
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
const creationDuration = Date.now() - creationStart;
|
|
1834
|
+
console.log(`✅ Wallet created successfully in ${creationDuration}ms`);
|
|
1835
|
+
console.log('- Wallet ID:', walletInfo.id);
|
|
1836
|
+
console.log('- Address:', walletInfo.dopAddress);
|
|
1837
|
+
console.log('- Mnemonic words:', mnemonic.split(' ').length);
|
|
1838
|
+
|
|
1839
|
+
return { walletInfo, mnemonic };
|
|
1840
|
+
|
|
1841
|
+
} catch (error) {
|
|
1842
|
+
console.error('❌ Debug wallet creation failed:', error);
|
|
1843
|
+
console.error('Error details:', {
|
|
1844
|
+
message: error.message,
|
|
1845
|
+
stack: error.stack
|
|
1846
|
+
});
|
|
1847
|
+
throw error;
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
```
|
|
1851
|
+
|
|
1852
|
+
### General React Native Issues
|
|
1853
|
+
|
|
1854
|
+
#### Missing Dependencies
|
|
1855
|
+
|
|
1856
|
+
**Error**: `Module not found` or `Cannot resolve module`
|
|
1857
|
+
|
|
1858
|
+
**Solution**: Install all required dependencies:
|
|
1859
|
+
```bash
|
|
1860
|
+
npm install dop-wallet-v6 @react-native-async-storage/async-storage react-native-get-random-values react-native-url-polyfill buffer util stream-browserify crypto-browserify
|
|
1861
|
+
```
|
|
1862
|
+
|
|
1863
|
+
#### Metro Bundle Issues
|
|
1864
|
+
|
|
1865
|
+
**Error**: Bundle transformation errors or module resolution failures
|
|
1866
|
+
|
|
1867
|
+
**Solution**: Update Metro configuration and clear cache:
|
|
1868
|
+
```bash
|
|
1869
|
+
# Clear Metro cache
|
|
1870
|
+
npx react-native start --reset-cache
|
|
1871
|
+
|
|
1872
|
+
# Clean project
|
|
1873
|
+
cd ios && xcodebuild clean && cd ..
|
|
1874
|
+
cd android && ./gradlew clean && cd ..
|
|
1875
|
+
```
|
|
1876
|
+
|
|
1877
|
+
#### iOS Build Issues
|
|
1878
|
+
|
|
1879
|
+
**Error**: Build failures on iOS
|
|
1880
|
+
|
|
1881
|
+
**Solution**:
|
|
1882
|
+
1. Install pods: `cd ios && pod install && cd ..`
|
|
1883
|
+
2. Clean build folder in Xcode
|
|
1884
|
+
3. Ensure iOS deployment target is 11.0+
|
|
1885
|
+
|
|
1886
|
+
#### Android Build Issues
|
|
1887
|
+
|
|
1888
|
+
**Error**: Build failures on Android
|
|
1889
|
+
|
|
1890
|
+
**Solution**:
|
|
1891
|
+
1. Ensure `minSdkVersion` is 21+
|
|
1892
|
+
2. Enable Hermes if disabled
|
|
1893
|
+
3. Add proguard rules if using release builds
|
|
1894
|
+
|
|
1895
|
+
```typescript
|
|
1896
|
+
import { testCircomlibjs } from 'dop-wallet-v6';
|
|
1897
|
+
|
|
1898
|
+
// Test circomlibjs in isolation first
|
|
1899
|
+
export const debugCircomlibjs = async () => {
|
|
1900
|
+
try {
|
|
1901
|
+
console.log('🔍 Testing circomlibjs components...');
|
|
1902
|
+
|
|
1903
|
+
// Test individual operations with timeout
|
|
1904
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
1905
|
+
setTimeout(() => reject(new Error('Operation timed out')), 30000)
|
|
1906
|
+
);
|
|
1907
|
+
|
|
1908
|
+
await Promise.race([
|
|
1909
|
+
testCircomlibjs(),
|
|
1910
|
+
timeoutPromise
|
|
1911
|
+
]);
|
|
1912
|
+
|
|
1913
|
+
console.log('✅ All circomlibjs tests passed');
|
|
1914
|
+
return true;
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
console.error('❌ CircomLibJS debug failed:', error);
|
|
1917
|
+
|
|
1918
|
+
// Provide specific guidance based on error
|
|
1919
|
+
if (error.message.includes('timed out')) {
|
|
1920
|
+
console.error('⚠️ SOLUTION: Add these to your App.js BEFORE importing dop-wallet-v6:');
|
|
1921
|
+
console.error(`
|
|
1922
|
+
// Add this at the very top of App.js:
|
|
1923
|
+
global.Worker = undefined;
|
|
1924
|
+
global.WebAssembly = undefined;
|
|
1925
|
+
global.navigator = { ...global.navigator, serviceWorker: undefined };
|
|
1926
|
+
`);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
return false;
|
|
1930
|
+
}
|
|
1931
|
+
};
|
|
1932
|
+
```
|
|
1933
|
+
|
|
1934
|
+
### Critical Setup Issues
|
|
1935
|
+
|
|
1936
|
+
#### 1. "startDopEngineReactNative is undefined" Error
|
|
1937
|
+
|
|
1938
|
+
**Cause**: Missing dependencies or failed module loading during import.
|
|
1939
|
+
|
|
1940
|
+
**Solution**:
|
|
1941
|
+
```bash
|
|
1942
|
+
# Ensure all dependencies are installed
|
|
1943
|
+
npm install --legacy-peer-deps
|
|
1944
|
+
# or
|
|
1945
|
+
yarn install
|
|
1946
|
+
|
|
1947
|
+
# If the issue persists, try installing memdown explicitly
|
|
1948
|
+
npm install memdown@^6.1.1
|
|
1949
|
+
```
|
|
1950
|
+
|
|
1951
|
+
**Alternative Import** (temporary workaround):
|
|
1952
|
+
```typescript
|
|
1953
|
+
// If main import fails, use direct import
|
|
1954
|
+
import { startDopEngineReactNative } from 'dop-wallet-v6/dist/services/dop/core/react-native-init';
|
|
1955
|
+
```
|
|
1956
|
+
|
|
1957
|
+
#### 2. "Cannot read property 'call' of undefined" Error
|
|
1958
|
+
|
|
1959
|
+
**Cause**: Missing required polyfills or incorrect import order.
|
|
1960
|
+
|
|
1961
|
+
**Solution**:
|
|
1962
|
+
```typescript
|
|
1963
|
+
// ❌ WRONG - This will cause the error
|
|
1964
|
+
import { createDopWallet } from 'dop-wallet-v6';
|
|
1965
|
+
import 'react-native-get-random-values';
|
|
1966
|
+
|
|
1967
|
+
// ✅ CORRECT - Polyfills must come FIRST
|
|
1968
|
+
import 'react-native-get-random-values';
|
|
1969
|
+
import 'react-native-url-polyfill/auto';
|
|
1970
|
+
import { Buffer } from 'buffer';
|
|
1971
|
+
global.Buffer = global.Buffer || Buffer;
|
|
1972
|
+
import 'dop-wallet-v6/react-native-shims';
|
|
1973
|
+
|
|
1974
|
+
import { createDopWallet } from 'dop-wallet-v6';
|
|
1975
|
+
```
|
|
1976
|
+
|
|
1977
|
+
#### 3. "Module not found" Errors for Node.js Core Modules
|
|
1978
|
+
|
|
1979
|
+
**Cause**: Missing Metro configuration for Node.js core modules.
|
|
1980
|
+
|
|
1981
|
+
**Error Examples**:
|
|
1982
|
+
- `Module not found: Can't resolve 'stream'`
|
|
1983
|
+
- `Module not found: Can't resolve 'crypto'`
|
|
1984
|
+
- `Module not found: Can't resolve 'url'`
|
|
1985
|
+
|
|
1986
|
+
**Solution**: Ensure your `metro.config.js` includes all the aliases mentioned in the Metro Configuration section above.
|
|
1987
|
+
|
|
1988
|
+
#### 3. "AsyncStorage has been removed from react-native core"
|
|
1989
|
+
|
|
1990
|
+
**Cause**: This error should no longer occur in the latest version, but if you see it:
|
|
1991
|
+
|
|
1992
|
+
**Solution**:
|
|
1993
|
+
```bash
|
|
1994
|
+
npm install @react-native-async-storage/async-storage
|
|
1995
|
+
```
|
|
1996
|
+
|
|
1997
|
+
#### 4. Bundle/Build Failures
|
|
1998
|
+
|
|
1999
|
+
**Cause**: Metro not properly configured or missing browserify dependencies.
|
|
2000
|
+
|
|
2001
|
+
**Solution**:
|
|
2002
|
+
1. Clear Metro cache: `npx react-native start --reset-cache`
|
|
2003
|
+
2. Ensure all browserify packages are installed:
|
|
2004
|
+
```bash
|
|
2005
|
+
npm install stream-browserify crypto-browserify stream-http https-browserify url browserify-zlib path-browserify
|
|
2006
|
+
```
|
|
2007
|
+
3. Restart Metro bundler completely
|
|
2008
|
+
|
|
2009
|
+
#### 5. Wallet Initialization Fails
|
|
2010
|
+
|
|
2011
|
+
**Error**: `Error creating wallet: TypeError: Cannot read property 'call' of undefined`
|
|
2012
|
+
|
|
2013
|
+
**Solution Checklist**:
|
|
2014
|
+
- [ ] All polyfills imported in correct order
|
|
2015
|
+
- [ ] Metro config includes Node.js aliases
|
|
2016
|
+
- [ ] All browserify dependencies installed
|
|
2017
|
+
- [ ] React Native shims imported
|
|
2018
|
+
- [ ] Buffer made available globally
|
|
2019
|
+
|
|
2020
|
+
#### 6. Level-FS Database Errors
|
|
2021
|
+
|
|
2022
|
+
**Error Examples**:
|
|
2023
|
+
- `TypeError: LevelFS.level is not a function (it is undefined)`
|
|
2024
|
+
- `TypeError: _reactNativeLevelFs.default is not a function (it is Object)`
|
|
2025
|
+
- `TypeError: 0, _$$_REQUIRE(...).level is not a function (it is undefined)`
|
|
2026
|
+
|
|
2027
|
+
**Root Cause**: `react-native-level-fs` is a filesystem shim, not a LevelDB constructor. The DOP Engine requires an `AbstractLevelDOWN` compatible database instance.
|
|
2028
|
+
|
|
2029
|
+
**Incorrect Approach** (causes the error):
|
|
2030
|
+
```typescript
|
|
2031
|
+
// ❌ Wrong - this returns a filesystem interface, not a database
|
|
2032
|
+
import level from 'react-native-level-fs';
|
|
2033
|
+
const db = level('./path'); // This is NOT a valid LevelDB instance
|
|
2034
|
+
```
|
|
2035
|
+
|
|
2036
|
+
**Correct Solution**:
|
|
2037
|
+
```typescript
|
|
2038
|
+
// ✅ Correct - use startDopEngineReactNative (SDK v1.2.10+)
|
|
2039
|
+
import { startDopEngineReactNative } from 'dop-wallet-v6';
|
|
2040
|
+
await startDopEngineReactNative(walletSource, shouldDebug, artifactStore, ...);
|
|
2041
|
+
```
|
|
2042
|
+
|
|
2043
|
+
**Migration Steps**:
|
|
2044
|
+
1. **Update imports**: Replace database imports with `startDopEngineReactNative`
|
|
2045
|
+
2. **Update initialization**: Use the new React Native-specific function
|
|
2046
|
+
3. **Remove database code**: No need to manage LevelDB instances manually
|
|
2047
|
+
4. **Test persistence**: Data now automatically persists between app sessions
|
|
2048
|
+
|
|
2049
|
+
### Common Runtime Issues
|
|
2050
|
+
|
|
2051
|
+
#### 1. Engine Initialization Fails
|
|
2052
|
+
- Ensure all dependencies are properly installed
|
|
2053
|
+
- Check that React Native shims are imported
|
|
2054
|
+
- Verify database path is writable
|
|
2055
|
+
- Confirm ArtifactStore implementation is correct
|
|
2056
|
+
|
|
2057
|
+
#### 2. Balance Loading Issues
|
|
2058
|
+
- Ensure network is properly configured
|
|
2059
|
+
- Check wallet is properly synced
|
|
2060
|
+
- Verify token contracts are supported
|
|
2061
|
+
- Check internet connectivity
|
|
2062
|
+
|
|
2063
|
+
#### 3. Transaction Failures
|
|
2064
|
+
- Check gas settings and network fees
|
|
2065
|
+
- Ensure sufficient balance for transactions
|
|
2066
|
+
- Verify recipient addresses are valid
|
|
2067
|
+
- Confirm network supports the transaction type
|
|
2068
|
+
|
|
2069
|
+
#### 4. Performance Issues
|
|
2070
|
+
- Use native artifacts on mobile (`useNativeArtifacts: true`)
|
|
2071
|
+
- Implement proper caching strategies
|
|
2072
|
+
- Optimize sync frequency
|
|
2073
|
+
- Consider using creation block numbers for faster initial sync
|
|
2074
|
+
|
|
2075
|
+
### Debug Mode
|
|
2076
|
+
|
|
2077
|
+
Enable debug mode during development for detailed logging:
|
|
2078
|
+
|
|
2079
|
+
```typescript
|
|
2080
|
+
const shouldDebug = __DEV__;
|
|
2081
|
+
const verboseScanLogging = __DEV__;
|
|
2082
|
+
|
|
2083
|
+
await startDopEngine(
|
|
2084
|
+
walletSource,
|
|
2085
|
+
db,
|
|
2086
|
+
shouldDebug, // Enable debug logging
|
|
2087
|
+
artifactStore,
|
|
2088
|
+
useNativeArtifacts,
|
|
2089
|
+
skipMerkletreeScans,
|
|
2090
|
+
verboseScanLogging // Enable verbose scan logging
|
|
2091
|
+
);
|
|
2092
|
+
```
|
|
2093
|
+
|
|
2094
|
+
### Complete Dependency Checklist
|
|
2095
|
+
|
|
2096
|
+
Ensure you have all these dependencies installed:
|
|
2097
|
+
|
|
2098
|
+
**Core Dependencies**:
|
|
2099
|
+
- ✅ `dop-wallet-v6`
|
|
2100
|
+
- ✅ `asyncstorage-down`
|
|
2101
|
+
- ✅ `@react-native-async-storage/async-storage`
|
|
2102
|
+
|
|
2103
|
+
**Polyfills**:
|
|
2104
|
+
- ✅ `react-native-get-random-values`
|
|
2105
|
+
- ✅ `react-native-url-polyfill`
|
|
2106
|
+
- ✅ `buffer`
|
|
2107
|
+
- ✅ `util`
|
|
2108
|
+
|
|
2109
|
+
**Browserify Modules**:
|
|
2110
|
+
- ✅ `stream-browserify`
|
|
2111
|
+
- ✅ `crypto-browserify`
|
|
2112
|
+
- ✅ `stream-http`
|
|
2113
|
+
- ✅ `https-browserify`
|
|
2114
|
+
- ✅ `url`
|
|
2115
|
+
- ✅ `util`
|
|
2116
|
+
- ✅ `browserify-zlib`
|
|
2117
|
+
- ✅ `path-browserify`
|
|
2118
|
+
|
|
2119
|
+
**Dev Dependencies**:
|
|
2120
|
+
- ✅ `metro-config` (if using custom Metro config)
|
|
2121
|
+
|
|
2122
|
+
### Quick Setup Verification Script
|
|
2123
|
+
|
|
2124
|
+
Use this script to verify your setup is correct:
|
|
2125
|
+
|
|
2126
|
+
```typescript
|
|
2127
|
+
// SetupVerification.js
|
|
2128
|
+
export const verifyDopWalletSetup = () => {
|
|
2129
|
+
const checks = {
|
|
2130
|
+
buffer: typeof Buffer !== 'undefined',
|
|
2131
|
+
crypto: typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function',
|
|
2132
|
+
url: typeof URL !== 'undefined',
|
|
2133
|
+
util: (() => {
|
|
2134
|
+
try {
|
|
2135
|
+
const util = require('util');
|
|
2136
|
+
return typeof util.promisify === 'function';
|
|
2137
|
+
} catch {
|
|
2138
|
+
return false;
|
|
2139
|
+
}
|
|
2140
|
+
})(),
|
|
2141
|
+
randomValues: (() => {
|
|
2142
|
+
try {
|
|
2143
|
+
const array = new Uint8Array(1);
|
|
2144
|
+
crypto.getRandomValues(array);
|
|
2145
|
+
return true;
|
|
2146
|
+
} catch {
|
|
2147
|
+
return false;
|
|
2148
|
+
}
|
|
2149
|
+
})(),
|
|
2150
|
+
};
|
|
2151
|
+
|
|
2152
|
+
console.log('DOP Wallet Setup Verification:');
|
|
2153
|
+
Object.entries(checks).forEach(([check, passed]) => {
|
|
2154
|
+
console.log(`${passed ? '✅' : '❌'} ${check}: ${passed ? 'OK' : 'FAILED'}`);
|
|
2155
|
+
});
|
|
2156
|
+
|
|
2157
|
+
const allPassed = Object.values(checks).every(Boolean);
|
|
2158
|
+
console.log(`\nOverall setup: ${allPassed ? '✅ READY' : '❌ NEEDS FIXES'}`);
|
|
2159
|
+
|
|
2160
|
+
return allPassed;
|
|
2161
|
+
};
|
|
2162
|
+
```
|
|
2163
|
+
|
|
2164
|
+
Run this before initializing the DOP Wallet SDK to ensure everything is properly configured.
|
|
2165
|
+
|
|
2166
|
+
## Conclusion
|
|
2167
|
+
|
|
2168
|
+
This guide provides a comprehensive foundation for integrating DOP Wallet v6 into React Native applications. The SDK enables privacy-focused features for both ERC-20 tokens and ERC-721 NFTs across multiple blockchain networks.
|
|
2169
|
+
|
|
2170
|
+
For additional support and advanced features, refer to the official DOP documentation and community resources.
|
|
2171
|
+
|
|
2172
|
+
---
|
|
2173
|
+
|
|
2174
|
+
**Note**: This integration guide is based on the DOP Wallet v6 SDK. Always refer to the latest documentation for the most up-to-date API references and best practices.
|