orbis1-sdk-rn 0.0.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.
Files changed (98) hide show
  1. package/LICENSE +20 -0
  2. package/Orbis1Sdk.podspec +24 -0
  3. package/README.md +269 -0
  4. package/android/build.gradle +78 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/orbis1sdk/AppConstants.kt +25 -0
  7. package/android/src/main/java/com/orbis1sdk/Orbis1SdkModule.kt +15 -0
  8. package/android/src/main/java/com/orbis1sdk/Orbis1SdkPackage.kt +33 -0
  9. package/android/src/main/java/com/orbis1sdk/RgbModule.kt +2100 -0
  10. package/android/src/main/java/com/orbis1sdk/RgbPackage.kt +33 -0
  11. package/android/src/main/java/com/orbis1sdk/WalletStore.kt +49 -0
  12. package/ios/AppConstants.swift +57 -0
  13. package/ios/Orbis1Sdk.h +5 -0
  14. package/ios/Orbis1Sdk.mm +1765 -0
  15. package/ios/Rgb.h +5 -0
  16. package/ios/Rgb.mm +1761 -0
  17. package/ios/Rgb.swift +2094 -0
  18. package/ios/RgbLib.swift +7871 -0
  19. package/ios/WalletStore.swift +61 -0
  20. package/lib/module/Orbis1SDK.js +116 -0
  21. package/lib/module/Orbis1SDK.js.map +1 -0
  22. package/lib/module/core/Interfaces.js +65 -0
  23. package/lib/module/core/Interfaces.js.map +1 -0
  24. package/lib/module/core/NativeRgb.js +5 -0
  25. package/lib/module/core/NativeRgb.js.map +1 -0
  26. package/lib/module/core/RgbError.js +65 -0
  27. package/lib/module/core/RgbError.js.map +1 -0
  28. package/lib/module/core/Wallet.js +854 -0
  29. package/lib/module/core/Wallet.js.map +1 -0
  30. package/lib/module/errors/FeatureNotEnabledError.js +19 -0
  31. package/lib/module/errors/FeatureNotEnabledError.js.map +1 -0
  32. package/lib/module/features/watch-tower/WatchTowerModule.js +94 -0
  33. package/lib/module/features/watch-tower/WatchTowerModule.js.map +1 -0
  34. package/lib/module/features/watch-tower/index.js +11 -0
  35. package/lib/module/features/watch-tower/index.js.map +1 -0
  36. package/lib/module/features/watch-tower/types/WatchTowerConfig.js +10 -0
  37. package/lib/module/features/watch-tower/types/WatchTowerConfig.js.map +1 -0
  38. package/lib/module/features/watch-tower/types/index.js +4 -0
  39. package/lib/module/features/watch-tower/types/index.js.map +1 -0
  40. package/lib/module/index.js +58 -0
  41. package/lib/module/index.js.map +1 -0
  42. package/lib/module/package.json +1 -0
  43. package/lib/module/types/Feature.js +16 -0
  44. package/lib/module/types/Feature.js.map +1 -0
  45. package/lib/module/types/IFeatureModule.js +35 -0
  46. package/lib/module/types/IFeatureModule.js.map +1 -0
  47. package/lib/module/types/Orbis1CreateConfig.js +29 -0
  48. package/lib/module/types/Orbis1CreateConfig.js.map +1 -0
  49. package/lib/module/utils/FeatureRegistry.js +49 -0
  50. package/lib/module/utils/FeatureRegistry.js.map +1 -0
  51. package/lib/typescript/package.json +1 -0
  52. package/lib/typescript/src/Orbis1SDK.d.ts +57 -0
  53. package/lib/typescript/src/Orbis1SDK.d.ts.map +1 -0
  54. package/lib/typescript/src/core/Interfaces.d.ts +422 -0
  55. package/lib/typescript/src/core/Interfaces.d.ts.map +1 -0
  56. package/lib/typescript/src/core/NativeRgb.d.ts +287 -0
  57. package/lib/typescript/src/core/NativeRgb.d.ts.map +1 -0
  58. package/lib/typescript/src/core/RgbError.d.ts +28 -0
  59. package/lib/typescript/src/core/RgbError.d.ts.map +1 -0
  60. package/lib/typescript/src/core/Wallet.d.ts +656 -0
  61. package/lib/typescript/src/core/Wallet.d.ts.map +1 -0
  62. package/lib/typescript/src/errors/FeatureNotEnabledError.d.ts +11 -0
  63. package/lib/typescript/src/errors/FeatureNotEnabledError.d.ts.map +1 -0
  64. package/lib/typescript/src/features/watch-tower/WatchTowerModule.d.ts +38 -0
  65. package/lib/typescript/src/features/watch-tower/WatchTowerModule.d.ts.map +1 -0
  66. package/lib/typescript/src/features/watch-tower/index.d.ts +9 -0
  67. package/lib/typescript/src/features/watch-tower/index.d.ts.map +1 -0
  68. package/lib/typescript/src/features/watch-tower/types/WatchTowerConfig.d.ts +13 -0
  69. package/lib/typescript/src/features/watch-tower/types/WatchTowerConfig.d.ts.map +1 -0
  70. package/lib/typescript/src/features/watch-tower/types/index.d.ts +3 -0
  71. package/lib/typescript/src/features/watch-tower/types/index.d.ts.map +1 -0
  72. package/lib/typescript/src/index.d.ts +46 -0
  73. package/lib/typescript/src/index.d.ts.map +1 -0
  74. package/lib/typescript/src/types/Feature.d.ts +10 -0
  75. package/lib/typescript/src/types/Feature.d.ts.map +1 -0
  76. package/lib/typescript/src/types/IFeatureModule.d.ts +74 -0
  77. package/lib/typescript/src/types/IFeatureModule.d.ts.map +1 -0
  78. package/lib/typescript/src/types/Orbis1CreateConfig.d.ts +26 -0
  79. package/lib/typescript/src/types/Orbis1CreateConfig.d.ts.map +1 -0
  80. package/lib/typescript/src/utils/FeatureRegistry.d.ts +19 -0
  81. package/lib/typescript/src/utils/FeatureRegistry.d.ts.map +1 -0
  82. package/package.json +190 -0
  83. package/scripts/download-rgb-lib-ios.js +102 -0
  84. package/src/Orbis1SDK.ts +132 -0
  85. package/src/core/Interfaces.ts +412 -0
  86. package/src/core/NativeRgb.ts +491 -0
  87. package/src/core/RgbError.ts +84 -0
  88. package/src/core/Wallet.ts +1127 -0
  89. package/src/errors/FeatureNotEnabledError.ts +22 -0
  90. package/src/features/watch-tower/WatchTowerModule.ts +115 -0
  91. package/src/features/watch-tower/index.ts +9 -0
  92. package/src/features/watch-tower/types/WatchTowerConfig.ts +15 -0
  93. package/src/features/watch-tower/types/index.ts +2 -0
  94. package/src/index.tsx +75 -0
  95. package/src/types/Feature.ts +13 -0
  96. package/src/types/IFeatureModule.ts +99 -0
  97. package/src/types/Orbis1CreateConfig.ts +43 -0
  98. package/src/utils/FeatureRegistry.ts +61 -0
