react-native-zcash 0.0.2 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # React Native Zcash
2
2
 
3
+ ## 0.2.1 (2022-03-16)
4
+
5
+ - Update the ZcashLightClientKit dependency
6
+ - Remove unused build scripts
7
+
8
+ ## 0.2.0 (2022-01-10)
9
+
10
+ - Add iOS support
11
+ - Android: Cleanup unused methods
12
+
13
+ ## 0.1.0 (2021-11-09)
14
+
15
+ - Initial release
16
+
3
17
  ## 0.0.2
4
18
 
5
19
  - Add stubs for deriveViewKey and getShieldedBalance
@@ -1,19 +1,34 @@
1
1
  buildscript {
2
+ ext.versions = [
3
+ 'kotlin': '1.5.0',
4
+ 'zcash': '1.3.0-beta18',
5
+ 'room': '2.3.0'
6
+ ]
2
7
  repositories {
8
+ mavenLocal()
3
9
  google()
10
+ mavenCentral()
4
11
  jcenter()
12
+ maven {
13
+ url 'https://jitpack.io'
14
+ }
15
+ maven {
16
+ url "https://plugins.gradle.org/m2/"
17
+ }
5
18
  }
6
19
 
7
20
  dependencies {
8
- classpath 'com.android.tools.build:gradle:3.2.1'
21
+ classpath 'com.android.tools.build:gradle:4.0.2'
22
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
9
23
  }
10
24
  }
11
25
 
12
26
  apply plugin: 'com.android.library'
13
- apply plugin: 'maven'
27
+ apply plugin: 'kotlin-android'
28
+ apply plugin: 'kotlin-kapt'
14
29
 
15
30
  def safeExtGet(prop, fallback) {
16
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
31
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
17
32
  }
18
33
 
19
34
  def DEFAULT_COMPILE_SDK_VERSION = 28
