react-native-pirate-wallet 0.2.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/LICENSE-MIT +21 -0
- package/README.md +537 -0
- package/android/build.gradle +82 -0
- package/android/consumer-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/pirate/wallet/reactnative/PirateWalletReactNativeModule.kt +96 -0
- package/android/src/main/java/com/pirate/wallet/reactnative/PirateWalletReactNativePackage.kt +14 -0
- package/ios/PirateWalletReactNative.m +172 -0
- package/ios/PirateWalletReactNative.swift +40 -0
- package/package.json +64 -0
- package/react-native-pirate-wallet.podspec +19 -0
- package/react-native.config.js +10 -0
- package/scripts/assemble-ios-framework.js +124 -0
- package/scripts/resolve-android-packages.js +75 -0
- package/scripts/verify-package.js +128 -0
- package/src/index.d.ts +245 -0
- package/src/index.js +876 -0
- package/test/smoke.js +220 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
function getNativeModule() {
|
|
2
|
+
let reactNative
|
|
3
|
+
try {
|
|
4
|
+
reactNative = require('react-native')
|
|
5
|
+
} catch (error) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
'react-native is not available. Pass a native module explicitly when testing outside React Native.'
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const nativeModule =
|
|
12
|
+
reactNative &&
|
|
13
|
+
reactNative.NativeModules &&
|
|
14
|
+
reactNative.NativeModules.PirateWalletReactNative
|
|
15
|
+
|
|
16
|
+
if (
|
|
17
|
+
nativeModule == null ||
|
|
18
|
+
typeof nativeModule.invoke !== 'function'
|
|
19
|
+
) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'PirateWalletReactNative native module is not linked. Rebuild the app and check native installation.'
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
return nativeModule
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const AMOUNT_WIRE_KEYS = new Set([
|
|
28
|
+
'amount',
|
|
29
|
+
'arrrtoshis',
|
|
30
|
+
'available',
|
|
31
|
+
'balance',
|
|
32
|
+
'change',
|
|
33
|
+
'default_fee',
|
|
34
|
+
'fee',
|
|
35
|
+
'fee_opt',
|
|
36
|
+
'fee_per_output',
|
|
37
|
+
'input_total',
|
|
38
|
+
'max_fee',
|
|
39
|
+
'min_fee',
|
|
40
|
+
'new_balance',
|
|
41
|
+
'pending',
|
|
42
|
+
'required',
|
|
43
|
+
'spendable',
|
|
44
|
+
'total',
|
|
45
|
+
'total_amount',
|
|
46
|
+
'value'
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
function normalizeRequestValue(key, value) {
|
|
50
|
+
if (typeof value === 'bigint') {
|
|
51
|
+
return value.toString()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (key && AMOUNT_WIRE_KEYS.has(key) && typeof value === 'number') {
|
|
55
|
+
if (!Number.isSafeInteger(value)) {
|
|
56
|
+
throw new Error(`Unsafe integer amount for ${key}; pass decimal string or bigint instead.`)
|
|
57
|
+
}
|
|
58
|
+
return value.toString()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
return value.map(entry => normalizeRequestValue(null, entry))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (value && typeof value === 'object') {
|
|
66
|
+
const result = {}
|
|
67
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
68
|
+
result[entryKey] = normalizeRequestValue(entryKey, entryValue)
|
|
69
|
+
}
|
|
70
|
+
return result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return value
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildRequest(method, params = {}) {
|
|
77
|
+
const request = { method }
|
|
78
|
+
for (const [key, value] of Object.entries(params)) {
|
|
79
|
+
if (value !== undefined && value !== null) {
|
|
80
|
+
request[key] = normalizeRequestValue(key, value)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return JSON.stringify(request)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function unwrapEnvelope(responseJson, method, options = {}) {
|
|
87
|
+
const { camelizeResult = true } = options
|
|
88
|
+
let envelope
|
|
89
|
+
try {
|
|
90
|
+
envelope = JSON.parse(responseJson)
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(`Invalid JSON response from native bridge for ${method}: ${String(error)}`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!envelope || envelope.ok !== true) {
|
|
96
|
+
const message =
|
|
97
|
+
envelope && typeof envelope.error === 'string'
|
|
98
|
+
? envelope.error
|
|
99
|
+
: `Native request failed for ${method}`
|
|
100
|
+
throw new Error(message)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!Object.prototype.hasOwnProperty.call(envelope, 'result')) {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
// Some results are opaque payloads that must round-trip back into a later
|
|
107
|
+
// RPC unchanged (e.g. the pending tx from build_tx is fed straight into
|
|
108
|
+
// sign_tx, and the signed tx into broadcast_tx). Camelizing those rewrites
|
|
109
|
+
// their snake_case keys and the native deserializer then rejects them
|
|
110
|
+
// ("missing field total_amount"). Callers pass camelizeResult: false to keep
|
|
111
|
+
// such payloads verbatim.
|
|
112
|
+
return camelizeResult ? camelize(envelope.result) : envelope.result
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function camelize(value) {
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
return value.map(camelize)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (value && typeof value === 'object') {
|
|
121
|
+
const result = {}
|
|
122
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
123
|
+
const camelKey = key.replace(/_([a-z])/g, (_, chr) => chr.toUpperCase())
|
|
124
|
+
result[camelKey] = camelize(entry)
|
|
125
|
+
}
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pendingTransactionForWire(pending) {
|
|
133
|
+
if (!pending || typeof pending !== 'object' || Array.isArray(pending)) {
|
|
134
|
+
return pending
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const mapValue = (snakeKey, camelKey) =>
|
|
138
|
+
pending[snakeKey] !== undefined ? pending[snakeKey] : pending[camelKey]
|
|
139
|
+
|
|
140
|
+
const wire = {
|
|
141
|
+
id: pending.id,
|
|
142
|
+
outputs: pending.outputs,
|
|
143
|
+
total_amount: mapValue('total_amount', 'totalAmount'),
|
|
144
|
+
fee: pending.fee,
|
|
145
|
+
change: pending.change,
|
|
146
|
+
input_total: mapValue('input_total', 'inputTotal'),
|
|
147
|
+
num_inputs: mapValue('num_inputs', 'numInputs'),
|
|
148
|
+
expiry_height: mapValue('expiry_height', 'expiryHeight'),
|
|
149
|
+
created_at: mapValue('created_at', 'createdAt')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const key of Object.keys(wire)) {
|
|
153
|
+
if (wire[key] === undefined) {
|
|
154
|
+
delete wire[key]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return wire
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isSyncComplete(syncStatus) {
|
|
162
|
+
return (
|
|
163
|
+
syncStatus != null &&
|
|
164
|
+
typeof syncStatus.localHeight === 'number' &&
|
|
165
|
+
typeof syncStatus.targetHeight === 'number' &&
|
|
166
|
+
syncStatus.targetHeight > 0 &&
|
|
167
|
+
syncStatus.localHeight >= syncStatus.targetHeight
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isSyncing(syncStatus) {
|
|
172
|
+
return (
|
|
173
|
+
syncStatus != null &&
|
|
174
|
+
typeof syncStatus.localHeight === 'number' &&
|
|
175
|
+
typeof syncStatus.targetHeight === 'number' &&
|
|
176
|
+
syncStatus.targetHeight > 0 &&
|
|
177
|
+
syncStatus.localHeight < syncStatus.targetHeight
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function cloneCallbacks(callbacks) {
|
|
182
|
+
return {
|
|
183
|
+
onStatusChanged:
|
|
184
|
+
callbacks && typeof callbacks.onStatusChanged === 'function'
|
|
185
|
+
? callbacks.onStatusChanged
|
|
186
|
+
: null,
|
|
187
|
+
onUpdate:
|
|
188
|
+
callbacks && typeof callbacks.onUpdate === 'function'
|
|
189
|
+
? callbacks.onUpdate
|
|
190
|
+
: null,
|
|
191
|
+
onError:
|
|
192
|
+
callbacks && typeof callbacks.onError === 'function'
|
|
193
|
+
? callbacks.onError
|
|
194
|
+
: null
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function sanitizeAddressInfo(entry) {
|
|
199
|
+
if (!entry || typeof entry !== 'object') {
|
|
200
|
+
return entry
|
|
201
|
+
}
|
|
202
|
+
const { label, colorTag, ...rest } = entry
|
|
203
|
+
return rest
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function sanitizeAddressBalanceInfo(entry) {
|
|
207
|
+
if (!entry || typeof entry !== 'object') {
|
|
208
|
+
return entry
|
|
209
|
+
}
|
|
210
|
+
const { label, colorTag, ...rest } = entry
|
|
211
|
+
return rest
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function sanitizeKeyGroupInfo(entry) {
|
|
215
|
+
if (!entry || typeof entry !== 'object') {
|
|
216
|
+
return entry
|
|
217
|
+
}
|
|
218
|
+
const { label, ...rest } = entry
|
|
219
|
+
return rest
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
class PirateWalletAdvancedKeyManagement {
|
|
223
|
+
constructor(sdk) {
|
|
224
|
+
this.sdk = sdk
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async listKeyGroups(walletId) {
|
|
228
|
+
const result = await this.sdk._call('list_key_groups', { wallet_id: walletId })
|
|
229
|
+
return Array.isArray(result) ? result.map(sanitizeKeyGroupInfo) : result
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async exportKeyGroupKeys(walletId, keyId) {
|
|
233
|
+
return this.sdk._call('export_key_group_keys', {
|
|
234
|
+
wallet_id: walletId,
|
|
235
|
+
key_id: keyId
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async importSpendingKey(requestOrWalletId, birthdayHeight, saplingSpendingKey, ironwoodSpendingKey) {
|
|
240
|
+
const request =
|
|
241
|
+
typeof requestOrWalletId === 'object' && requestOrWalletId !== null
|
|
242
|
+
? requestOrWalletId
|
|
243
|
+
: {
|
|
244
|
+
walletId: requestOrWalletId,
|
|
245
|
+
birthdayHeight,
|
|
246
|
+
saplingSpendingKey,
|
|
247
|
+
ironwoodSpendingKey
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return this.sdk._call('import_spending_key', {
|
|
251
|
+
wallet_id: request.walletId,
|
|
252
|
+
sapling_key: request.saplingSpendingKey,
|
|
253
|
+
ironwood_key: request.ironwoodSpendingKey,
|
|
254
|
+
birthday_height: request.birthdayHeight
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async exportSeed(walletId, mnemonicLanguage = null) {
|
|
259
|
+
return this.sdk._call('export_seed_raw', {
|
|
260
|
+
wallet_id: walletId,
|
|
261
|
+
mnemonic_language: mnemonicLanguage
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
class PirateWalletSynchronizer {
|
|
267
|
+
constructor(sdk, walletId, config = {}) {
|
|
268
|
+
this.sdk = sdk
|
|
269
|
+
this.walletId = walletId
|
|
270
|
+
this.config = {
|
|
271
|
+
syncMode: config.syncMode || 'Compact',
|
|
272
|
+
syncingPollIntervalMs:
|
|
273
|
+
config.syncingPollIntervalMs == null ? 1000 : config.syncingPollIntervalMs,
|
|
274
|
+
syncedPollIntervalMs:
|
|
275
|
+
config.syncedPollIntervalMs == null ? 5000 : config.syncedPollIntervalMs,
|
|
276
|
+
errorPollIntervalMs:
|
|
277
|
+
config.errorPollIntervalMs == null ? 5000 : config.errorPollIntervalMs,
|
|
278
|
+
transactionLimit:
|
|
279
|
+
config.transactionLimit == null ? null : config.transactionLimit
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.status = 'STOPPED'
|
|
283
|
+
this.progress = 0
|
|
284
|
+
this.syncStatus = null
|
|
285
|
+
this.latestBirthdayHeight = null
|
|
286
|
+
this.balance = null
|
|
287
|
+
this.transactions = []
|
|
288
|
+
this.lastError = null
|
|
289
|
+
this.updatedAtMillis = null
|
|
290
|
+
|
|
291
|
+
this._timer = null
|
|
292
|
+
this._subscribers = new Set()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
currentSnapshot() {
|
|
296
|
+
return {
|
|
297
|
+
walletId: this.walletId,
|
|
298
|
+
alias: this.walletId,
|
|
299
|
+
status: this.status,
|
|
300
|
+
progressPercent: this.progress,
|
|
301
|
+
syncStatus: this.syncStatus,
|
|
302
|
+
latestBirthdayHeight: this.latestBirthdayHeight,
|
|
303
|
+
balance: this.balance,
|
|
304
|
+
transactions: this.transactions,
|
|
305
|
+
updatedAtMillis: this.updatedAtMillis,
|
|
306
|
+
lastError: this.lastError
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
isRunning() {
|
|
311
|
+
return this._timer !== null
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
isSyncing() {
|
|
315
|
+
return this.status === 'SYNCING'
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
isComplete() {
|
|
319
|
+
return isSyncComplete(this.syncStatus)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async start() {
|
|
323
|
+
if (this._timer !== null) {
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const previousStatus = this.status
|
|
328
|
+
this.status = 'SYNCING'
|
|
329
|
+
this.lastError = null
|
|
330
|
+
this.updatedAtMillis = Date.now()
|
|
331
|
+
this._publish(previousStatus)
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
await this.sdk.startSync(this.walletId, this.config.syncMode)
|
|
335
|
+
this._schedule(0)
|
|
336
|
+
} catch (error) {
|
|
337
|
+
this.status = 'STOPPED'
|
|
338
|
+
this.lastError = error
|
|
339
|
+
this.updatedAtMillis = Date.now()
|
|
340
|
+
this._publish(previousStatus)
|
|
341
|
+
this._notifyError(error)
|
|
342
|
+
throw error
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async stop() {
|
|
347
|
+
const previousStatus = this.status
|
|
348
|
+
const shouldCancelBackend = this._timer !== null || this.status !== 'STOPPED'
|
|
349
|
+
if (this._timer !== null) {
|
|
350
|
+
clearTimeout(this._timer)
|
|
351
|
+
this._timer = null
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this.status = 'STOPPED'
|
|
355
|
+
this.updatedAtMillis = Date.now()
|
|
356
|
+
this._publish(previousStatus)
|
|
357
|
+
|
|
358
|
+
if (!shouldCancelBackend) {
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
await this.sdk.cancelSync(this.walletId)
|
|
364
|
+
} catch (error) {
|
|
365
|
+
this.lastError = error
|
|
366
|
+
this.updatedAtMillis = Date.now()
|
|
367
|
+
this._notifyError(error)
|
|
368
|
+
throw error
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async refresh() {
|
|
373
|
+
return this._refreshOnce()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
close() {
|
|
377
|
+
return this.stop()
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
subscribe(callbacks = {}) {
|
|
381
|
+
const subscriber = cloneCallbacks(callbacks)
|
|
382
|
+
this._subscribers.add(subscriber)
|
|
383
|
+
return () => {
|
|
384
|
+
this._subscribers.delete(subscriber)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
_schedule(delayMs) {
|
|
389
|
+
if (this._timer !== null) {
|
|
390
|
+
clearTimeout(this._timer)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
this._timer = setTimeout(() => {
|
|
394
|
+
this._refreshOnce().catch(error => {
|
|
395
|
+
this.lastError = error
|
|
396
|
+
this.updatedAtMillis = Date.now()
|
|
397
|
+
this._notifyError(error)
|
|
398
|
+
if (this._timer !== null) {
|
|
399
|
+
this._schedule(this.config.errorPollIntervalMs)
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
}, delayMs)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async _refreshOnce() {
|
|
406
|
+
const observedAtMillis = Date.now()
|
|
407
|
+
const previousStatus = this.status
|
|
408
|
+
|
|
409
|
+
const syncStatus = await this.sdk.getSyncStatus(this.walletId)
|
|
410
|
+
const [balance, latestBirthdayHeight, transactions] = await Promise.all([
|
|
411
|
+
this.sdk
|
|
412
|
+
.getBalance(this.walletId)
|
|
413
|
+
.catch(() => this.balance),
|
|
414
|
+
this.sdk
|
|
415
|
+
.getLatestBirthdayHeight(this.walletId)
|
|
416
|
+
.catch(() => this.latestBirthdayHeight),
|
|
417
|
+
this.sdk
|
|
418
|
+
.listTransactions(this.walletId, this.config.transactionLimit)
|
|
419
|
+
.catch(() => this.transactions)
|
|
420
|
+
])
|
|
421
|
+
|
|
422
|
+
this.syncStatus = syncStatus
|
|
423
|
+
this.latestBirthdayHeight = latestBirthdayHeight
|
|
424
|
+
this.balance = balance
|
|
425
|
+
this.transactions = transactions
|
|
426
|
+
this.status = isSyncComplete(syncStatus) ? 'SYNCED' : 'SYNCING'
|
|
427
|
+
this.progress =
|
|
428
|
+
syncStatus && typeof syncStatus.percent === 'number'
|
|
429
|
+
? syncStatus.percent
|
|
430
|
+
: this.status === 'SYNCED'
|
|
431
|
+
? 100
|
|
432
|
+
: this.progress
|
|
433
|
+
this.updatedAtMillis = observedAtMillis
|
|
434
|
+
this.lastError = null
|
|
435
|
+
this._publish(previousStatus)
|
|
436
|
+
|
|
437
|
+
if (this._timer !== null) {
|
|
438
|
+
this._schedule(
|
|
439
|
+
isSyncing(syncStatus)
|
|
440
|
+
? this.config.syncingPollIntervalMs
|
|
441
|
+
: this.config.syncedPollIntervalMs
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return this.currentSnapshot()
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
_publish(previousStatus) {
|
|
449
|
+
const snapshot = this.currentSnapshot()
|
|
450
|
+
if (previousStatus !== this.status) {
|
|
451
|
+
for (const subscriber of this._subscribers) {
|
|
452
|
+
if (subscriber.onStatusChanged) {
|
|
453
|
+
subscriber.onStatusChanged({
|
|
454
|
+
walletId: this.walletId,
|
|
455
|
+
alias: this.walletId,
|
|
456
|
+
name: this.status
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
for (const subscriber of this._subscribers) {
|
|
463
|
+
if (subscriber.onUpdate) {
|
|
464
|
+
subscriber.onUpdate(snapshot)
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
_notifyError(error) {
|
|
470
|
+
for (const subscriber of this._subscribers) {
|
|
471
|
+
if (subscriber.onError) {
|
|
472
|
+
subscriber.onError(error)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
class PirateWalletSdk {
|
|
479
|
+
constructor(nativeModule = getNativeModule()) {
|
|
480
|
+
this._native = nativeModule
|
|
481
|
+
this.advancedKeyManagement = new PirateWalletAdvancedKeyManagement(this)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async invoke(requestJson, pretty = false) {
|
|
485
|
+
return this._native.invoke(requestJson, pretty)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async _call(method, params = {}, pretty = false) {
|
|
489
|
+
const response = await this.invoke(buildRequest(method, params), pretty)
|
|
490
|
+
return unwrapEnvelope(response, method)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Like _call, but returns the result without camelizing it. Use for RPCs
|
|
494
|
+
// whose result is an opaque payload that must be passed back into a later
|
|
495
|
+
// RPC unchanged (for example, sign_tx -> broadcast_tx).
|
|
496
|
+
async _callRaw(method, params = {}, pretty = false) {
|
|
497
|
+
const response = await this.invoke(buildRequest(method, params), pretty)
|
|
498
|
+
return unwrapEnvelope(response, method, { camelizeResult: false })
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
createSynchronizer(walletId, config = {}) {
|
|
502
|
+
return new PirateWalletSynchronizer(this, walletId, config)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async configureAccountStorage(config) {
|
|
506
|
+
if (!config || typeof config !== 'object') {
|
|
507
|
+
throw new Error('configureAccountStorage requires a config object.')
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const accountId = String(config.accountId || '').trim()
|
|
511
|
+
if (!accountId) {
|
|
512
|
+
throw new Error('configureAccountStorage requires accountId.')
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (typeof config.passphrase !== 'string' || config.passphrase.length === 0) {
|
|
516
|
+
throw new Error('configureAccountStorage requires passphrase.')
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const storagePath =
|
|
520
|
+
typeof config.storagePath === 'string' && config.storagePath.length > 0
|
|
521
|
+
? config.storagePath
|
|
522
|
+
: null
|
|
523
|
+
|
|
524
|
+
if (typeof this._native.configureAccountStorage !== 'function') {
|
|
525
|
+
throw new Error(
|
|
526
|
+
'PirateWalletReactNative native module does not expose configureAccountStorage. Rebuild the app with the current native module.'
|
|
527
|
+
)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const response = await this._native.configureAccountStorage(
|
|
531
|
+
accountId,
|
|
532
|
+
config.passphrase,
|
|
533
|
+
storagePath
|
|
534
|
+
)
|
|
535
|
+
return unwrapEnvelope(response, 'configure_wallet_storage')
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
buildInfoJson(pretty = false) {
|
|
539
|
+
return this.invoke(buildRequest('get_build_info'), pretty)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
buildInfo() {
|
|
543
|
+
return this._call('get_build_info')
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
walletRegistryExists() {
|
|
547
|
+
return this._call('wallet_registry_exists')
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
listWallets() {
|
|
551
|
+
return this._call('list_wallets')
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
getActiveWalletId() {
|
|
555
|
+
return this._call('get_active_wallet')
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async getActiveWallet() {
|
|
559
|
+
const activeWalletId = await this.getActiveWalletId()
|
|
560
|
+
if (!activeWalletId) {
|
|
561
|
+
return null
|
|
562
|
+
}
|
|
563
|
+
return this.getWallet(activeWalletId)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async getWallet(walletId) {
|
|
567
|
+
const wallets = await this.listWallets()
|
|
568
|
+
return wallets.find(wallet => wallet.id === walletId) || null
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
createWallet(requestOrName, birthdayHeight = null, mnemonicLanguage = null) {
|
|
572
|
+
const request =
|
|
573
|
+
typeof requestOrName === 'object' && requestOrName !== null
|
|
574
|
+
? requestOrName
|
|
575
|
+
: { name: requestOrName, birthdayHeight, mnemonicLanguage }
|
|
576
|
+
|
|
577
|
+
return this._call('create_wallet', {
|
|
578
|
+
name: request.name,
|
|
579
|
+
birthday_opt: request.birthdayHeight,
|
|
580
|
+
mnemonic_language: request.mnemonicLanguage ?? null
|
|
581
|
+
})
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
restoreWallet(requestOrName, mnemonic, birthdayHeight = null, mnemonicLanguage = null) {
|
|
585
|
+
const request =
|
|
586
|
+
typeof requestOrName === 'object' && requestOrName !== null
|
|
587
|
+
? requestOrName
|
|
588
|
+
: { name: requestOrName, mnemonic, birthdayHeight, mnemonicLanguage }
|
|
589
|
+
|
|
590
|
+
return this._call('restore_wallet', {
|
|
591
|
+
name: request.name,
|
|
592
|
+
mnemonic: request.mnemonic,
|
|
593
|
+
birthday_opt: request.birthdayHeight,
|
|
594
|
+
mnemonic_language: request.mnemonicLanguage ?? null
|
|
595
|
+
})
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
importViewingWallet(requestOrName, saplingViewingKey = null, ironwoodViewingKey = null, birthdayHeight) {
|
|
599
|
+
const request =
|
|
600
|
+
typeof requestOrName === 'object' && requestOrName !== null
|
|
601
|
+
? requestOrName
|
|
602
|
+
: { name: requestOrName, saplingViewingKey, ironwoodViewingKey, birthdayHeight }
|
|
603
|
+
|
|
604
|
+
return this._call('import_viewing_wallet', {
|
|
605
|
+
name: request.name,
|
|
606
|
+
sapling_viewing_key: request.saplingViewingKey,
|
|
607
|
+
ironwood_viewing_key: request.ironwoodViewingKey,
|
|
608
|
+
birthday: request.birthdayHeight
|
|
609
|
+
})
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
switchWallet(walletId) {
|
|
613
|
+
return this._call('switch_wallet', { wallet_id: walletId })
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
renameWallet(walletId, newName) {
|
|
617
|
+
return this._call('rename_wallet', { wallet_id: walletId, new_name: newName })
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
deleteWallet(walletId) {
|
|
621
|
+
return this._call('delete_wallet', { wallet_id: walletId })
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
setWalletBirthdayHeight(walletId, birthdayHeight) {
|
|
625
|
+
return this._call('set_wallet_birthday_height', {
|
|
626
|
+
wallet_id: walletId,
|
|
627
|
+
birthday_height: birthdayHeight
|
|
628
|
+
})
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async getLatestBirthdayHeight(walletId) {
|
|
632
|
+
const wallet = await this.getWallet(walletId)
|
|
633
|
+
return wallet ? wallet.birthdayHeight : null
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
generateMnemonic(wordCount = null, mnemonicLanguage = null) {
|
|
637
|
+
return this._call('generate_mnemonic', {
|
|
638
|
+
word_count: wordCount,
|
|
639
|
+
mnemonic_language: mnemonicLanguage
|
|
640
|
+
})
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
validateMnemonic(mnemonic, mnemonicLanguage = null) {
|
|
644
|
+
return this._call('validate_mnemonic', {
|
|
645
|
+
mnemonic,
|
|
646
|
+
mnemonic_language: mnemonicLanguage
|
|
647
|
+
})
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
inspectMnemonic(mnemonic) {
|
|
651
|
+
return this._call('inspect_mnemonic', { mnemonic })
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
getNetworkInfo() {
|
|
655
|
+
return this._call('get_network_info')
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
isValidShieldedAddr(address) {
|
|
659
|
+
return this._call('is_valid_shielded_address', { address })
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
validateAddress(address) {
|
|
663
|
+
return this._call('validate_address', { address })
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
validateConsensusBranch(walletId) {
|
|
667
|
+
return this._call('validate_consensus_branch', { wallet_id: walletId })
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
formatAmount(arrrtoshis) {
|
|
671
|
+
return this._call('format_amount', { arrrtoshis })
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
parseAmount(arrr) {
|
|
675
|
+
return this._call('parse_amount', { arrr })
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
getCurrentReceiveAddress(walletId) {
|
|
679
|
+
return this.getCurrentAddress(walletId)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
getCurrentAddress(walletId) {
|
|
683
|
+
return this._call('current_receive_address', { wallet_id: walletId })
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
getNextReceiveAddress(walletId) {
|
|
687
|
+
return this.getNextAddress(walletId)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
getNextAddress(walletId) {
|
|
691
|
+
return this._call('next_receive_address', { wallet_id: walletId })
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
listAddresses(walletId) {
|
|
695
|
+
return this._call('list_addresses', { wallet_id: walletId }).then(result =>
|
|
696
|
+
Array.isArray(result) ? result.map(sanitizeAddressInfo) : result
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
listAddressBalances(walletId, keyId = null) {
|
|
701
|
+
return this._call('list_address_balances', {
|
|
702
|
+
wallet_id: walletId,
|
|
703
|
+
key_id: keyId
|
|
704
|
+
}).then(result => (Array.isArray(result) ? result.map(sanitizeAddressBalanceInfo) : result))
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
getBalance(walletId) {
|
|
708
|
+
return this._call('get_balance', { wallet_id: walletId })
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
getShieldedPoolBalances(walletId) {
|
|
712
|
+
return this._call('get_shielded_pool_balances', { wallet_id: walletId })
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
getSpendabilityStatus(walletId) {
|
|
716
|
+
return this._call('get_spendability_status', { wallet_id: walletId })
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
listTransactions(walletId, limit = null) {
|
|
720
|
+
return this._call('list_transactions', { wallet_id: walletId, limit })
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
fetchTransactionMemo(walletId, txId, outputIndex = null) {
|
|
724
|
+
return this._call('fetch_transaction_memo', {
|
|
725
|
+
wallet_id: walletId,
|
|
726
|
+
txid: txId,
|
|
727
|
+
output_index: outputIndex
|
|
728
|
+
})
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
getTransactionDetails(walletId, txId) {
|
|
732
|
+
return this._call('get_transaction_details', {
|
|
733
|
+
wallet_id: walletId,
|
|
734
|
+
txid: txId
|
|
735
|
+
})
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
exportPaymentDisclosures(walletId, txId) {
|
|
739
|
+
return this._call('export_payment_disclosures', {
|
|
740
|
+
wallet_id: walletId,
|
|
741
|
+
txid: txId
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
exportSaplingPaymentDisclosure(walletId, txId, outputIndex) {
|
|
746
|
+
return this._call('export_sapling_payment_disclosure', {
|
|
747
|
+
wallet_id: walletId,
|
|
748
|
+
txid: txId,
|
|
749
|
+
output_index: outputIndex
|
|
750
|
+
})
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
exportIronwoodPaymentDisclosure(walletId, txId, actionIndex) {
|
|
754
|
+
return this._call('export_ironwood_payment_disclosure', {
|
|
755
|
+
wallet_id: walletId,
|
|
756
|
+
txid: txId,
|
|
757
|
+
action_index: actionIndex
|
|
758
|
+
})
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
verifyPaymentDisclosure(walletId, disclosure) {
|
|
762
|
+
return this._call('verify_payment_disclosure', {
|
|
763
|
+
wallet_id: walletId,
|
|
764
|
+
disclosure
|
|
765
|
+
})
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
getFeeInfo() {
|
|
769
|
+
return this._call('get_fee_info')
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
startSync(walletIdOrRequest, mode = 'Compact') {
|
|
773
|
+
const request =
|
|
774
|
+
typeof walletIdOrRequest === 'object' && walletIdOrRequest !== null
|
|
775
|
+
? walletIdOrRequest
|
|
776
|
+
: { walletId: walletIdOrRequest, mode }
|
|
777
|
+
|
|
778
|
+
return this._call('start_sync', {
|
|
779
|
+
wallet_id: request.walletId,
|
|
780
|
+
mode: request.mode
|
|
781
|
+
})
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
getSyncStatus(walletId) {
|
|
785
|
+
return this._call('sync_status', { wallet_id: walletId })
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
cancelSync(walletId) {
|
|
789
|
+
return this._call('cancel_sync', { wallet_id: walletId })
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
rescan(walletIdOrRequest, fromHeight = null) {
|
|
793
|
+
const request =
|
|
794
|
+
typeof walletIdOrRequest === 'object' && walletIdOrRequest !== null
|
|
795
|
+
? walletIdOrRequest
|
|
796
|
+
: { walletId: walletIdOrRequest, fromHeight }
|
|
797
|
+
|
|
798
|
+
return this._call('rescan', {
|
|
799
|
+
wallet_id: request.walletId,
|
|
800
|
+
from_height: request.fromHeight
|
|
801
|
+
})
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
buildTransaction(walletIdOrRequest, outputs = null, fee = null) {
|
|
805
|
+
let request
|
|
806
|
+
if (typeof walletIdOrRequest === 'object' && walletIdOrRequest !== null && outputs == null) {
|
|
807
|
+
request = walletIdOrRequest
|
|
808
|
+
} else if (Array.isArray(outputs)) {
|
|
809
|
+
request = { walletId: walletIdOrRequest, outputs, fee }
|
|
810
|
+
} else {
|
|
811
|
+
request = { walletId: walletIdOrRequest, outputs: [outputs], fee }
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return this._call('build_tx', {
|
|
815
|
+
wallet_id: request.walletId,
|
|
816
|
+
outputs: request.outputs,
|
|
817
|
+
fee_opt: request.fee
|
|
818
|
+
})
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
signTransaction(walletId, pending) {
|
|
822
|
+
return this._callRaw('sign_tx', {
|
|
823
|
+
wallet_id: walletId,
|
|
824
|
+
pending: pendingTransactionForWire(pending)
|
|
825
|
+
})
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
broadcastTransaction(signed) {
|
|
829
|
+
return this._callRaw('broadcast_tx', { signed })
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async send(walletId, outputsOrOutput, fee = null) {
|
|
833
|
+
const outputs = Array.isArray(outputsOrOutput)
|
|
834
|
+
? outputsOrOutput
|
|
835
|
+
: [outputsOrOutput]
|
|
836
|
+
const pending = await this.buildTransaction(walletId, outputs, fee)
|
|
837
|
+
const signed = await this.signTransaction(walletId, pending)
|
|
838
|
+
return this.broadcastTransaction(signed)
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
exportSaplingViewingKey(walletId) {
|
|
842
|
+
return this._call('export_sapling_viewing_key', { wallet_id: walletId })
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
exportIronwoodViewingKey(walletId) {
|
|
846
|
+
return this._call('export_ironwood_viewing_key', { wallet_id: walletId })
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
importSaplingViewingKeyAsWatchOnly(requestOrName, saplingViewingKey = null, birthdayHeight = null) {
|
|
850
|
+
const request =
|
|
851
|
+
typeof requestOrName === 'object' && requestOrName !== null
|
|
852
|
+
? requestOrName
|
|
853
|
+
: { name: requestOrName, saplingViewingKey, birthdayHeight }
|
|
854
|
+
|
|
855
|
+
return this._call('import_sapling_viewing_key_as_watch_only', {
|
|
856
|
+
name: request.name,
|
|
857
|
+
sapling_viewing_key: request.saplingViewingKey,
|
|
858
|
+
birthday_height: request.birthdayHeight
|
|
859
|
+
})
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
getWatchOnlyCapabilities(walletId) {
|
|
863
|
+
return this._call('get_watch_only_capabilities', { wallet_id: walletId })
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function createPirateWalletSdk() {
|
|
868
|
+
return new PirateWalletSdk()
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
module.exports = {
|
|
872
|
+
PirateWalletSdk,
|
|
873
|
+
PirateWalletSynchronizer,
|
|
874
|
+
PirateWalletAdvancedKeyManagement,
|
|
875
|
+
createPirateWalletSdk
|
|
876
|
+
}
|