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/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
+ }