@@ -24,7 +39,13 @@ def DEFAULT_TARGET_SDK_VERSION = 27
24
39
  android {
25
40
  compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
26
41
  buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
27
-
42
+ compileOptions {
43
+ sourceCompatibility JavaVersion.VERSION_1_8
44
+ targetCompatibility JavaVersion.VERSION_1_8
45
+ }
46
+ kotlinOptions {
47
+ jvmTarget = JavaVersion.VERSION_1_8
48
+ }
28
49
  defaultConfig {
29
50
  minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
30
51
  targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
@@ -37,9 +58,26 @@ android {
37
58
  }
38
59
 
39
60
  repositories {
40
- mavenCentral()
61
+ mavenLocal()
62
+ google()
63
+ jcenter()
64
+ // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
65
+ maven {
66
+ url("../node_modules/react-native/android")
67
+ }
41
68
  }
42
69
 
43
70
  dependencies {
71
+ // React-Native
44
72
  implementation 'com.facebook.react:react-native:+'
73
+
74
+ // Zcash
75
+ implementation "cash.z.ecc.android:zcash-android-sdk:${versions.zcash}"
76
+
77
+ // Process Room annotations
78
+ kapt "androidx.room:room-compiler:${versions.room}"
79
+
80
+ implementation "androidx.paging:paging-runtime-ktx:2.1.2"
81
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
82
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
45
83
  }
@@ -1,4 +1,4 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.reactlibrary">
2
+ package="app.edge.rnzcash">
3
3
 
4
4
  </manifest>
@@ -0,0 +1,317 @@
1
+ package app.edge.rnzcash;
2
+
3
+ import androidx.paging.PagedList
4
+ import cash.z.ecc.android.sdk.Initializer
5
+ import cash.z.ecc.android.sdk.SdkSynchronizer
6
+ import cash.z.ecc.android.sdk.Synchronizer
7
+ import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
8
+ import cash.z.ecc.android.sdk.block.CompactBlockProcessor
9
+ import cash.z.ecc.android.sdk.db.entity.*
10
+ import cash.z.ecc.android.sdk.ext.*
11
+ import cash.z.ecc.android.sdk.transaction.*
12
+ import cash.z.ecc.android.sdk.type.*
13
+ import cash.z.ecc.android.sdk.tool.DerivationTool
14
+ import com.facebook.react.bridge.*
15
+ import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
16
+ import kotlinx.coroutines.CoroutineScope
17
+ import kotlinx.coroutines.cancel
18
+ import kotlinx.coroutines.flow.distinctUntilChanged
19
+ import kotlinx.coroutines.flow.filter
20
+ import kotlinx.coroutines.launch
21
+ import java.nio.charset.StandardCharsets
22
+ import kotlin.coroutines.EmptyCoroutineContext
23
+
24
+ class WalletSynchronizer constructor(val initializer: Initializer) {
25
+
26
+ val synchronizer: SdkSynchronizer = Synchronizer(
27
+ initializer
28
+ ) as SdkSynchronizer
29
+ val repository = PagedTransactionRepository(initializer.context, 10, initializer.rustBackend, initializer.birthday, initializer.viewingKeys)
30
+ var isStarted = false
31
+ }
32
+
33
+ class RNZcashModule(private val reactContext: ReactApplicationContext) :
34
+ ReactContextBaseJavaModule(reactContext) {
35
+
36
+ /**
37
+ * Scope for anything that out-lives the synchronizer, meaning anything that can be used before
38
+ * the synchronizer starts or after it stops. Everything else falls within the scope of the
39
+ * synchronizer and should use `synchronizer.coroutineScope` whenever a scope is needed.
40
+ *
41
+ * In a real production app, we'd use the scope of the parent activity
42
+ */
43
+ var moduleScope: CoroutineScope = CoroutineScope(EmptyCoroutineContext)
44
+ var synchronizerMap = mutableMapOf<String, WalletSynchronizer>()
45
+
46
+ val networks = mapOf("mainnet" to ZcashNetwork.Mainnet, "testnet" to ZcashNetwork.Testnet)
47
+
48
+ override fun getName() = "RNZcash"
49
+
50
+ @ReactMethod
51
+ fun initialize(extfvk: String, extpub: String, birthdayHeight: Int, alias: String, networkName: String = "mainnet", defaultHost: String = "mainnet.lightwalletd.com", defaultPort: Int = 9067, promise: Promise) =
52
+ promise.wrap {
53
+ Twig.plant(TroubleshootingTwig())
54
+ var vk = UnifiedViewingKey(extfvk, extpub)
55
+ if (synchronizerMap[alias] == null) {
56
+ val initializer = Initializer(reactApplicationContext) { config ->
57
+ config.importedWalletBirthday(birthdayHeight)
58
+ config.setViewingKeys(vk)
59
+ config.setNetwork(networks[networkName] ?: ZcashNetwork.Mainnet, defaultHost, defaultPort)
60
+ config.alias = alias
61
+ }
62
+ synchronizerMap[alias] = WalletSynchronizer(initializer)
63
+ val wallet = getWallet(alias)
64
+ }
65
+ val wallet = getWallet(alias)
66
+ wallet.synchronizer.hashCode().toString()
67
+
68
+ }
69
+
70
+ @ReactMethod
71
+ fun start(alias: String, promise: Promise) = promise.wrap {
72
+ val wallet = getWallet(alias)
73
+ if (!wallet.isStarted) {
74
+ wallet.synchronizer.prepare()
75
+ wallet.synchronizer.start(moduleScope)
76
+ val scope = wallet.synchronizer.coroutineScope
77
+ wallet.synchronizer.processorInfo.collectWith(scope, { update ->
78
+ sendEvent("UpdateEvent") { args ->
79
+ args.putString("alias", alias)
80
+ args.putInt("lastDownloadedHeight", update.lastDownloadedHeight)
81
+ args.putInt("lastScannedHeight", update.lastScannedHeight)
82
+ args.putInt("scanProgress", update.scanProgress)
83
+ args.putInt("networkBlockHeight", update.networkBlockHeight)
84
+ }
85
+ })
86
+ wallet.synchronizer.status.collectWith(scope, { status ->
87
+ sendEvent("StatusEvent") { args ->
88
+ args.putString("alias", alias)
89
+ args.putString("name", status.toString())
90
+ }
91
+ })
92
+ wallet.synchronizer.saplingBalances.collectWith(scope, { walletBalance ->
93
+ sendEvent("BalanceEvent") { args ->
94
+ args.putString("alias", alias)
95
+ args.putString("availableZatoshi", walletBalance.availableZatoshi.toString())
96
+ args.putString("totalZatoshi", walletBalance.totalZatoshi.toString())
97
+ }
98
+ })
99
+ // add 'distinctUntilChanged' to filter by events that represent changes in txs, rather than each poll
100
+ wallet.synchronizer.clearedTransactions.distinctUntilChanged().collectWith(scope, { txList ->
101
+ sendEvent("TransactionEvent") { args ->
102
+ args.putString("alias", alias)
103
+ args.putBoolean("hasChanged", true)
104
+ args.putInt("transactionCount", txList.count())
105
+ }
106
+ })
107
+ wallet.repository.prepare()
108
+ wallet.isStarted = true
109
+ }
110
+ "success"
111
+ }
112
+
113
+ @ReactMethod
114
+ fun stop(alias: String, promise: Promise) = promise.wrap {
115
+ val wallet = getWallet(alias)
116
+ wallet.synchronizer.stop()
117
+ synchronizerMap.remove(alias)
118
+ "success"
119
+ }
120
+
121
+ @ReactMethod
122
+ fun getTransactions(alias: String, first: Int, last: Int, promise: Promise) {
123
+ val wallet = getWallet(alias)
124
+ moduleScope.launch {
125
+ promise.wrap {
126
+ val result = wallet.repository.findNewTransactions(first..last)
127
+ val nativeArray = Arguments.createArray()
128
+
129
+ for (i in 0..result.size - 1) {
130
+ val map = Arguments.createMap()
131
+ map.putString("value", result[i].value.toString())
132
+ map.putInt("minedHeight", result[i].minedHeight)
133
+ map.putInt("blockTimeInSeconds", result[i].blockTimeInSeconds.toInt())
134
+ map.putString("rawTransactionId", result[i].rawTransactionId.toHexReversed())
135
+ if (result[i].memo != null) map.putString("memo", result[i].memo?.decodeToString()?.trim('\u0000', '\uFFFD'))
136
+ if (result[i].toAddress != null) map.putString("toAddress", result[i].toAddress)
137
+ nativeArray.pushMap(map)
138
+ }
139
+
140
+ nativeArray
141
+ }
142
+ }
143
+ }
144
+
145
+ @ReactMethod
146
+ fun rescan(alias: String, height: Int, promise: Promise) {
147
+ val wallet = getWallet(alias)
148
+ moduleScope.launch {
149
+ wallet.synchronizer.rewindToNearestHeight(height)
150
+ }
151
+ }
152
+
153
+ @ReactMethod
154
+ fun deriveViewingKey(seedBytesHex: String, network: String = "mainnet", promise: Promise) {
155
+ var keys = DerivationTool.deriveUnifiedViewingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0]
156
+ val map = Arguments.createMap()
157
+ map.putString("extfvk", keys.extfvk)
158
+ map.putString("extpub", keys.extpub)
159
+ promise.resolve(map)
160
+ }
161
+
162
+ @ReactMethod
163
+ fun deriveSpendingKey(seedBytesHex: String, network: String = "mainnet", promise: Promise) = promise.wrap {
164
+ DerivationTool.deriveSpendingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0]
165
+ }
166
+
167
+ //
168
+ // Properties
169
+ //
170
+
171
+
172
+ @ReactMethod
173
+ fun getLatestNetworkHeight(alias: String, promise: Promise) = promise.wrap {
174
+ val wallet = getWallet(alias)
175
+ wallet.synchronizer.latestHeight
176
+ }
177
+
178
+ @ReactMethod
179
+ fun getShieldedBalance(alias: String, promise: Promise) = promise.wrap {
180
+ val wallet = getWallet(alias)
181
+ val map = Arguments.createMap()
182
+ map.putString("totalZatoshi", wallet.synchronizer.saplingBalances.value.totalZatoshi.toString(10))
183
+ map.putString("availableZatoshi", wallet.synchronizer.saplingBalances.value.availableZatoshi.toString(10))
184
+ map
185
+ }
186
+
187
+ @ReactMethod
188
+ fun spendToAddress(
189
+ alias: String,
190
+ zatoshi: String,
191
+ toAddress: String,
192
+ memo: String,
193
+ fromAccountIndex: Int,
194
+ spendingKey: String,
195
+ promise: Promise
196
+ ) {
197
+ val wallet = getWallet(alias)
198
+ wallet.synchronizer.coroutineScope.launch {
199
+ try {
200
+ wallet.synchronizer.sendToAddress(
201
+ spendingKey,
202
+ zatoshi.toLong(),
203
+ toAddress,
204
+ memo,
205
+ fromAccountIndex
206
+ ).collectWith(wallet.synchronizer.coroutineScope) {tx ->
207
+ // this block is called repeatedly for each update to the pending transaction, including all 10 confirmations
208
+ // the promise either shouldn't be used (and rely on events instead) or it can be resolved once the transaction is submitted to the network or mined
209
+ if (tx.isSubmitSuccess()) { // alternatively use it.isMined() but be careful about making a promise that never resolves!
210
+ val map = Arguments.createMap()
211
+ map.putString("txId", tx.rawTransactionId?.toHexReversed())
212
+ map.putString("raw", tx.raw?.toHexReversed())
213
+ promise.resolve(map)
214
+ } else if (tx.isFailure()) {
215
+ val map = Arguments.createMap()
216
+ map.putInt("expiryHeight", tx.expiryHeight)
217
+ map.putString("cancelled", tx.cancelled.toString())
218
+ map.putString("encodeAttempts", tx.encodeAttempts.toString())
219
+ map.putString("submitAttempts", tx.submitAttempts.toString())
220
+ if (tx.errorMessage != null) map.putString("errorMessage", tx.errorMessage)
221
+ if (tx.errorCode != null) map.putString("errorCode", tx.errorCode.toString())
222
+ promise.resolve(false)
223
+ }
224
+ }
225
+ } catch (t: Throwable) {
226
+ promise.reject("Err", t)
227
+ }
228
+ }
229
+
230
+ }
231
+
232
+ //
233
+ // AddressTool
234
+ //
235
+
236
+ @ReactMethod
237
+ fun deriveShieldedAddress(viewingKey: String, network: String = "mainnet", promise: Promise) = promise.wrap {
238
+ DerivationTool.deriveShieldedAddress(viewingKey, networks.getOrDefault(network, ZcashNetwork.Mainnet))
239
+ }
240
+
241
+ @ReactMethod
242
+ fun isValidShieldedAddress(address: String, network: String, promise: Promise) {
243
+ moduleScope.launch {
244
+ promise.wrap {
245
+ var isValid = false
246
+ val wallets = synchronizerMap.asIterable()
247
+ for (wallet in wallets) {
248
+ if (wallet.value.synchronizer.network.networkName == network) {
249
+ isValid = wallet.value.synchronizer.isValidShieldedAddr(address)
250
+ break
251
+ }
252
+ }
253
+ isValid
254
+ }
255
+ }
256
+ }
257
+
258
+ @ReactMethod
259
+ fun isValidTransparentAddress(address: String, network: String, promise: Promise) {
260
+ moduleScope.launch {
261
+ promise.wrap {
262
+ var isValid = false
263
+ val wallets = synchronizerMap.asIterable()
264
+ for (wallet in wallets) {
265
+ if (wallet.value.synchronizer.network.networkName == network) {
266
+ isValid = wallet.value.synchronizer.isValidTransparentAddr(address)
267
+ break
268
+ }
269
+ }
270
+ isValid
271
+ }
272
+ }
273
+ }
274
+
275
+ //
276
+ // Utilities
277
+ //
278
+
279
+ /**
280
+ * Retrieve wallet object from synchronizer map
281
+ */
282
+ private fun getWallet(alias: String): WalletSynchronizer {
283
+ val wallet = synchronizerMap.get(alias)
284
+ if (wallet == null) throw Exception("Wallet not found")
285
+ return wallet
286
+ }
287
+
288
+
289
+ /**
290
+ * Wrap the given block of logic in a promise, rejecting for any error.
291
+ */
292
+ private inline fun <T> Promise.wrap(block: () -> T) {
293
+ try {
294
+ resolve(block())
295
+ } catch (t: Throwable) {
296
+ reject("Err", t)
297
+ }
298
+ }
299
+
300
+ private fun sendEvent(eventName: String, putArgs: (WritableMap) -> Unit) {
301
+ val args = Arguments.createMap()
302
+ putArgs(args)
303
+ reactApplicationContext
304
+ .getJSModule(RCTDeviceEventEmitter::class.java)
305
+ .emit(eventName, args)
306
+ }
307
+
308
+ // TODO: move this to the SDK
309
+ inline fun ByteArray?.toUtf8Memo(): String {
310
+ return if (this == null || this[0] >= 0xF5) "" else try {
311
+ // trim empty and "replacement characters" for codes that can't be represented in unicode
312
+ String(this, StandardCharsets.UTF_8).trim('\u0000', '\uFFFD')
313
+ } catch (t: Throwable) {
314
+ "Unable to parse memo."
315
+ }
316
+ }
317
+ }
@@ -0,0 +1,18 @@
1
+ package app.edge.rnzcash;
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+ import java.util.Collections.emptyList
8
+
9
+
10
+ class RNZcashPackage : ReactPackage {
11
+ override fun createNativeModules(reactContext: ReactApplicationContext) =
12
+ listOf<NativeModule>(
13
+ RNZcashModule(reactContext)
14
+ )
15
+
16
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
17
+ emptyList()
18
+ }
@@ -0,0 +1,6 @@
1
+ //
2
+ // Use this file to import your target's public headers that you would like to expose to Swift.
3
+ //
4
+
5
+ #import <React/RCTBridgeModule.h>
6
+ #import <React/RCTEventEmitter.h>
package/ios/RNZcash.m CHANGED
@@ -1,45 +1,91 @@
1
- // Formatted using clang-format with default settings.
2
-
3
- #import "RNZcash.h"
4
-
5
- @implementation RNZcash
6
-
7
- RCT_EXPORT_MODULE()
8
-
9
- // - (id)init {
10
- // self = [super init];
11
- // return self;
12
- // }
13
-
14
- + (BOOL)requiresMainQueueSetup {
15
- return NO;
16
- }
17
-
18
- RCT_REMAP_METHOD(getNumTransactions, getNumTransactions:(float) N
19
- resolver:(RCTPromiseResolveBlock)resolve
20
- rejecter:(RCTPromiseRejectBlock)reject)
21
- {
22
- // NSUInteger const onehundred = 100;
23
- // NSNumber *val = [NSNumber numberWithInteger:(N+42)];
24
- NSNumber *myNum = [NSNumber numberWithFloat:(N+123)];
25
- resolve(myNum);
26
- }
27
-
28
- RCT_REMAP_METHOD(deriveViewKey, deriveViewKey:(NSString *) seedBytesHex
29
- resolver:(RCTPromiseResolveBlock)resolve
30
- rejecter:(RCTPromiseRejectBlock)reject)
31
- {
32
- NSString *out = [seedBytesHex stringByAppendingString:@"-viewKey"];
33
- resolve(out);
34
- }
35
-
36
-
37
- RCT_REMAP_METHOD(getShieldedBalance,
38
- resolver:(RCTPromiseResolveBlock)resolve
39
- rejecter:(RCTPromiseRejectBlock)reject)
40
- {
41
- NSDictionary *dict = @{@"availableBalance":@"123",@"totalBalance": @"1234"};
42
- resolve(dict);
43
- }
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+
5
+ @interface RCT_EXTERN_MODULE(RNZcash, RCTEventEmitter<RCTBridgeModule>)
6
+
7
+ // Synchronizer
8
+ RCT_EXTERN_METHOD(initialize:(NSString *)extfvk
9
+ :(NSString *)extpub
10
+ :(NSInteger *)birthdayHeight
11
+ :(NSString *)alias
12
+ :(NSString *)networkName
13
+ :(NSString *)defaultHost
14
+ :(NSInteger *)defaultPort
15
+ resolver:(RCTPromiseResolveBlock)resolve
16
+ rejecter:(RCTPromiseRejectBlock)reject
17
+ )
18
+
19
+ RCT_EXTERN_METHOD(start:(NSString *)alias
20
+ resolver:(RCTPromiseResolveBlock)resolve
21
+ rejecter:(RCTPromiseRejectBlock)reject
22
+ )
23
+
24
+ RCT_EXTERN_METHOD(stop:(NSString *)alias
25
+ resolver:(RCTPromiseResolveBlock)resolve
26
+ rejecter:(RCTPromiseRejectBlock)reject
27
+ )
28
+
29
+ RCT_EXTERN_METHOD(spendToAddress:(NSString *)alias
30
+ :(NSString *)zatoshi
31
+ :(NSString *)toAddress
32
+ :(NSString *)memo
33
+ :(NSInteger *)fromAccountIndex
34
+ :(NSString *)spendingKey
35
+ resolver:(RCTPromiseResolveBlock)resolve
36
+ rejecter:(RCTPromiseRejectBlock)reject
37
+ )
38
+
39
+ RCT_EXTERN_METHOD(getTransactions:(NSString *)alias
40
+ :(NSInteger *)first
41
+ :(NSInteger *)last
42
+ resolver:(RCTPromiseResolveBlock)resolve
43
+ rejecter:(RCTPromiseRejectBlock)reject
44
+ )
45
+
46
+ RCT_EXTERN_METHOD(getShieldedBalance:(NSString *)alias
47
+ resolver:(RCTPromiseResolveBlock)resolve
48
+ rejecter:(RCTPromiseRejectBlock)reject
49
+ )
50
+
51
+ RCT_EXTERN_METHOD(rescan:(NSString *)alias
52
+ :(NSInteger *)height
53
+ resolver:(RCTPromiseResolveBlock)resolve
54
+ rejecter:(RCTPromiseRejectBlock)reject
55
+ )
56
+
57
+ // Derivation tool
58
+ RCT_EXTERN_METHOD(deriveViewingKey:(NSString *)seed
59
+ :(NSString *)network
60
+ resolver:(RCTPromiseResolveBlock)resolve
61
+ rejecter:(RCTPromiseRejectBlock)reject
62
+ )
63
+
64
+ RCT_EXTERN_METHOD(deriveSpendingKey:(NSString *)seed
65
+ :(NSString *)network
66
+ resolver:(RCTPromiseResolveBlock)resolve
67
+ rejecter:(RCTPromiseRejectBlock)reject
68
+ )
69
+
70
+ RCT_EXTERN_METHOD(deriveShieldedAddress:(NSString *)viewingKey
71
+ :(NSString *)network
72
+ resolver:(RCTPromiseResolveBlock)resolve
73
+ rejecter:(RCTPromiseRejectBlock)reject
74
+ )
75
+
76
+ RCT_EXTERN_METHOD(isValidTransparentAddress:(NSString *)address
77
+ :(NSString *)network
78
+ resolver:(RCTPromiseResolveBlock)resolve
79
+ rejecter:(RCTPromiseRejectBlock)reject
80
+ )
81
+
82
+ RCT_EXTERN_METHOD(isValidShieldedAddress:(NSString *)address
83
+ :(NSString *)network
84
+ resolver:(RCTPromiseResolveBlock)resolve
85
+ rejecter:(RCTPromiseRejectBlock)reject
86
+ )
87
+
88
+ // Events
89
+ RCT_EXTERN_METHOD(supportedEvents)
44
90
 
45
91
  @end