@@ -0,0 +1,2100 @@
1
+ package com.orbis1sdk
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReadableArray
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.module.annotations.ReactModule
10
+ import kotlinx.coroutines.CoroutineScope
11
+ import kotlinx.coroutines.Dispatchers
12
+ import kotlinx.coroutines.SupervisorJob
13
+ import kotlinx.coroutines.launch
14
+ import kotlinx.coroutines.withContext
15
+ import org.rgbtools.AssetSchema
16
+ import org.rgbtools.Assignment
17
+ import org.rgbtools.BitcoinNetwork
18
+ import org.rgbtools.DatabaseType
19
+ import org.rgbtools.RefreshFilter
20
+ import org.rgbtools.ExternalInput
21
+ import org.rgbtools.ExternalOutput
22
+ import org.rgbtools.Recipient
23
+ import org.rgbtools.RefreshTransferStatus
24
+ import org.rgbtools.Wallet
25
+ import org.rgbtools.WalletData
26
+ import org.rgbtools.WitnessData
27
+ import org.rgbtools.generateKeys
28
+ import org.rgbtools.restoreKeys
29
+ import com.facebook.react.bridge.ReadableMap
30
+ import com.facebook.react.bridge.ReadableType
31
+ import org.rgbtools.Token
32
+ import org.rgbtools.Invoice
33
+
34
+ @ReactModule(name = RgbModule.NAME)
35
+ class RgbModule(reactContext: ReactApplicationContext) :
36
+ NativeRgbSpec(reactContext) {
37
+ private val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
38
+ private val TAG = "RNRgb"
39
+
40
+ init {
41
+ AppConstants.ensureInitialized(reactContext)
42
+ }
43
+
44
+ override fun getName(): String {
45
+ return NAME
46
+ }
47
+
48
+ companion object {
49
+ const val NAME = "Rgb"
50
+ }
51
+
52
+ private suspend fun resolvePromise(promise: Promise, result: String) {
53
+ withContext(Dispatchers.Main) {
54
+ promise.resolve(result)
55
+ }
56
+ }
57
+
58
+ private fun getErrorClassName(exception: Exception): String {
59
+ val className = exception.javaClass.name
60
+ // Handle nested classes (separated by $)
61
+ val parts = className.split('$')
62
+ // Take the last part and split by . to get simple name
63
+ val lastPart = parts.last().split('.').last()
64
+ return lastPart
65
+ }
66
+
67
+ private fun parseErrorMessage(message: String?): String {
68
+ if (message == null) return "Unknown error"
69
+ // Remove "details=" prefix if present
70
+ return if (message.startsWith("details=", ignoreCase = true)) {
71
+ message.substring(8).trim()
72
+ } else {
73
+ message
74
+ }
75
+ }
76
+
77
+ override fun generateKeys(bitcoinNetwork: String, promise: Promise) {
78
+ coroutineScope.launch(Dispatchers.IO) {
79
+ try {
80
+ val network = when (bitcoinNetwork) {
81
+ "MAINNET" -> BitcoinNetwork.MAINNET
82
+ "TESTNET" -> BitcoinNetwork.TESTNET
83
+ "TESTNET4" -> BitcoinNetwork.TESTNET4
84
+ "REGTEST" -> BitcoinNetwork.REGTEST
85
+ "SIGNET" -> BitcoinNetwork.SIGNET
86
+ else -> throw IllegalArgumentException("Unknown BitcoinNetwork: $bitcoinNetwork")
87
+ }
88
+
89
+ val keys = generateKeys(bitcoinNetwork = network)
90
+ val result = Arguments.createMap()
91
+ result.putString("mnemonic", keys.mnemonic)
92
+ result.putString("xpub", keys.xpub)
93
+ result.putString("accountXpubVanilla", keys.accountXpubVanilla)
94
+ result.putString("accountXpubColored", keys.accountXpubColored)
95
+ result.putString("masterFingerprint", keys.masterFingerprint)
96
+ promise.resolve(result)
97
+ } catch (e: Exception) {
98
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
99
+ }
100
+ }
101
+ }
102
+
103
+ override fun restoreKeys(bitcoinNetwork: String, mnemonic: String, promise: Promise) {
104
+ coroutineScope.launch(Dispatchers.IO) {
105
+ try {
106
+ val network = when (bitcoinNetwork) {
107
+ "MAINNET" -> BitcoinNetwork.MAINNET
108
+ "TESTNET" -> BitcoinNetwork.TESTNET
109
+ "TESTNET4" -> BitcoinNetwork.TESTNET4
110
+ "REGTEST" -> BitcoinNetwork.REGTEST
111
+ "SIGNET" -> BitcoinNetwork.SIGNET
112
+ else -> throw IllegalArgumentException("Unknown BitcoinNetwork: $bitcoinNetwork")
113
+ }
114
+ val keys = restoreKeys(bitcoinNetwork = network, mnemonic = mnemonic)
115
+ val result = Arguments.createMap()
116
+ result.putString("mnemonic", keys.mnemonic)
117
+ result.putString("xpub", keys.xpub)
118
+ result.putString("accountXpubVanilla", keys.accountXpubVanilla)
119
+ result.putString("accountXpubColored", keys.accountXpubColored)
120
+ result.putString("masterFingerprint", keys.masterFingerprint)
121
+ promise.resolve(result)
122
+ } catch (e: Exception) {
123
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
124
+ }
125
+ }
126
+ }
127
+
128
+ override fun restoreBackup(path: String, password: String, promise: Promise) {
129
+ coroutineScope.launch(Dispatchers.IO) {
130
+ try {
131
+ val rgbDir = AppConstants.rgbDir
132
+ ?: throw IllegalStateException("RGB directory not initialized.")
133
+ org.rgbtools.restoreBackup(path, password, rgbDir.absolutePath)
134
+
135
+ withContext(Dispatchers.Main) {
136
+ promise.resolve(null)
137
+ }
138
+ } catch (e: Exception) {
139
+ Log.e(TAG, "restoreBackup error: ${e.message}", e)
140
+ withContext(Dispatchers.Main) {
141
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ override fun decodeInvoice(invoice: String, promise: Promise) {
148
+ coroutineScope.launch(Dispatchers.IO) {
149
+ try {
150
+ val invoiceData = Invoice(invoiceString = invoice).invoiceData()
151
+ val map = Arguments.createMap()
152
+ map.putString("invoice", invoice)
153
+ map.putString("recipientId", invoiceData.recipientId)
154
+ invoiceData.assetSchema?.let { schema ->
155
+ val assetSchemaString = when (schema) {
156
+ AssetSchema.NIA -> "NIA"
157
+ AssetSchema.UDA -> "UDA"
158
+ AssetSchema.CFA -> "CFA"
159
+ AssetSchema.IFA -> "IFA"
160
+ }
161
+ map.putString("assetSchema", assetSchemaString)
162
+ }
163
+ map.putString("assetId", invoiceData.assetId)
164
+ map.putMap("assignment", assignmentToMap(invoiceData.assignment))
165
+ map.putString("assignmentName", invoiceData.assignmentName)
166
+ map.putString("network", invoiceData.network.toString())
167
+ val transportEndpointsArray = Arguments.createArray()
168
+ invoiceData.transportEndpoints.forEach {
169
+ transportEndpointsArray.pushString(it)
170
+ }
171
+ map.putArray("transportEndpoints", transportEndpointsArray)
172
+
173
+ invoiceData.expirationTimestamp?.let {
174
+ map.putDouble("expirationTimestamp", it.toDouble())
175
+ } ?: run {
176
+ map.putNull("expirationTimestamp")
177
+ }
178
+ withContext(Dispatchers.Main) {
179
+ promise.resolve(map)
180
+ }
181
+ } catch (e: Exception) {
182
+ Log.e(TAG, "decodeInvoice error: ${e.message}", e)
183
+ withContext(Dispatchers.Main) {
184
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
185
+ }
186
+ }
187
+ }
188
+ }
189
+
190
+ private fun getNetwork(network: String): BitcoinNetwork {
191
+ return when (network) {
192
+ "MAINNET" -> BitcoinNetwork.MAINNET
193
+ "TESTNET" -> BitcoinNetwork.TESTNET
194
+ "TESTNET4" -> BitcoinNetwork.TESTNET4
195
+ "REGTEST" -> BitcoinNetwork.REGTEST
196
+ "SIGNET" -> BitcoinNetwork.SIGNET
197
+ else -> throw IllegalArgumentException("Unknown BitcoinNetwork: $network")
198
+ }
199
+ }
200
+
201
+ private fun getAssetSchema(schema: String): AssetSchema {
202
+ return when (schema) {
203
+ "NIA" -> AssetSchema.NIA
204
+ "UDA" -> AssetSchema.UDA
205
+ "CFA" -> AssetSchema.CFA
206
+ "IFA" -> AssetSchema.IFA
207
+ else -> throw IllegalArgumentException("Unknown AssetSchema: $schema")
208
+ }
209
+ }
210
+
211
+ override fun initializeWallet(
212
+ network: String,
213
+ accountXpubVanilla: String,
214
+ accountXpubColored: String,
215
+ mnemonic: String,
216
+ masterFingerprint: String,
217
+ supportedSchemas: ReadableArray,
218
+ maxAllocationsPerUtxo: Double,
219
+ vanillaKeychain: Double,
220
+ promise: Promise
221
+ ) {
222
+ coroutineScope.launch(Dispatchers.IO) {
223
+ try {
224
+ val rgbDir = AppConstants.rgbDir
225
+ ?: throw IllegalStateException("RGB directory not initialized. Call AppConstants.initContext() first.")
226
+
227
+ val rgbNetwork = getNetwork(network)
228
+ val schemaList = mutableListOf<AssetSchema>()
229
+ for (i in 0 until supportedSchemas.size()) {
230
+ val schemaStr = supportedSchemas.getString(i)
231
+ schemaList.add(getAssetSchema(schemaStr ?: throw IllegalArgumentException("Invalid schema at index $i")))
232
+ }
233
+
234
+ val walletData = WalletData(
235
+ dataDir = rgbDir.absolutePath,
236
+ bitcoinNetwork = rgbNetwork,
237
+ databaseType = DatabaseType.SQLITE,
238
+ maxAllocationsPerUtxo = maxAllocationsPerUtxo.toInt().toUInt(),
239
+ accountXpubVanilla = accountXpubVanilla,
240
+ accountXpubColored = accountXpubColored,
241
+ mnemonic = mnemonic,
242
+ masterFingerprint = masterFingerprint,
243
+ vanillaKeychain = vanillaKeychain.toInt().toUByte(),
244
+ supportedSchemas = schemaList
245
+ )
246
+ val wallet = Wallet(walletData)
247
+ val walletId = WalletStore.create(wallet)
248
+ withContext(Dispatchers.Main) {
249
+ promise.resolve(walletId)
250
+ }
251
+ } catch (e: Exception) {
252
+ Log.e(TAG, "initializeWallet error: ${e.message}", e)
253
+ withContext(Dispatchers.Main) {
254
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ override fun goOnline(
261
+ walletId: Double,
262
+ skipConsistencyCheck: Boolean,
263
+ indexerUrl: String,
264
+ promise: Promise
265
+ ) {
266
+ coroutineScope.launch(Dispatchers.IO) {
267
+ try {
268
+ val session = WalletStore.get(walletId.toInt())
269
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
270
+
271
+ val online = session.wallet.goOnline(
272
+ skipConsistencyCheck = skipConsistencyCheck,
273
+ indexerUrl = indexerUrl
274
+ )
275
+ WalletStore.setOnline(walletId.toInt(), online)
276
+
277
+ withContext(Dispatchers.Main) {
278
+ promise.resolve(null)
279
+ }
280
+ } catch (e: Exception) {
281
+ Log.e(TAG, "goOnline error: ${e.message}", e)
282
+ withContext(Dispatchers.Main) {
283
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
284
+ }
285
+ }
286
+ }
287
+ }
288
+
289
+ override fun getBtcBalance(
290
+ walletId: Double,
291
+ skipSync: Boolean,
292
+ promise: Promise
293
+ ) {
294
+ coroutineScope.launch(Dispatchers.IO) {
295
+ try {
296
+ val session = WalletStore.get(walletId.toInt())
297
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
298
+
299
+ val btcBalance = session.wallet.getBtcBalance(
300
+ online = session.online,
301
+ skipSync = skipSync
302
+ )
303
+
304
+ val result = Arguments.createMap()
305
+
306
+ val vanilla = Arguments.createMap()
307
+ vanilla.putDouble("settled", btcBalance.vanilla.settled.toDouble())
308
+ vanilla.putDouble("future", btcBalance.vanilla.future.toDouble())
309
+ vanilla.putDouble("spendable", btcBalance.vanilla.spendable.toDouble())
310
+
311
+ val colored = Arguments.createMap()
312
+ colored.putDouble("settled", btcBalance.colored.settled.toDouble())
313
+ colored.putDouble("future", btcBalance.colored.future.toDouble())
314
+ colored.putDouble("spendable", btcBalance.colored.spendable.toDouble())
315
+
316
+ result.putMap("vanilla", vanilla)
317
+ result.putMap("colored", colored)
318
+
319
+ withContext(Dispatchers.Main) {
320
+ promise.resolve(result)
321
+ }
322
+ } catch (e: Exception) {
323
+ Log.e(TAG, "getBtcBalance error: ${e.message}", e)
324
+ withContext(Dispatchers.Main) {
325
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ override fun walletClose(walletId: Double, promise: Promise) {
332
+ coroutineScope.launch(Dispatchers.IO) {
333
+ try {
334
+ WalletStore.remove(walletId.toInt())
335
+ withContext(Dispatchers.Main) {
336
+ promise.resolve(null)
337
+ }
338
+ } catch (e: Exception) {
339
+ Log.e(TAG, "walletClose error: ${e.message}", e)
340
+ withContext(Dispatchers.Main) {
341
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
342
+ }
343
+ }
344
+ }
345
+ }
346
+
347
+ // Helper functions for type conversions
348
+ private fun getAssignment(assignmentMap: ReadableMap): Assignment {
349
+ val type = assignmentMap.getString("type") ?: throw IllegalArgumentException("Assignment type is required")
350
+ return when (type) {
351
+ "FUNGIBLE" -> {
352
+ val amount = assignmentMap.getDouble("amount").toULong()
353
+ Assignment.Fungible(amount)
354
+ }
355
+ "NON_FUNGIBLE" -> Assignment.NonFungible
356
+ "INFLATION_RIGHT" -> {
357
+ val amount = assignmentMap.getDouble("amount").toULong()
358
+ Assignment.InflationRight(amount)
359
+ }
360
+ "REPLACE_RIGHT" -> Assignment.ReplaceRight
361
+ "ANY" -> Assignment.Any
362
+ else -> throw IllegalArgumentException("Unknown Assignment type: $type")
363
+ }
364
+ }
365
+
366
+ private fun getRefreshFilter(filterMap: ReadableMap): RefreshFilter {
367
+ val statusStr = filterMap.getString("status") ?: throw IllegalArgumentException("RefreshFilter status is required")
368
+ val status = when (statusStr.uppercase()) {
369
+ "WAITING_COUNTERPARTY" -> RefreshTransferStatus.WAITING_COUNTERPARTY
370
+ "WAITING_CONFIRMATIONS" -> RefreshTransferStatus.WAITING_CONFIRMATIONS
371
+ else -> throw IllegalArgumentException("Unknown RefreshTransferStatus: $statusStr")
372
+ }
373
+ val incoming = filterMap.getBoolean("incoming")
374
+ return RefreshFilter(status, incoming)
375
+ }
376
+
377
+ private fun getRecipient(recipientMap: ReadableMap): Recipient {
378
+ val recipientId = recipientMap.getString("recipientId") ?: throw IllegalArgumentException("Recipient recipientId is required")
379
+ val assignmentMap = recipientMap.getMap("assignment") ?: throw IllegalArgumentException("Recipient assignment is required")
380
+ val assignment = getAssignment(assignmentMap)
381
+
382
+ val transportEndpointsArray = recipientMap.getArray("transportEndpoints") ?: throw IllegalArgumentException("Recipient transportEndpoints is required")
383
+ val transportEndpoints = mutableListOf<String>()
384
+ for (i in 0 until transportEndpointsArray.size()) {
385
+ transportEndpoints.add(transportEndpointsArray.getString(i) ?: "")
386
+ }
387
+
388
+ val witnessData = if (recipientMap.hasKey("witnessData") && recipientMap.getType("witnessData") == ReadableType.Map) {
389
+ val witnessDataMap = recipientMap.getMap("witnessData")
390
+ ?: throw IllegalArgumentException("WitnessData map is null")
391
+
392
+ val amountSat = witnessDataMap.getDouble("amountSat").toULong()
393
+ val blinding = if (witnessDataMap.hasKey("blinding") && !witnessDataMap.isNull("blinding")) {
394
+ witnessDataMap.getDouble("blinding").toULong()
395
+ } else {
396
+ null
397
+ }
398
+
399
+ WitnessData(amountSat, blinding)
400
+ } else {
401
+ null
402
+ }
403
+
404
+ return Recipient(recipientId, witnessData, assignment, transportEndpoints)
405
+ }
406
+
407
+ private fun getExternalInput(map: ReadableMap): ExternalInput {
408
+ val txid = map.getString("txid") ?: throw IllegalArgumentException("ExternalInput txid is required")
409
+ val vout = map.getDouble("vout").toInt().toUInt()
410
+ val value = map.getDouble("value").toULong()
411
+ val scriptPubkey = map.getString("scriptPubkey") ?: throw IllegalArgumentException("ExternalInput scriptPubkey is required")
412
+ return ExternalInput(txid, vout, value, scriptPubkey)
413
+ }
414
+
415
+ private fun getExternalOutput(map: ReadableMap): ExternalOutput {
416
+ val address = map.getString("address") ?: throw IllegalArgumentException("ExternalOutput address is required")
417
+ val value = map.getDouble("value").toULong()
418
+ return ExternalOutput(address, value)
419
+ }
420
+
421
+ private fun assignmentToMap(assignment: Assignment): WritableMap {
422
+ val map = Arguments.createMap()
423
+ when (assignment) {
424
+ is Assignment.Fungible -> {
425
+ map.putString("type", "FUNGIBLE")
426
+ map.putDouble("amount", assignment.amount.toDouble())
427
+ }
428
+ is Assignment.NonFungible -> {
429
+ map.putString("type", "NON_FUNGIBLE")
430
+ }
431
+ is Assignment.InflationRight -> {
432
+ map.putString("type", "INFLATION_RIGHT")
433
+ map.putDouble("amount", assignment.amount.toDouble())
434
+ }
435
+ is Assignment.ReplaceRight -> {
436
+ map.putString("type", "REPLACE_RIGHT")
437
+ }
438
+ is Assignment.Any -> {
439
+ map.putString("type", "ANY")
440
+ }
441
+ }
442
+ return map
443
+ }
444
+
445
+ private fun outpointToMap(outpoint: org.rgbtools.Outpoint): WritableMap {
446
+ val map = Arguments.createMap()
447
+ map.putString("txid", outpoint.txid)
448
+ map.putDouble("vout", outpoint.vout.toDouble())
449
+ return map
450
+ }
451
+
452
+ private fun balanceToMap(balance: org.rgbtools.Balance): WritableMap {
453
+ val map = Arguments.createMap()
454
+ map.putDouble("settled", balance.settled.toDouble())
455
+ map.putDouble("future", balance.future.toDouble())
456
+ map.putDouble("spendable", balance.spendable.toDouble())
457
+ return map
458
+ }
459
+
460
+ private fun assetCfaToMap(asset: org.rgbtools.AssetCfa): WritableMap {
461
+ val map = Arguments.createMap()
462
+ map.putString("assetId", asset.assetId)
463
+ map.putString("name", asset.name)
464
+ asset.details?.let { map.putString("details", it) }
465
+ map.putInt("precision", asset.precision.toInt())
466
+ map.putDouble("issuedSupply", asset.issuedSupply.toDouble())
467
+ map.putDouble("timestamp", asset.timestamp.toDouble())
468
+ map.putDouble("addedAt", asset.addedAt.toDouble())
469
+ map.putMap("balance", balanceToMap(asset.balance))
470
+ asset.media?.let { media ->
471
+ val mediaMap = Arguments.createMap()
472
+ mediaMap.putString("filePath", media.filePath)
473
+ mediaMap.putString("mime", media.mime)
474
+ mediaMap.putString("digest", media.digest)
475
+ map.putMap("media", mediaMap)
476
+ }
477
+ return map
478
+ }
479
+
480
+ private fun assetIfaToMap(asset: org.rgbtools.AssetIfa): WritableMap {
481
+ val map = Arguments.createMap()
482
+ map.putString("assetId", asset.assetId)
483
+ map.putString("ticker", asset.ticker)
484
+ map.putString("name", asset.name)
485
+ asset.details?.let { map.putString("details", it) }
486
+ map.putInt("precision", asset.precision.toInt())
487
+ map.putDouble("initialSupply", asset.initialSupply.toDouble())
488
+ map.putDouble("maxSupply", asset.maxSupply.toDouble())
489
+ map.putDouble("knownCirculatingSupply", asset.knownCirculatingSupply.toDouble())
490
+ map.putDouble("timestamp", asset.timestamp.toDouble())
491
+ map.putDouble("addedAt", asset.addedAt.toDouble())
492
+ map.putMap("balance", balanceToMap(asset.balance))
493
+ asset.media?.let { media ->
494
+ val mediaMap = Arguments.createMap()
495
+ mediaMap.putString("filePath", media.filePath)
496
+ mediaMap.putString("mime", media.mime)
497
+ mediaMap.putString("digest", media.digest)
498
+ map.putMap("media", mediaMap)
499
+ }
500
+ asset.rejectListUrl?.let { map.putString("rejectListUrl", it) }
501
+ return map
502
+ }
503
+
504
+ private fun assetNiaToMap(asset: org.rgbtools.AssetNia): WritableMap {
505
+ val map = Arguments.createMap()
506
+ map.putString("assetId", asset.assetId)
507
+ map.putString("ticker", asset.ticker)
508
+ map.putString("name", asset.name)
509
+ asset.details?.let { map.putString("details", it) }
510
+ map.putInt("precision", asset.precision.toInt())
511
+ map.putDouble("issuedSupply", asset.issuedSupply.toDouble())
512
+ map.putDouble("timestamp", asset.timestamp.toDouble())
513
+ map.putDouble("addedAt", asset.addedAt.toDouble())
514
+ map.putMap("balance", balanceToMap(asset.balance))
515
+ asset.media?.let { media ->
516
+ val mediaMap = Arguments.createMap()
517
+ mediaMap.putString("filePath", media.filePath)
518
+ mediaMap.putString("mime", media.mime)
519
+ mediaMap.putString("digest", media.digest)
520
+ map.putMap("media", mediaMap)
521
+ }
522
+ return map
523
+ }
524
+
525
+ private fun assetUdaToMap(asset: org.rgbtools.AssetUda): WritableMap {
526
+ val map = Arguments.createMap()
527
+ map.putString("assetId", asset.assetId)
528
+ map.putString("ticker", asset.ticker)
529
+ map.putString("name", asset.name)
530
+ asset.details?.let { map.putString("details", it) }
531
+ map.putInt("precision", asset.precision.toInt())
532
+ map.putDouble("timestamp", asset.timestamp.toDouble())
533
+ map.putDouble("addedAt", asset.addedAt.toDouble())
534
+ map.putMap("balance", balanceToMap(asset.balance))
535
+ asset.token?.let { tokenLight ->
536
+ val token = Arguments.createMap()
537
+ token.putInt("index", tokenLight.index.toInt())
538
+ tokenLight.ticker?.let { token.putString("ticker", it) }
539
+ tokenLight.name?.let { token.putString("name", it) }
540
+ tokenLight.details?.let { token.putString("details", it) }
541
+ token.putBoolean("embeddedMedia", tokenLight.embeddedMedia)
542
+
543
+ tokenLight.media?.let { media ->
544
+ val mediaMap = Arguments.createMap()
545
+ mediaMap.putString("filePath", media.filePath)
546
+ mediaMap.putString("mime", media.mime)
547
+ mediaMap.putString("digest", media.digest)
548
+ token.putMap("media", mediaMap)
549
+ }
550
+
551
+ val attachmentsArray = Arguments.createArray()
552
+ tokenLight.attachments.forEach { (key, media) ->
553
+ val attachmentMap = Arguments.createMap()
554
+ attachmentMap.putInt("key", key.toInt())
555
+ attachmentMap.putString("filePath", media.filePath)
556
+ attachmentMap.putString("mime", media.mime)
557
+ attachmentMap.putString("digest", media.digest)
558
+ attachmentsArray.pushMap(attachmentMap)
559
+ }
560
+ token.putArray("attachments", attachmentsArray)
561
+ token.putBoolean("reserves", tokenLight.reserves)
562
+ map.putMap("token", token)
563
+ }
564
+ return map
565
+ }
566
+
567
+ private fun operationResultToMap(result: org.rgbtools.OperationResult): WritableMap {
568
+ val map = Arguments.createMap()
569
+ map.putString("txid", result.txid)
570
+ map.putInt("batchTransferIdx", result.batchTransferIdx)
571
+ return map
572
+ }
573
+
574
+ private fun receiveDataToMap(data: org.rgbtools.ReceiveData): WritableMap {
575
+ val map = Arguments.createMap()
576
+ map.putString("invoice", data.invoice)
577
+ map.putString("recipientId", data.recipientId)
578
+ data.expirationTimestamp?.let { map.putDouble("expirationTimestamp", it.toDouble()) }
579
+ map.putInt("batchTransferIdx", data.batchTransferIdx)
580
+ return map
581
+ }
582
+
583
+ // Wallet methods
584
+ override fun backup(walletId: Double, backupPath: String, password: String, promise: Promise) {
585
+ coroutineScope.launch(Dispatchers.IO) {
586
+ try {
587
+ val session = WalletStore.get(walletId.toInt())
588
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
589
+
590
+ session.wallet.backup(backupPath, password)
591
+
592
+ withContext(Dispatchers.Main) {
593
+ promise.resolve(null)
594
+ }
595
+ } catch (e: Exception) {
596
+ Log.e(TAG, "backup error: ${e.message}", e)
597
+ withContext(Dispatchers.Main) {
598
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
599
+ }
600
+ }
601
+ }
602
+ }
603
+
604
+ override fun backupInfo(walletId: Double, promise: Promise) {
605
+ coroutineScope.launch(Dispatchers.IO) {
606
+ try {
607
+ val session = WalletStore.get(walletId.toInt())
608
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
609
+
610
+ val hasBackup = session.wallet.backupInfo()
611
+
612
+ withContext(Dispatchers.Main) {
613
+ promise.resolve(hasBackup)
614
+ }
615
+ } catch (e: Exception) {
616
+ Log.e(TAG, "backupInfo error: ${e.message}", e)
617
+ withContext(Dispatchers.Main) {
618
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
619
+ }
620
+ }
621
+ }
622
+ }
623
+
624
+ override fun blindReceive(
625
+ walletId: Double,
626
+ assetId: String?,
627
+ assignment: ReadableMap,
628
+ durationSeconds: Double?,
629
+ transportEndpoints: ReadableArray,
630
+ minConfirmations: Double,
631
+ promise: Promise
632
+ ) {
633
+ coroutineScope.launch(Dispatchers.IO) {
634
+ try {
635
+ val session = WalletStore.get(walletId.toInt())
636
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
637
+
638
+ val assignmentObj = getAssignment(assignment)
639
+ val endpoints = mutableListOf<String>()
640
+ for (i in 0 until transportEndpoints.size()) {
641
+ endpoints.add(transportEndpoints.getString(i) ?: "")
642
+ }
643
+
644
+ val receiveData = session.wallet.blindReceive(
645
+ assetId,
646
+ assignmentObj,
647
+ durationSeconds?.toInt()?.toUInt(),
648
+ endpoints,
649
+ minConfirmations.toInt().toUByte()
650
+ )
651
+
652
+ withContext(Dispatchers.Main) {
653
+ promise.resolve(receiveDataToMap(receiveData))
654
+ }
655
+ } catch (e: Exception) {
656
+ Log.e(TAG, "blindReceive error: ${e.message}", e)
657
+ withContext(Dispatchers.Main) {
658
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
659
+ }
660
+ }
661
+ }
662
+ }
663
+
664
+ override fun createUtxos(
665
+ walletId: Double,
666
+ upTo: Boolean,
667
+ num: Double?,
668
+ size: Double?,
669
+ feeRate: Double,
670
+ skipSync: Boolean,
671
+ promise: Promise
672
+ ) {
673
+ coroutineScope.launch(Dispatchers.IO) {
674
+ try {
675
+ val session = WalletStore.get(walletId.toInt())
676
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
677
+
678
+ val online = session.online
679
+ ?: throw IllegalStateException("Wallet is not online")
680
+
681
+ val count = session.wallet.createUtxos(
682
+ online,
683
+ upTo,
684
+ num?.toInt()?.toUByte(),
685
+ size?.toInt()?.toUInt(),
686
+ feeRate.toULong(),
687
+ skipSync
688
+ )
689
+
690
+ withContext(Dispatchers.Main) {
691
+ promise.resolve(count.toInt())
692
+ }
693
+ } catch (e: Exception) {
694
+ Log.e(TAG, "createUtxos error: ${e.message}", e)
695
+ withContext(Dispatchers.Main) {
696
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
697
+ }
698
+ }
699
+ }
700
+ }
701
+
702
+ override fun createUtxosBegin(
703
+ walletId: Double,
704
+ upTo: Boolean,
705
+ num: Double?,
706
+ size: Double?,
707
+ feeRate: Double,
708
+ skipSync: Boolean,
709
+ promise: Promise
710
+ ) {
711
+ coroutineScope.launch(Dispatchers.IO) {
712
+ try {
713
+ val session = WalletStore.get(walletId.toInt())
714
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
715
+
716
+ val online = session.online
717
+ ?: throw IllegalStateException("Wallet is not online")
718
+
719
+ val psbt = session.wallet.createUtxosBegin(
720
+ online,
721
+ upTo,
722
+ num?.toInt()?.toUByte(),
723
+ size?.toInt()?.toUInt(),
724
+ feeRate.toULong(),
725
+ skipSync
726
+ )
727
+
728
+ withContext(Dispatchers.Main) {
729
+ promise.resolve(psbt)
730
+ }
731
+ } catch (e: Exception) {
732
+ Log.e(TAG, "createUtxosBegin error: ${e.message}", e)
733
+ withContext(Dispatchers.Main) {
734
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
735
+ }
736
+ }
737
+ }
738
+ }
739
+
740
+ override fun createUtxosEnd(
741
+ walletId: Double,
742
+ signedPsbt: String,
743
+ skipSync: Boolean,
744
+ promise: Promise
745
+ ) {
746
+ coroutineScope.launch(Dispatchers.IO) {
747
+ try {
748
+ val session = WalletStore.get(walletId.toInt())
749
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
750
+
751
+ val online = session.online
752
+ ?: throw IllegalStateException("Wallet is not online")
753
+
754
+ val count = session.wallet.createUtxosEnd(online, signedPsbt, skipSync)
755
+
756
+ withContext(Dispatchers.Main) {
757
+ promise.resolve(count.toInt())
758
+ }
759
+ } catch (e: Exception) {
760
+ Log.e(TAG, "createUtxosEnd error: ${e.message}", e)
761
+ withContext(Dispatchers.Main) {
762
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
763
+ }
764
+ }
765
+ }
766
+ }
767
+
768
+ override fun deleteTransfers(
769
+ walletId: Double,
770
+ batchTransferIdx: Double?,
771
+ noAssetOnly: Boolean,
772
+ promise: Promise
773
+ ) {
774
+ coroutineScope.launch(Dispatchers.IO) {
775
+ try {
776
+ val session = WalletStore.get(walletId.toInt())
777
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
778
+
779
+ val deleted = session.wallet.deleteTransfers(
780
+ batchTransferIdx?.toInt(),
781
+ noAssetOnly
782
+ )
783
+
784
+ withContext(Dispatchers.Main) {
785
+ promise.resolve(deleted)
786
+ }
787
+ } catch (e: Exception) {
788
+ Log.e(TAG, "deleteTransfers error: ${e.message}", e)
789
+ withContext(Dispatchers.Main) {
790
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
791
+ }
792
+ }
793
+ }
794
+ }
795
+
796
+ override fun drainTo(
797
+ walletId: Double,
798
+ address: String,
799
+ destroyAssets: Boolean,
800
+ feeRate: Double,
801
+ promise: Promise
802
+ ) {
803
+ coroutineScope.launch(Dispatchers.IO) {
804
+ try {
805
+ val session = WalletStore.get(walletId.toInt())
806
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
807
+
808
+ val online = session.online
809
+ ?: throw IllegalStateException("Wallet is not online")
810
+
811
+ val txid = session.wallet.drainTo(online, address, destroyAssets, feeRate.toULong())
812
+
813
+ withContext(Dispatchers.Main) {
814
+ promise.resolve(txid)
815
+ }
816
+ } catch (e: Exception) {
817
+ Log.e(TAG, "drainTo error: ${e.message}", e)
818
+ withContext(Dispatchers.Main) {
819
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
820
+ }
821
+ }
822
+ }
823
+ }
824
+
825
+ override fun drainToBegin(
826
+ walletId: Double,
827
+ address: String,
828
+ destroyAssets: Boolean,
829
+ feeRate: Double,
830
+ promise: Promise
831
+ ) {
832
+ coroutineScope.launch(Dispatchers.IO) {
833
+ try {
834
+ val session = WalletStore.get(walletId.toInt())
835
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
836
+
837
+ val online = session.online
838
+ ?: throw IllegalStateException("Wallet is not online")
839
+
840
+ val psbt = session.wallet.drainToBegin(online, address, destroyAssets, feeRate.toULong())
841
+
842
+ withContext(Dispatchers.Main) {
843
+ promise.resolve(psbt)
844
+ }
845
+ } catch (e: Exception) {
846
+ Log.e(TAG, "drainToBegin error: ${e.message}", e)
847
+ withContext(Dispatchers.Main) {
848
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
849
+ }
850
+ }
851
+ }
852
+ }
853
+
854
+ override fun drainToEnd(
855
+ walletId: Double,
856
+ signedPsbt: String,
857
+ promise: Promise
858
+ ) {
859
+ coroutineScope.launch(Dispatchers.IO) {
860
+ try {
861
+ val session = WalletStore.get(walletId.toInt())
862
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
863
+
864
+ val online = session.online
865
+ ?: throw IllegalStateException("Wallet is not online")
866
+
867
+ val txid = session.wallet.drainToEnd(online, signedPsbt)
868
+
869
+ withContext(Dispatchers.Main) {
870
+ promise.resolve(txid)
871
+ }
872
+ } catch (e: Exception) {
873
+ Log.e(TAG, "drainToEnd error: ${e.message}", e)
874
+ withContext(Dispatchers.Main) {
875
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
876
+ }
877
+ }
878
+ }
879
+ }
880
+
881
+ override fun failTransfers(
882
+ walletId: Double,
883
+ batchTransferIdx: Double?,
884
+ noAssetOnly: Boolean,
885
+ skipSync: Boolean,
886
+ promise: Promise
887
+ ) {
888
+ coroutineScope.launch(Dispatchers.IO) {
889
+ try {
890
+ val session = WalletStore.get(walletId.toInt())
891
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
892
+
893
+ val online = session.online
894
+ ?: throw IllegalStateException("Wallet is not online")
895
+
896
+ val failed = session.wallet.failTransfers(
897
+ online,
898
+ batchTransferIdx?.toInt(),
899
+ noAssetOnly,
900
+ skipSync
901
+ )
902
+
903
+ withContext(Dispatchers.Main) {
904
+ promise.resolve(failed)
905
+ }
906
+ } catch (e: Exception) {
907
+ Log.e(TAG, "failTransfers error: ${e.message}", e)
908
+ withContext(Dispatchers.Main) {
909
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
910
+ }
911
+ }
912
+ }
913
+ }
914
+
915
+ override fun finalizePsbt(walletId: Double, signedPsbt: String, promise: Promise) {
916
+ coroutineScope.launch(Dispatchers.IO) {
917
+ try {
918
+ val session = WalletStore.get(walletId.toInt())
919
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
920
+
921
+ val finalizedPsbt = session.wallet.finalizePsbt(signedPsbt)
922
+
923
+ withContext(Dispatchers.Main) {
924
+ promise.resolve(finalizedPsbt)
925
+ }
926
+ } catch (e: Exception) {
927
+ Log.e(TAG, "finalizePsbt error: ${e.message}", e)
928
+ withContext(Dispatchers.Main) {
929
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
930
+ }
931
+ }
932
+ }
933
+ }
934
+
935
+ override fun getAddress(walletId: Double, promise: Promise) {
936
+ coroutineScope.launch(Dispatchers.IO) {
937
+ try {
938
+ val session = WalletStore.get(walletId.toInt())
939
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
940
+
941
+ val address = session.wallet.getAddress()
942
+
943
+ withContext(Dispatchers.Main) {
944
+ promise.resolve(address)
945
+ }
946
+ } catch (e: Exception) {
947
+ Log.e(TAG, "getAddress error: ${e.message}", e)
948
+ withContext(Dispatchers.Main) {
949
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
950
+ }
951
+ }
952
+ }
953
+ }
954
+
955
+ override fun getAssetBalance(walletId: Double, assetId: String, promise: Promise) {
956
+ coroutineScope.launch(Dispatchers.IO) {
957
+ try {
958
+ val session = WalletStore.get(walletId.toInt())
959
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
960
+
961
+ val balance = session.wallet.getAssetBalance(assetId)
962
+
963
+ withContext(Dispatchers.Main) {
964
+ promise.resolve(balanceToMap(balance))
965
+ }
966
+ } catch (e: Exception) {
967
+ Log.e(TAG, "getAssetBalance error: ${e.message}", e)
968
+ withContext(Dispatchers.Main) {
969
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
970
+ }
971
+ }
972
+ }
973
+ }
974
+
975
+ override fun getAssetMetadata(walletId: Double, assetId: String, promise: Promise) {
976
+ coroutineScope.launch(Dispatchers.IO) {
977
+ try {
978
+ val session = WalletStore.get(walletId.toInt())
979
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
980
+
981
+ val metadata = session.wallet.getAssetMetadata(assetId)
982
+ val map = Arguments.createMap()
983
+ map.putString("assetId", assetId)
984
+ val assetSchemaString = when (metadata.assetSchema) {
985
+ AssetSchema.NIA -> "NIA"
986
+ AssetSchema.UDA -> "UDA"
987
+ AssetSchema.CFA -> "CFA"
988
+ AssetSchema.IFA -> "IFA"
989
+ }
990
+ map.putString("assetSchema", assetSchemaString)
991
+ map.putString("name", metadata.name)
992
+ map.putInt("precision", metadata.precision.toInt())
993
+ map.putDouble("initialSupply", metadata.initialSupply.toDouble())
994
+ map.putDouble("maxSupply", metadata.maxSupply.toDouble())
995
+ map.putDouble("knownCirculatingSupply", metadata.knownCirculatingSupply.toDouble())
996
+ map.putDouble("timestamp", metadata.timestamp.toDouble())
997
+ metadata.ticker?.let { map.putString("ticker", it) }
998
+ metadata.details?.let { map.putString("details", it) }
999
+ metadata.rejectListUrl?.let { map.putString("rejectListUrl", it) }
1000
+ metadata.token?.let { token ->
1001
+ val tokenMap = Arguments.createMap()
1002
+ tokenMap.putInt("index", token.index.toInt())
1003
+ token.ticker?.let { tokenMap.putString("ticker", it) }
1004
+ token.name?.let { tokenMap.putString("name", it) }
1005
+ token.details?.let { tokenMap.putString("details", it) }
1006
+ token.embeddedMedia?.let { embeddedMedia ->
1007
+ val embeddedMediaMap = Arguments.createMap()
1008
+ embeddedMediaMap.putString("mime", embeddedMedia.mime)
1009
+ val dataArray = Arguments.createArray()
1010
+ embeddedMedia.data.forEach { dataArray.pushInt(it.toInt()) }
1011
+ embeddedMediaMap.putArray("data", dataArray)
1012
+ tokenMap.putMap("embeddedMedia", embeddedMediaMap)
1013
+ }
1014
+ token.media?.let { media ->
1015
+ val mediaMap = Arguments.createMap()
1016
+ mediaMap.putString("filePath", media.filePath)
1017
+ mediaMap.putString("mime", media.mime)
1018
+ mediaMap.putString("digest", media.digest)
1019
+ tokenMap.putMap("media", mediaMap)
1020
+ }
1021
+ val attachmentsArray = Arguments.createArray()
1022
+ token.attachments.forEach { (key, media) ->
1023
+ val attachmentMap = Arguments.createMap()
1024
+ attachmentMap.putInt("key", key.toInt())
1025
+ attachmentMap.putString("filePath", media.filePath)
1026
+ attachmentMap.putString("mime", media.mime)
1027
+ attachmentMap.putString("digest", media.digest)
1028
+ attachmentsArray.pushMap(attachmentMap)
1029
+ }
1030
+ tokenMap.putArray("attachments", attachmentsArray)
1031
+ token.reserves?.let { reserves ->
1032
+ val reservesMap = Arguments.createMap()
1033
+ val utxoMap = Arguments.createMap()
1034
+ utxoMap.putString("txid", reserves.utxo.txid)
1035
+ utxoMap.putDouble("vout", reserves.utxo.vout.toDouble())
1036
+ reservesMap.putMap("utxo", utxoMap)
1037
+ val proofArray = Arguments.createArray()
1038
+ reserves.proof.forEach { proofArray.pushInt(it.toInt()) }
1039
+ reservesMap.putArray("proof", proofArray)
1040
+ tokenMap.putMap("reserves", reservesMap)
1041
+ }
1042
+ map.putMap("token", tokenMap)
1043
+ }
1044
+
1045
+ withContext(Dispatchers.Main) {
1046
+ promise.resolve(map)
1047
+ }
1048
+ } catch (e: Exception) {
1049
+ Log.e(TAG, "getAssetMetadata error: ${e.message}", e)
1050
+ withContext(Dispatchers.Main) {
1051
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+
1057
+ override fun getFeeEstimation(
1058
+ walletId: Double,
1059
+ blocks: Double,
1060
+ promise: Promise
1061
+ ) {
1062
+ coroutineScope.launch(Dispatchers.IO) {
1063
+ try {
1064
+ val session = WalletStore.get(walletId.toInt())
1065
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1066
+
1067
+ val online = session.online
1068
+ ?: throw IllegalStateException("Wallet is not online")
1069
+
1070
+ val feeRate = session.wallet.getFeeEstimation(online, blocks.toInt().toUShort())
1071
+
1072
+ withContext(Dispatchers.Main) {
1073
+ promise.resolve(feeRate)
1074
+ }
1075
+ } catch (e: Exception) {
1076
+ Log.e(TAG, "getFeeEstimation error: ${e.message}", e)
1077
+ withContext(Dispatchers.Main) {
1078
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1079
+ }
1080
+ }
1081
+ }
1082
+ }
1083
+
1084
+ override fun getMediaDir(walletId: Double, promise: Promise) {
1085
+ coroutineScope.launch(Dispatchers.IO) {
1086
+ try {
1087
+ val session = WalletStore.get(walletId.toInt())
1088
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1089
+
1090
+ val mediaDir = session.wallet.getMediaDir()
1091
+
1092
+ withContext(Dispatchers.Main) {
1093
+ promise.resolve(mediaDir)
1094
+ }
1095
+ } catch (e: Exception) {
1096
+ Log.e(TAG, "getMediaDir error: ${e.message}", e)
1097
+ withContext(Dispatchers.Main) {
1098
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+
1104
+ override fun getWalletData(walletId: Double, promise: Promise) {
1105
+ coroutineScope.launch(Dispatchers.IO) {
1106
+ try {
1107
+ val session = WalletStore.get(walletId.toInt())
1108
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1109
+
1110
+ val walletData = session.wallet.getWalletData()
1111
+ val map = Arguments.createMap()
1112
+ map.putString("dataDir", walletData.dataDir)
1113
+
1114
+ val networkString = when (walletData.bitcoinNetwork) {
1115
+ BitcoinNetwork.MAINNET -> "MAINNET"
1116
+ BitcoinNetwork.TESTNET -> "TESTNET"
1117
+ BitcoinNetwork.TESTNET4 -> "TESTNET4"
1118
+ BitcoinNetwork.REGTEST -> "REGTEST"
1119
+ BitcoinNetwork.SIGNET -> "SIGNET"
1120
+ else -> {
1121
+ throw Exception("Unknown bitcoin network")
1122
+ }
1123
+ }
1124
+ map.putString("bitcoinNetwork", networkString)
1125
+
1126
+ val dbTypeString = when (walletData.databaseType) {
1127
+ DatabaseType.SQLITE -> "SQLITE"
1128
+ }
1129
+ map.putString("databaseType", dbTypeString)
1130
+
1131
+ map.putDouble("maxAllocationsPerUtxo", walletData.maxAllocationsPerUtxo.toDouble())
1132
+ map.putString("accountXpubVanilla", walletData.accountXpubVanilla)
1133
+ map.putString("accountXpubColored", walletData.accountXpubColored)
1134
+ walletData.mnemonic?.let { map.putString("mnemonic", it) }
1135
+ map.putString("masterFingerprint", walletData.masterFingerprint)
1136
+ walletData.vanillaKeychain?.let { map.putInt("vanillaKeychain", it.toInt()) }
1137
+ val schemasArray = Arguments.createArray()
1138
+ walletData.supportedSchemas.forEach { schema ->
1139
+ val schemaString = when (schema) {
1140
+ AssetSchema.NIA -> "NIA"
1141
+ AssetSchema.UDA -> "UDA"
1142
+ AssetSchema.CFA -> "CFA"
1143
+ AssetSchema.IFA -> "IFA"
1144
+ }
1145
+ schemasArray.pushString(schemaString)
1146
+ }
1147
+ map.putArray("supportedSchemas", schemasArray)
1148
+
1149
+ withContext(Dispatchers.Main) {
1150
+ promise.resolve(map)
1151
+ }
1152
+ } catch (e: Exception) {
1153
+ Log.e(TAG, "getWalletData error: ${e.message}", e)
1154
+ withContext(Dispatchers.Main) {
1155
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1156
+ }
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ override fun getWalletDir(walletId: Double, promise: Promise) {
1162
+ coroutineScope.launch(Dispatchers.IO) {
1163
+ try {
1164
+ val session = WalletStore.get(walletId.toInt())
1165
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1166
+
1167
+ val walletDir = session.wallet.getWalletDir()
1168
+
1169
+ withContext(Dispatchers.Main) {
1170
+ promise.resolve(walletDir)
1171
+ }
1172
+ } catch (e: Exception) {
1173
+ Log.e(TAG, "getWalletDir error: ${e.message}", e)
1174
+ withContext(Dispatchers.Main) {
1175
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1176
+ }
1177
+ }
1178
+ }
1179
+ }
1180
+
1181
+ override fun inflate(
1182
+ walletId: Double,
1183
+ assetId: String,
1184
+ inflationAmounts: ReadableArray,
1185
+ feeRate: Double,
1186
+ minConfirmations: Double,
1187
+ promise: Promise
1188
+ ) {
1189
+ coroutineScope.launch(Dispatchers.IO) {
1190
+ try {
1191
+ val session = WalletStore.get(walletId.toInt())
1192
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1193
+
1194
+ val online = session.online
1195
+ ?: throw IllegalStateException("Wallet is not online")
1196
+
1197
+ val amounts = mutableListOf<ULong>()
1198
+ for (i in 0 until inflationAmounts.size()) {
1199
+ amounts.add(inflationAmounts.getDouble(i).toULong())
1200
+ }
1201
+
1202
+ val result = session.wallet.inflate(
1203
+ online,
1204
+ assetId,
1205
+ amounts,
1206
+ feeRate.toULong(),
1207
+ minConfirmations.toInt().toUByte()
1208
+ )
1209
+
1210
+ withContext(Dispatchers.Main) {
1211
+ promise.resolve(operationResultToMap(result))
1212
+ }
1213
+ } catch (e: Exception) {
1214
+ Log.e(TAG, "inflate error: ${e.message}", e)
1215
+ withContext(Dispatchers.Main) {
1216
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1217
+ }
1218
+ }
1219
+ }
1220
+ }
1221
+
1222
+ override fun inflateBegin(
1223
+ walletId: Double,
1224
+ assetId: String,
1225
+ inflationAmounts: ReadableArray,
1226
+ feeRate: Double,
1227
+ minConfirmations: Double,
1228
+ promise: Promise
1229
+ ) {
1230
+ coroutineScope.launch(Dispatchers.IO) {
1231
+ try {
1232
+ val session = WalletStore.get(walletId.toInt())
1233
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1234
+
1235
+ val online = session.online
1236
+ ?: throw IllegalStateException("Wallet is not online")
1237
+
1238
+ val amounts = mutableListOf<ULong>()
1239
+ for (i in 0 until inflationAmounts.size()) {
1240
+ amounts.add(inflationAmounts.getDouble(i).toULong())
1241
+ }
1242
+
1243
+ val psbt = session.wallet.inflateBegin(
1244
+ online,
1245
+ assetId,
1246
+ amounts,
1247
+ feeRate.toULong(),
1248
+ minConfirmations.toInt().toUByte()
1249
+ )
1250
+
1251
+ withContext(Dispatchers.Main) {
1252
+ promise.resolve(psbt)
1253
+ }
1254
+ } catch (e: Exception) {
1255
+ Log.e(TAG, "inflateBegin error: ${e.message}", e)
1256
+ withContext(Dispatchers.Main) {
1257
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+
1263
+ override fun inflateEnd(
1264
+ walletId: Double,
1265
+ signedPsbt: String,
1266
+ promise: Promise
1267
+ ) {
1268
+ coroutineScope.launch(Dispatchers.IO) {
1269
+ try {
1270
+ val session = WalletStore.get(walletId.toInt())
1271
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1272
+
1273
+ val online = session.online
1274
+ ?: throw IllegalStateException("Wallet is not online")
1275
+
1276
+ val result = session.wallet.inflateEnd(online, signedPsbt)
1277
+
1278
+ withContext(Dispatchers.Main) {
1279
+ promise.resolve(operationResultToMap(result))
1280
+ }
1281
+ } catch (e: Exception) {
1282
+ Log.e(TAG, "inflateEnd error: ${e.message}", e)
1283
+ withContext(Dispatchers.Main) {
1284
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1289
+
1290
+ override fun issueAssetCfa(
1291
+ walletId: Double,
1292
+ name: String,
1293
+ details: String?,
1294
+ precision: Double,
1295
+ amounts: ReadableArray,
1296
+ filePath: String?,
1297
+ promise: Promise
1298
+ ) {
1299
+ coroutineScope.launch(Dispatchers.IO) {
1300
+ try {
1301
+ val session = WalletStore.get(walletId.toInt())
1302
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1303
+
1304
+ val amountsList = mutableListOf<ULong>()
1305
+ for (i in 0 until amounts.size()) {
1306
+ amountsList.add(amounts.getDouble(i).toULong())
1307
+ }
1308
+
1309
+ val asset = session.wallet.issueAssetCfa(
1310
+ name,
1311
+ details,
1312
+ precision.toInt().toUByte(),
1313
+ amountsList,
1314
+ filePath
1315
+ )
1316
+
1317
+ withContext(Dispatchers.Main) {
1318
+ promise.resolve(assetCfaToMap(asset))
1319
+ }
1320
+ } catch (e: Exception) {
1321
+ Log.e(TAG, "issueAssetCfa error: ${e.message}", e)
1322
+ withContext(Dispatchers.Main) {
1323
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1324
+ }
1325
+ }
1326
+ }
1327
+ }
1328
+
1329
+ override fun issueAssetIfa(
1330
+ walletId: Double,
1331
+ ticker: String,
1332
+ name: String,
1333
+ precision: Double,
1334
+ amounts: ReadableArray,
1335
+ inflationAmounts: ReadableArray,
1336
+ replaceRightsNum: Double,
1337
+ rejectListUrl: String?,
1338
+ promise: Promise
1339
+ ) {
1340
+ coroutineScope.launch(Dispatchers.IO) {
1341
+ try {
1342
+ val session = WalletStore.get(walletId.toInt())
1343
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1344
+
1345
+ val amountsList = mutableListOf<ULong>()
1346
+ for (i in 0 until amounts.size()) {
1347
+ amountsList.add(amounts.getDouble(i).toULong())
1348
+ }
1349
+
1350
+ val inflationAmountsList = mutableListOf<ULong>()
1351
+ for (i in 0 until inflationAmounts.size()) {
1352
+ inflationAmountsList.add(inflationAmounts.getDouble(i).toULong())
1353
+ }
1354
+
1355
+ val asset = session.wallet.issueAssetIfa(
1356
+ ticker,
1357
+ name,
1358
+ precision.toInt().toUByte(),
1359
+ amountsList,
1360
+ inflationAmountsList,
1361
+ replaceRightsNum.toInt().toUByte(),
1362
+ rejectListUrl
1363
+ )
1364
+
1365
+ withContext(Dispatchers.Main) {
1366
+ promise.resolve(assetIfaToMap(asset))
1367
+ }
1368
+ } catch (e: Exception) {
1369
+ Log.e(TAG, "issueAssetIfa error: ${e.message}", e)
1370
+ withContext(Dispatchers.Main) {
1371
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1372
+ }
1373
+ }
1374
+ }
1375
+ }
1376
+
1377
+ override fun issueAssetNia(
1378
+ walletId: Double,
1379
+ ticker: String,
1380
+ name: String,
1381
+ precision: Double,
1382
+ amounts: ReadableArray,
1383
+ promise: Promise
1384
+ ) {
1385
+ coroutineScope.launch(Dispatchers.IO) {
1386
+ try {
1387
+ val session = WalletStore.get(walletId.toInt())
1388
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1389
+
1390
+ val amountsList = mutableListOf<ULong>()
1391
+ for (i in 0 until amounts.size()) {
1392
+ amountsList.add(amounts.getDouble(i).toULong())
1393
+ }
1394
+
1395
+ val asset = session.wallet.issueAssetNia(
1396
+ ticker,
1397
+ name,
1398
+ precision.toInt().toUByte(),
1399
+ amountsList
1400
+ )
1401
+
1402
+ withContext(Dispatchers.Main) {
1403
+ promise.resolve(assetNiaToMap(asset))
1404
+ }
1405
+ } catch (e: Exception) {
1406
+ Log.e(TAG, "issueAssetNia error: ${e.message}", e)
1407
+ withContext(Dispatchers.Main) {
1408
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1409
+ }
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ override fun issueAssetUda(
1415
+ walletId: Double,
1416
+ ticker: String,
1417
+ name: String,
1418
+ details: String?,
1419
+ precision: Double,
1420
+ mediaFilePath: String?,
1421
+ attachmentsFilePaths: ReadableArray,
1422
+ promise: Promise
1423
+ ) {
1424
+ coroutineScope.launch(Dispatchers.IO) {
1425
+ try {
1426
+ val session = WalletStore.get(walletId.toInt())
1427
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1428
+
1429
+ val attachmentsList = mutableListOf<String>()
1430
+ for (i in 0 until attachmentsFilePaths.size()) {
1431
+ attachmentsList.add(attachmentsFilePaths.getString(i) ?: "")
1432
+ }
1433
+
1434
+ val asset = session.wallet.issueAssetUda(
1435
+ ticker,
1436
+ name,
1437
+ details,
1438
+ precision.toInt().toUByte(),
1439
+ mediaFilePath,
1440
+ attachmentsList
1441
+ )
1442
+
1443
+ withContext(Dispatchers.Main) {
1444
+ promise.resolve(assetUdaToMap(asset))
1445
+ }
1446
+ } catch (e: Exception) {
1447
+ Log.e(TAG, "issueAssetUda error: ${e.message}", e)
1448
+ withContext(Dispatchers.Main) {
1449
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1450
+ }
1451
+ }
1452
+ }
1453
+ }
1454
+
1455
+ override fun listAssets(
1456
+ walletId: Double,
1457
+ filterAssetSchemas: ReadableArray,
1458
+ promise: Promise
1459
+ ) {
1460
+ coroutineScope.launch(Dispatchers.IO) {
1461
+ try {
1462
+ val session = WalletStore.get(walletId.toInt())
1463
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1464
+
1465
+ val schemaList = mutableListOf<AssetSchema>()
1466
+ for (i in 0 until filterAssetSchemas.size()) {
1467
+ val schemaStr = filterAssetSchemas.getString(i)
1468
+ schemaList.add(getAssetSchema(schemaStr ?: ""))
1469
+ }
1470
+
1471
+ val assets = session.wallet.listAssets(schemaList)
1472
+ val result = Arguments.createMap()
1473
+
1474
+ val niaArray = Arguments.createArray()
1475
+ assets.nia?.forEach { asset ->
1476
+ niaArray.pushMap(assetNiaToMap(asset))
1477
+ }
1478
+ result.putArray("nia", niaArray)
1479
+
1480
+ val udaArray = Arguments.createArray()
1481
+ assets.uda?.forEach { asset ->
1482
+ udaArray.pushMap(assetUdaToMap(asset))
1483
+ }
1484
+ result.putArray("uda", udaArray)
1485
+
1486
+ val cfaArray = Arguments.createArray()
1487
+ assets.cfa?.forEach { asset ->
1488
+ cfaArray.pushMap(assetCfaToMap(asset))
1489
+ }
1490
+ result.putArray("cfa", cfaArray)
1491
+
1492
+ val ifaArray = Arguments.createArray()
1493
+ assets.ifa?.forEach { asset ->
1494
+ ifaArray.pushMap(assetIfaToMap(asset))
1495
+ }
1496
+ result.putArray("ifa", ifaArray)
1497
+
1498
+ withContext(Dispatchers.Main) {
1499
+ promise.resolve(result)
1500
+ }
1501
+ } catch (e: Exception) {
1502
+ Log.e(TAG, "listAssets error: ${e.message}", e)
1503
+ withContext(Dispatchers.Main) {
1504
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+
1510
+ override fun listTransactions(
1511
+ walletId: Double,
1512
+ skipSync: Boolean,
1513
+ promise: Promise
1514
+ ) {
1515
+ coroutineScope.launch(Dispatchers.IO) {
1516
+ try {
1517
+ val session = WalletStore.get(walletId.toInt())
1518
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1519
+
1520
+ val online = session.online
1521
+
1522
+ val transactions = session.wallet.listTransactions(online, skipSync)
1523
+ val transactionsArray = Arguments.createArray()
1524
+
1525
+ transactions.forEach { tx ->
1526
+ val txMap = Arguments.createMap()
1527
+ val txTypeString = when (tx.transactionType) {
1528
+ org.rgbtools.TransactionType.RGB_SEND -> "RGB_SEND"
1529
+ org.rgbtools.TransactionType.DRAIN -> "DRAIN"
1530
+ org.rgbtools.TransactionType.CREATE_UTXOS -> "CREATE_UTXOS"
1531
+ org.rgbtools.TransactionType.USER -> "USER"
1532
+ }
1533
+ txMap.putString("transactionType", txTypeString)
1534
+ txMap.putString("txid", tx.txid)
1535
+ txMap.putDouble("received", tx.received.toDouble())
1536
+ txMap.putDouble("sent", tx.sent.toDouble())
1537
+ txMap.putDouble("fee", tx.fee.toDouble())
1538
+ tx.confirmationTime?.let { blockTime ->
1539
+ txMap.putDouble("confirmationTime", blockTime.timestamp.toDouble())
1540
+ }
1541
+ transactionsArray.pushMap(txMap)
1542
+ }
1543
+
1544
+ withContext(Dispatchers.Main) {
1545
+ promise.resolve(transactionsArray)
1546
+ }
1547
+ } catch (e: Exception) {
1548
+ Log.e(TAG, "listTransactions error: ${e.message}", e)
1549
+ withContext(Dispatchers.Main) {
1550
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1551
+ }
1552
+ }
1553
+ }
1554
+ }
1555
+
1556
+ override fun listTransfers(
1557
+ walletId: Double,
1558
+ assetId: String?,
1559
+ promise: Promise
1560
+ ) {
1561
+ coroutineScope.launch(Dispatchers.IO) {
1562
+ try {
1563
+ val session = WalletStore.get(walletId.toInt())
1564
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1565
+
1566
+ val transfers = session.wallet.listTransfers(assetId)
1567
+ val transfersArray = Arguments.createArray()
1568
+
1569
+ transfers.forEach { transfer ->
1570
+ val transferMap = Arguments.createMap()
1571
+ transferMap.putInt("idx", transfer.idx)
1572
+ transferMap.putInt("batchTransferIdx", transfer.batchTransferIdx)
1573
+ transferMap.putDouble("createdAt", transfer.createdAt.toDouble())
1574
+ transferMap.putDouble("updatedAt", transfer.updatedAt.toDouble())
1575
+
1576
+ val kindString = when (transfer.kind) {
1577
+ org.rgbtools.TransferKind.ISSUANCE -> "ISSUANCE"
1578
+ org.rgbtools.TransferKind.RECEIVE_BLIND -> "RECEIVE_BLIND"
1579
+ org.rgbtools.TransferKind.RECEIVE_WITNESS -> "RECEIVE_WITNESS"
1580
+ org.rgbtools.TransferKind.SEND -> "SEND"
1581
+ org.rgbtools.TransferKind.INFLATION -> "INFLATION"
1582
+ }
1583
+ transferMap.putString("kind", kindString)
1584
+
1585
+ val statusString = when (transfer.status) {
1586
+ org.rgbtools.TransferStatus.WAITING_COUNTERPARTY -> "WAITING_COUNTERPARTY"
1587
+ org.rgbtools.TransferStatus.WAITING_CONFIRMATIONS -> "WAITING_CONFIRMATIONS"
1588
+ org.rgbtools.TransferStatus.SETTLED -> "SETTLED"
1589
+ org.rgbtools.TransferStatus.FAILED -> "FAILED"
1590
+ }
1591
+ transferMap.putString("status", statusString)
1592
+
1593
+ transfer.txid?.let { transferMap.putString("txid", it) }
1594
+ transfer.recipientId?.let { transferMap.putString("recipientId", it) }
1595
+ transfer.expiration?.let { transferMap.putDouble("expiration", it.toDouble()) }
1596
+
1597
+ transfer.requestedAssignment?.let {
1598
+ transferMap.putMap("requestedAssignment", assignmentToMap(it))
1599
+ }
1600
+
1601
+ val assignmentsArray = Arguments.createArray()
1602
+ transfer.assignments.forEach { assignment ->
1603
+ assignmentsArray.pushMap(assignmentToMap(assignment))
1604
+ }
1605
+ transferMap.putArray("assignments", assignmentsArray)
1606
+
1607
+ transfer.receiveUtxo?.let {
1608
+ transferMap.putMap("receiveUtxo", outpointToMap(it))
1609
+ }
1610
+
1611
+ transfer.changeUtxo?.let {
1612
+ transferMap.putMap("changeUtxo", outpointToMap(it))
1613
+ }
1614
+
1615
+ val transportEndpointsMap = Arguments.createArray()
1616
+ transfer.transportEndpoints.forEach {
1617
+ val endpoint = Arguments.createMap()
1618
+ endpoint.putString("endpoint", it.endpoint)
1619
+ endpoint.putBoolean("used", it.used)
1620
+ endpoint.putString("transportType", it.transportType.toString())
1621
+ transportEndpointsMap.pushMap(endpoint)
1622
+ }
1623
+ transferMap.putArray("transportEndpoints", transportEndpointsMap)
1624
+
1625
+ transfer.invoiceString?.let { transferMap.putString("invoiceString", it) }
1626
+ transfer.consignmentPath?.let { transferMap.putString("consignmentPath", it) }
1627
+
1628
+ transfersArray.pushMap(transferMap)
1629
+ }
1630
+
1631
+ withContext(Dispatchers.Main) {
1632
+ promise.resolve(transfersArray)
1633
+ }
1634
+ } catch (e: Exception) {
1635
+ Log.e(TAG, "listTransfers error: ${e.message}", e)
1636
+ withContext(Dispatchers.Main) {
1637
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1638
+ }
1639
+ }
1640
+ }
1641
+ }
1642
+
1643
+ override fun listUnspents(
1644
+ walletId: Double,
1645
+ settledOnly: Boolean,
1646
+ skipSync: Boolean,
1647
+ promise: Promise
1648
+ ) {
1649
+ coroutineScope.launch(Dispatchers.IO) {
1650
+ try {
1651
+ val session = WalletStore.get(walletId.toInt())
1652
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1653
+
1654
+ val online = session.online
1655
+
1656
+ val unspents = session.wallet.listUnspents(online, settledOnly, skipSync)
1657
+ val unspentsArray = Arguments.createArray()
1658
+
1659
+ unspents.forEach { unspent ->
1660
+ val unspentMap = Arguments.createMap()
1661
+ val utxoMap = Arguments.createMap()
1662
+ val outpointMap = Arguments.createMap()
1663
+ outpointMap.putString("txid", unspent.utxo.outpoint.txid)
1664
+ outpointMap.putDouble("vout", unspent.utxo.outpoint.vout.toDouble())
1665
+ utxoMap.putMap("outpoint", outpointMap)
1666
+ utxoMap.putDouble("btcAmount", unspent.utxo.btcAmount.toDouble())
1667
+ utxoMap.putBoolean("colorable", unspent.utxo.colorable)
1668
+ utxoMap.putBoolean("exists", unspent.utxo.exists)
1669
+ unspentMap.putMap("utxo", utxoMap)
1670
+ unspentMap.putDouble("pendingBlinded", unspent.pendingBlinded.toDouble())
1671
+
1672
+ val rgbAllocationsArray = Arguments.createArray()
1673
+ unspent.rgbAllocations.forEach { allocation ->
1674
+ val allocationMap = Arguments.createMap()
1675
+ allocation.assetId?.let { allocationMap.putString("assetId", it) }
1676
+ allocationMap.putMap("assignment", assignmentToMap(allocation.assignment))
1677
+ allocationMap.putBoolean("settled", allocation.settled)
1678
+ rgbAllocationsArray.pushMap(allocationMap)
1679
+ }
1680
+ unspentMap.putArray("rgbAllocations", rgbAllocationsArray)
1681
+
1682
+ unspentsArray.pushMap(unspentMap)
1683
+ }
1684
+
1685
+ withContext(Dispatchers.Main) {
1686
+ promise.resolve(unspentsArray)
1687
+ }
1688
+ } catch (e: Exception) {
1689
+ Log.e(TAG, "listUnspents error: ${e.message}", e)
1690
+ withContext(Dispatchers.Main) {
1691
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1692
+ }
1693
+ }
1694
+ }
1695
+ }
1696
+
1697
+ override fun refresh(
1698
+ walletId: Double,
1699
+ assetId: String?,
1700
+ filter: ReadableArray,
1701
+ skipSync: Boolean,
1702
+ promise: Promise
1703
+ ) {
1704
+ coroutineScope.launch(Dispatchers.IO) {
1705
+ try {
1706
+ val session = WalletStore.get(walletId.toInt())
1707
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1708
+
1709
+ val online = session.online
1710
+ ?: throw IllegalStateException("Wallet is not online")
1711
+
1712
+ val filterList = mutableListOf<RefreshFilter>()
1713
+ for (i in 0 until filter.size()) {
1714
+ val filterMap = filter.getMap(i) ?: continue
1715
+ filterList.add(getRefreshFilter(filterMap))
1716
+ }
1717
+
1718
+ val refreshed = session.wallet.refresh(online, assetId, filterList, skipSync)
1719
+ val result = Arguments.createMap()
1720
+
1721
+ refreshed.forEach { (idx, refreshedTransfer) ->
1722
+ val refreshedMap = Arguments.createMap()
1723
+ refreshedTransfer.updatedStatus?.let { status ->
1724
+ val statusString = when (status) {
1725
+ org.rgbtools.TransferStatus.WAITING_COUNTERPARTY -> "WAITING_COUNTERPARTY"
1726
+ org.rgbtools.TransferStatus.WAITING_CONFIRMATIONS -> "WAITING_CONFIRMATIONS"
1727
+ org.rgbtools.TransferStatus.SETTLED -> "SETTLED"
1728
+ org.rgbtools.TransferStatus.FAILED -> "FAILED"
1729
+ }
1730
+ refreshedMap.putString("updatedStatus", statusString)
1731
+ }
1732
+ refreshedTransfer.failure?.let {
1733
+ refreshedMap.putString("failure", it.toString())
1734
+ }
1735
+ result.putMap(idx.toString(), refreshedMap)
1736
+ }
1737
+
1738
+ withContext(Dispatchers.Main) {
1739
+ promise.resolve(result)
1740
+ }
1741
+ } catch (e: Exception) {
1742
+ Log.e(TAG, "refresh error: ${e.message}", e)
1743
+ withContext(Dispatchers.Main) {
1744
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1745
+ }
1746
+ }
1747
+ }
1748
+ }
1749
+
1750
+ override fun send(
1751
+ walletId: Double,
1752
+ recipientMap: ReadableMap,
1753
+ donation: Boolean,
1754
+ feeRate: Double,
1755
+ minConfirmations: Double,
1756
+ skipSync: Boolean,
1757
+ promise: Promise
1758
+ ) {
1759
+ coroutineScope.launch(Dispatchers.IO) {
1760
+ try {
1761
+ val session = WalletStore.get(walletId.toInt())
1762
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1763
+
1764
+ val online = session.online
1765
+ ?: throw IllegalStateException("Wallet is not online")
1766
+
1767
+ // Convert ReadableMap to Map<String, List<Recipient>>
1768
+ val recipientMapNative = mutableMapOf<String, List<Recipient>>()
1769
+ val keys = recipientMap.keySetIterator()
1770
+ while (keys.hasNextKey()) {
1771
+ val key = keys.nextKey()
1772
+ val recipientsArray = recipientMap.getArray(key) ?: continue
1773
+ val recipientsList = mutableListOf<Recipient>()
1774
+ for (i in 0 until recipientsArray.size()) {
1775
+ val recipientMapItem = recipientsArray.getMap(i) ?: continue
1776
+ recipientsList.add(getRecipient(recipientMapItem))
1777
+ }
1778
+ recipientMapNative[key] = recipientsList
1779
+ }
1780
+
1781
+ val result = session.wallet.send(
1782
+ online,
1783
+ recipientMapNative,
1784
+ donation,
1785
+ feeRate.toULong(),
1786
+ minConfirmations.toInt().toUByte(),
1787
+ skipSync
1788
+ )
1789
+
1790
+ withContext(Dispatchers.Main) {
1791
+ promise.resolve(operationResultToMap(result))
1792
+ }
1793
+ } catch (e: Exception) {
1794
+ Log.e(TAG, "send error: ${e.message}", e)
1795
+ withContext(Dispatchers.Main) {
1796
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1797
+ }
1798
+ }
1799
+ }
1800
+ }
1801
+
1802
+ override fun sendBegin(
1803
+ walletId: Double,
1804
+ recipientMap: ReadableMap,
1805
+ donation: Boolean,
1806
+ feeRate: Double,
1807
+ minConfirmations: Double,
1808
+ externalInputs: ReadableArray?,
1809
+ externalOutputs: ReadableArray?,
1810
+ promise: Promise
1811
+ ) {
1812
+ coroutineScope.launch(Dispatchers.IO) {
1813
+ try {
1814
+ val session = WalletStore.get(walletId.toInt())
1815
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1816
+
1817
+ val online = session.online
1818
+ ?: throw IllegalStateException("Wallet is not online")
1819
+
1820
+ // Convert ReadableMap to Map<String, List<Recipient>>
1821
+ val recipientMapNative = mutableMapOf<String, List<Recipient>>()
1822
+ val keys = recipientMap.keySetIterator()
1823
+ while (keys.hasNextKey()) {
1824
+ val key = keys.nextKey()
1825
+ val recipientsArray = recipientMap.getArray(key) ?: continue
1826
+ val recipientsList = mutableListOf<Recipient>()
1827
+ for (i in 0 until recipientsArray.size()) {
1828
+ val recipientMapItem = recipientsArray.getMap(i) ?: continue
1829
+ recipientsList.add(getRecipient(recipientMapItem))
1830
+ }
1831
+ recipientMapNative[key] = recipientsList
1832
+ }
1833
+
1834
+ val externalInputsNative = if (externalInputs != null && externalInputs.size() > 0) {
1835
+ (0 until externalInputs.size()).mapNotNull { i -> externalInputs.getMap(i)?.let { getExternalInput(it) } }
1836
+ } else {
1837
+ null
1838
+ }
1839
+
1840
+ val externalOutputsNative = if (externalOutputs != null && externalOutputs.size() > 0) {
1841
+ (0 until externalOutputs.size()).mapNotNull { i -> externalOutputs.getMap(i)?.let { getExternalOutput(it) } }
1842
+ } else {
1843
+ null
1844
+ }
1845
+
1846
+ val psbt = session.wallet.sendBegin(
1847
+ online,
1848
+ recipientMapNative,
1849
+ donation,
1850
+ feeRate.toULong(),
1851
+ minConfirmations.toInt().toUByte(),
1852
+ externalInputsNative,
1853
+ externalOutputsNative
1854
+ )
1855
+
1856
+ withContext(Dispatchers.Main) {
1857
+ promise.resolve(psbt)
1858
+ }
1859
+ } catch (e: Exception) {
1860
+ Log.e(TAG, "sendBegin error: ${e.message}", e)
1861
+ withContext(Dispatchers.Main) {
1862
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1863
+ }
1864
+ }
1865
+ }
1866
+ }
1867
+
1868
+ override fun sendBtc(
1869
+ walletId: Double,
1870
+ address: String,
1871
+ amount: Double,
1872
+ feeRate: Double,
1873
+ skipSync: Boolean,
1874
+ promise: Promise
1875
+ ) {
1876
+ coroutineScope.launch(Dispatchers.IO) {
1877
+ try {
1878
+ val session = WalletStore.get(walletId.toInt())
1879
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1880
+
1881
+ val online = session.online
1882
+ ?: throw IllegalStateException("Wallet is not online")
1883
+
1884
+ val txid = session.wallet.sendBtc(
1885
+ online,
1886
+ address,
1887
+ amount.toULong(),
1888
+ feeRate.toULong(),
1889
+ skipSync
1890
+ )
1891
+
1892
+ withContext(Dispatchers.Main) {
1893
+ promise.resolve(txid)
1894
+ }
1895
+ } catch (e: Exception) {
1896
+ Log.e(TAG, "sendBtc error: ${e.message}", e)
1897
+ withContext(Dispatchers.Main) {
1898
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1899
+ }
1900
+ }
1901
+ }
1902
+ }
1903
+
1904
+ override fun sendBtcBegin(
1905
+ walletId: Double,
1906
+ address: String,
1907
+ amount: Double,
1908
+ feeRate: Double,
1909
+ skipSync: Boolean,
1910
+ promise: Promise
1911
+ ) {
1912
+ coroutineScope.launch(Dispatchers.IO) {
1913
+ try {
1914
+ val session = WalletStore.get(walletId.toInt())
1915
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1916
+
1917
+ val online = session.online
1918
+ ?: throw IllegalStateException("Wallet is not online")
1919
+
1920
+ val psbt = session.wallet.sendBtcBegin(
1921
+ online,
1922
+ address,
1923
+ amount.toULong(),
1924
+ feeRate.toULong(),
1925
+ skipSync
1926
+ )
1927
+
1928
+ withContext(Dispatchers.Main) {
1929
+ promise.resolve(psbt)
1930
+ }
1931
+ } catch (e: Exception) {
1932
+ Log.e(TAG, "sendBtcBegin error: ${e.message}", e)
1933
+ withContext(Dispatchers.Main) {
1934
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1935
+ }
1936
+ }
1937
+ }
1938
+ }
1939
+
1940
+ override fun sendBtcEnd(
1941
+ walletId: Double,
1942
+ signedPsbt: String,
1943
+ skipSync: Boolean,
1944
+ promise: Promise
1945
+ ) {
1946
+ coroutineScope.launch(Dispatchers.IO) {
1947
+ try {
1948
+ val session = WalletStore.get(walletId.toInt())
1949
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1950
+
1951
+ val online = session.online
1952
+ ?: throw IllegalStateException("Wallet is not online")
1953
+
1954
+ val txid = session.wallet.sendBtcEnd(online, signedPsbt, skipSync)
1955
+
1956
+ withContext(Dispatchers.Main) {
1957
+ promise.resolve(txid)
1958
+ }
1959
+ } catch (e: Exception) {
1960
+ Log.e(TAG, "sendBtcEnd error: ${e.message}", e)
1961
+ withContext(Dispatchers.Main) {
1962
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1967
+
1968
+ override fun sendEnd(
1969
+ walletId: Double,
1970
+ signedPsbt: String,
1971
+ skipSync: Boolean,
1972
+ promise: Promise
1973
+ ) {
1974
+ coroutineScope.launch(Dispatchers.IO) {
1975
+ try {
1976
+ val session = WalletStore.get(walletId.toInt())
1977
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
1978
+
1979
+ val online = session.online
1980
+ ?: throw IllegalStateException("Wallet is not online")
1981
+
1982
+ val result = session.wallet.sendEnd(online, signedPsbt, skipSync)
1983
+
1984
+ withContext(Dispatchers.Main) {
1985
+ promise.resolve(operationResultToMap(result))
1986
+ }
1987
+ } catch (e: Exception) {
1988
+ Log.e(TAG, "sendEnd error: ${e.message}", e)
1989
+ withContext(Dispatchers.Main) {
1990
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
1991
+ }
1992
+ }
1993
+ }
1994
+ }
1995
+
1996
+ override fun signPsbt(walletId: Double, unsignedPsbt: String, promise: Promise) {
1997
+ coroutineScope.launch(Dispatchers.IO) {
1998
+ try {
1999
+ val session = WalletStore.get(walletId.toInt())
2000
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
2001
+
2002
+ val signedPsbt = session.wallet.signPsbt(unsignedPsbt)
2003
+
2004
+ withContext(Dispatchers.Main) {
2005
+ promise.resolve(signedPsbt)
2006
+ }
2007
+ } catch (e: Exception) {
2008
+ Log.e(TAG, "signPsbt error: ${e.message}", e)
2009
+ withContext(Dispatchers.Main) {
2010
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
2011
+ }
2012
+ }
2013
+ }
2014
+ }
2015
+
2016
+ override fun sync(walletId: Double, promise: Promise) {
2017
+ coroutineScope.launch(Dispatchers.IO) {
2018
+ try {
2019
+ val session = WalletStore.get(walletId.toInt())
2020
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
2021
+
2022
+ val online = session.online
2023
+ ?: throw IllegalStateException("Wallet is not online")
2024
+
2025
+ session.wallet.sync(online)
2026
+ val refresh = session.wallet.refresh(online, null, emptyList(), false)
2027
+ val failed = session.wallet.failTransfers(online, null, false, false)
2028
+ val delete = session.wallet.deleteTransfers(null, false)
2029
+ val assets = session.wallet.listAssets(listOf())
2030
+ val rgb25Assets = assets.cfa
2031
+ val rgb20Assets = assets.nia
2032
+ val udaAssets = assets.uda
2033
+ if (rgb20Assets != null) {
2034
+ for (rgb20Asset in rgb20Assets) {
2035
+ val assetRefresh = session.wallet.refresh(online, rgb20Asset.assetId, listOf(), false)
2036
+ }
2037
+ }
2038
+ if (rgb25Assets != null) {
2039
+ for (rgb25Asset in rgb25Assets) {
2040
+ val assetRefresh = session.wallet.refresh(online, rgb25Asset.assetId, listOf(), false)
2041
+ }
2042
+ }
2043
+ if (udaAssets != null) {
2044
+ for (udaAsset in udaAssets) {
2045
+ val assetRefresh = session.wallet.refresh(online, udaAsset.assetId, listOf(), false)
2046
+ }
2047
+ }
2048
+ withContext(Dispatchers.Main) {
2049
+ promise.resolve(null)
2050
+ }
2051
+ } catch (e: Exception) {
2052
+ Log.e(TAG, "sync error: ${e.message}", e)
2053
+ withContext(Dispatchers.Main) {
2054
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
2055
+ }
2056
+ }
2057
+ }
2058
+ }
2059
+
2060
+ override fun witnessReceive(
2061
+ walletId: Double,
2062
+ assetId: String?,
2063
+ assignment: ReadableMap,
2064
+ durationSeconds: Double?,
2065
+ transportEndpoints: ReadableArray,
2066
+ minConfirmations: Double,
2067
+ promise: Promise
2068
+ ) {
2069
+ coroutineScope.launch(Dispatchers.IO) {
2070
+ try {
2071
+ val session = WalletStore.get(walletId.toInt())
2072
+ ?: throw IllegalStateException("Wallet with id $walletId not found")
2073
+
2074
+ val assignmentObj = getAssignment(assignment)
2075
+ val endpoints = mutableListOf<String>()
2076
+ for (i in 0 until transportEndpoints.size()) {
2077
+ endpoints.add(transportEndpoints.getString(i) ?: "")
2078
+ }
2079
+
2080
+ val receiveData = session.wallet.witnessReceive(
2081
+ assetId,
2082
+ assignmentObj,
2083
+ durationSeconds?.toInt()?.toUInt(),
2084
+ endpoints,
2085
+ minConfirmations.toInt().toUByte()
2086
+ )
2087
+
2088
+ withContext(Dispatchers.Main) {
2089
+ promise.resolve(receiveDataToMap(receiveData))
2090
+ }
2091
+ } catch (e: Exception) {
2092
+ Log.e(TAG, "witnessReceive error: ${e.message}", e)
2093
+ withContext(Dispatchers.Main) {
2094
+ promise.reject(getErrorClassName(e), parseErrorMessage(e.message), e)
2095
+ }
2096
+ }
2097
+ }
2098
+ }
2099
+
2100
+ }