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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-zcash",
3
- "version": "0.0.2",
3
+ "version": "0.2.1",
4
4
  "description": "Zcash library for React Native",
5
5
  "homepage": "https://github.com/EdgeApp/react-native-zcash",
6
6
  "repository": {
@@ -12,25 +12,26 @@
12
12
  "contributors": [
13
13
  "William Swanson <william@edge.app>",
14
14
  "Paul Puey <paul@edge.app>",
15
- "Matthew Piché <matthew@edge.app>"
15
+ "Matthew Piché <matthew@edge.app>",
16
+ "Kevin Gorham <kevin.gorham@z.cash>"
16
17
  ],
17
18
  "files": [
18
19
  "android/build.gradle",
19
20
  "android/src",
20
21
  "CHANGELOG.md",
21
22
  "react-native-zcash.podspec",
22
- "ios/RNZcash.h",
23
+ "ios/RNZcash.swift",
23
24
  "ios/RNZcash.m",
25
+ "ios/RNZcash-Bridging-Header.h",
24
26
  "ios/RNZcash.xcodeproj",
25
27
  "ios/RNZcash.xcworkspace",
26
28
  "lib",
27
29
  "package.json",
28
30
  "README.md",
31
+ "Scripts",
29
32
  "src"
30
33
  ],
31
- "main": "lib/rnzcash.cjs.js",
32
- "module": "lib/rnzcash.js",
33
- "browser": "lib/rnzcash.web.js",
34
+ "main": "lib/rnzcash.rn.js",
34
35
  "types": "lib/src/index.d.ts",
35
36
  "scripts": {
36
37
  "fix": "npm run lint -- --fix",
@@ -87,5 +88,7 @@
87
88
  "tmp": "^0.2.1",
88
89
  "typescript": "^3.9.3"
89
90
  },
90
- "react-native": "lib/rnzcash.rn.js"
91
+ "peerDependencies": {
92
+ "react-native": ">=0.47.0 <1.0.0"
93
+ }
91
94
  }
@@ -10,10 +10,13 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.authors = package['author']
12
12
 
13
- s.platform = :ios, "8.0"
13
+ s.swift_version = '5.4'
14
+ s.platform = :ios, "12.0"
14
15
  s.requires_arc = true
15
16
  s.source = { :git => "https://github.com/EdgeApp/react-native-zcash.git", :tag => "v#{s.version}" }
16
- s.source_files = "ios/**/*.{h,m}"
17
+ s.source_files = "ios/**/*.{h,m,swift}"
18
+
19
+ s.dependency "React"
20
+ s.dependency 'ZcashLightClientKit', '0.13.0-beta.2'
17
21
 
18
- s.dependency "React"
19
22
  end
@@ -1,6 +1,21 @@
1
- import { NativeModules } from 'react-native'
1
+ import {
2
+ EventSubscription,
3
+ NativeEventEmitter,
4
+ NativeModules
5
+ } from 'react-native'
2
6
 
3
- import { InitializerConfig, WalletBalance } from './types'
7
+ import {
8
+ BlockRange,
9
+ ConfirmedTransaction,
10
+ InitializerConfig,
11
+ Network,
12
+ PendingTransaction,
13
+ SpendInfo,
14
+ SynchronizerCallbacks,
15
+ SynchronizerStatus,
16
+ UnifiedViewingKey,
17
+ WalletBalance
18
+ } from './types'
4
19
 
5
20
  const { RNZcash } = NativeModules
6
21
 
@@ -9,105 +24,215 @@ const snooze: Function = (ms: number) =>
9
24
 
10
25
  type Callback = (...args: any[]) => any
11
26
 
12
- // export function subscribeToFoo(callback: Callback): void {
13
- // const eventEmitter = new NativeEventEmitter(RNZcash)
14
- // eventEmitter.addListener('FooEvent', callback)
15
- // }
16
-
17
27
  export const KeyTool = {
18
- deriveViewKey: async (seedBytesHex: string): Promise<string> => {
19
- const result = await RNZcash.deriveViewKey(seedBytesHex)
28
+ deriveViewingKey: async (
29
+ seedBytesHex: string,
30
+ network: Network
31
+ ): Promise<UnifiedViewingKey> => {
32
+ const result = await RNZcash.deriveViewingKey(seedBytesHex, network)
33
+ return result
34
+ },
35
+ deriveSpendingKey: async (
36
+ seedBytesHex: string,
37
+ network: Network
38
+ ): Promise<string> => {
39
+ const result = await RNZcash.deriveSpendingKey(seedBytesHex, network)
40
+ return result
41
+ }
42
+ }
43
+
44
+ export const AddressTool = {
45
+ deriveShieldedAddress: async (
46
+ viewingKey: string,
47
+ network: Network
48
+ ): Promise<string> => {
49
+ const result = await RNZcash.deriveShieldedAddress(viewingKey, network)
50
+ return result
51
+ },
52
+ isValidShieldedAddress: async (
53
+ address: string,
54
+ network: Network = 'mainnet'
55
+ ): Promise<boolean> => {
56
+ const result = await RNZcash.isValidShieldedAddress(address, network)
57
+ return result
58
+ },
59
+ isValidTransparentAddress: async (
60
+ address: string,
61
+ network: Network = 'mainnet'
62
+ ): Promise<boolean> => {
63
+ const result = await RNZcash.isValidTransparentAddress(address, network)
20
64
  return result
21
65
  }
22
- // deriveSpendingKey: async (seedBytesHex: string): Promise<string> => {return ''}
23
66
  }
24
- // const makeAddressTool = () => {
25
- // return {
26
- // deriveShieldedAddress: async (viewingKey: string): Promise<string> => {return ''},
27
- // deriveTransparentAddress: async (viewingKey: string): Promise<string> => {return ''},
28
- // isValidShieldedAddress: async (address: string): Promise<boolean> => {return true},
29
- // isValidTransparentAddress: async (address: string): Promise<boolean> => {return true}
30
- // }
31
- // }
32
- //
33
- // interface WalletBalance {
34
- // shieldedAvailable: string
35
- // shieldedTotal: string
36
- // transparentAvailable: string
37
- // transparentTotal: string
38
- // }
39
- // interface InitializerCallbacks {
40
- // onError: (e: Error): void
41
- // onTransactionsChanged: (): void
42
- // onBalanceChanged: (walletBalance: WalletBalance): void
43
- // }
44
- // interface SpendInfo {
45
- // zatoshi: string
46
- // toAddress: string
47
- // memo: string
48
- // fromAccountIndex: number
49
- // spendingKey?: string
50
- // }
51
-
52
- // interface ZcashTransaction {
53
- // txId: string
54
- // fee: string
55
- // value: string
56
- // direction: 'inbound' | 'outbound'
57
- // toAddress: string
58
- // memo?: string
59
- // minedHeight: number // 0 for unconfirmed
60
- // blockTime: number // UNIX timestamp
61
- // }
62
- // type PendingTransaction = ZcashTransaction & {
63
- // accountIndex: number
64
- // expiryHeight: number
65
- // cancelled: number
66
- // submitAttemps: number
67
- // errorMessage?: string
68
- // errorCode?: number
69
- // createTime: number
70
- // }
71
-
72
- // interface TransactionQuery {
73
- // offset?: number
74
- // limit?: number
75
- // startDate?: number
76
- // endDate?: number
77
- // }
67
+
78
68
  class Synchronizer {
79
- constructor(initializerConfig: InitializerConfig) {
80
- console.log(initializerConfig)
69
+ eventEmitter: NativeEventEmitter
70
+ subscriptions: EventSubscription[]
71
+ alias: string
72
+ network: Network
73
+
74
+ constructor(alias: string, network: Network) {
75
+ this.eventEmitter = new NativeEventEmitter(RNZcash)
76
+ this.subscriptions = []
77
+ this.alias = alias
78
+ this.network = network
79
+ }
80
+
81
+ /// ////////////////////////////////////////////////////////////////
82
+ // Start PoC behavior
83
+ // Here are a few functions to demonstrate functionality but the final library should not have these functions
84
+ //
85
+ async readyToSend(): Promise<boolean> {
86
+ const result = await RNZcash.readyToSend()
87
+ return result
88
+ }
89
+
90
+ async sendTestTransaction(
91
+ key: string,
92
+ address: string
93
+ ): Promise<PendingTransaction> {
94
+ // send an amount that's guaranteed to be too large for our wallet and expect it to fail but at least show how this is done
95
+ // simply change these two values to send a real transaction but ensure the function isn't called too often (although funds can't be spent if notes are unconfirmed)
96
+ const invalidValue = '9223372036854775807' // Max long value (change this to 10000 to send a small transaction equal to the miner's fee on every reload and you could send 10,000 of those before it equals 1 ZEC)
97
+ const invalidAccount = 99 // should be 0
98
+ return this.sendToAddress({
99
+ zatoshi: invalidValue,
100
+ toAddress: address,
101
+ memo: 'this is a test transaction that will fail',
102
+ fromAccountIndex: invalidAccount,
103
+ spendingKey: key
104
+ })
105
+ }
106
+
107
+ async getBlockCount(): Promise<number> {
108
+ const result = await RNZcash.getBlockCount()
109
+ return result
110
+ }
111
+ // End PoC behavior
112
+ /// ////////////////////////////////////////////////////////////////
113
+
114
+ async start(): Promise<String> {
115
+ const result = await RNZcash.start(this.alias)
116
+ return result
117
+ }
118
+
119
+ async stop(): Promise<String> {
120
+ this.unsubscribe()
121
+ const result = await RNZcash.stop(this.alias)
122
+ return result
123
+ }
124
+
125
+ async initialize(initializerConfig: InitializerConfig): Promise<void> {
126
+ await RNZcash.initialize(
127
+ initializerConfig.fullViewingKey.extfvk,
128
+ initializerConfig.fullViewingKey.extpub,
129
+ initializerConfig.birthdayHeight,
130
+ initializerConfig.alias,
131
+ initializerConfig.networkName,
132
+ initializerConfig.defaultHost,
133
+ initializerConfig.defaultPort
134
+ )
135
+ }
136
+
137
+ async getLatestNetworkHeight(alias: string): Promise<number> {
138
+ const result = await RNZcash.getLatestNetworkHeight(alias)
139
+ return result
140
+ }
141
+
142
+ async getLatestScannedHeight(): Promise<number> {
143
+ const result = await RNZcash.getLatestScannedHeight()
144
+ return result
145
+ }
146
+
147
+ async getProgress(): Promise<number> {
148
+ const result = await RNZcash.getProgress()
149
+ return result
150
+ }
151
+
152
+ async getStatus(): Promise<SynchronizerStatus> {
153
+ const result = await RNZcash.getStatus()
154
+ return SynchronizerStatus[result.name as keyof typeof SynchronizerStatus]
81
155
  }
82
156
 
83
- // static init (initializerConfig: InitializerConfig): void
84
- // setCallbackHandlers (callbacks: InitializerCallbacks): void
85
- // getLatestHeight (): number
86
- // getProgress (): number // 0-1
87
- // getStatus (): ??
88
157
  async getShieldedBalance(): Promise<WalletBalance> {
89
- const result = await RNZcash.getShieldedBalance()
158
+ const result = await RNZcash.getShieldedBalance(this.alias)
90
159
  return result
91
160
  }
92
161
 
93
162
  async getTransparentBalance(): Promise<WalletBalance> {
163
+ // TODO: integrate with lightwalletd service to provide taddr balance. Edge probably doesn't need this, though so it can wait.
164
+
94
165
  await snooze(0) // Hack to make typescript happy
95
166
  return {
96
- availableBalance: '0',
97
- totalBalance: '0'
167
+ availableZatoshi: '0',
168
+ totalZatoshi: '0'
98
169
  }
99
170
  }
171
+
172
+ async getTransactions(range: BlockRange): Promise<ConfirmedTransaction[]> {
173
+ const result = await RNZcash.getTransactions(
174
+ this.alias,
175
+ range.first,
176
+ range.last
177
+ )
178
+ return result
179
+ }
180
+
181
+ rescan(height: number): void {
182
+ RNZcash.rescan(this.alias, height)
183
+ }
184
+
185
+ async sendToAddress(spendInfo: SpendInfo): Promise<PendingTransaction> {
186
+ const result = await RNZcash.spendToAddress(
187
+ this.alias,
188
+ spendInfo.zatoshi,
189
+ spendInfo.toAddress,
190
+ spendInfo.memo,
191
+ spendInfo.fromAccountIndex,
192
+ // TODO: ask is it okay to send this across the boundary or should it live on the native side and never leave?
193
+ spendInfo.spendingKey
194
+ )
195
+ return result
196
+ }
197
+
100
198
  // estimateFee (spendInfo: SpendInfo): string
101
199
  // sendToAddress (spendInfo: SpendInfo): void
102
- // start (): void
103
- // stop (): void
200
+
104
201
  // getPendingTransactions (): PendingTransactions[]
105
202
  // getConfirmedTransactions (query: TransactionQuery): ZcashTransaction[]
203
+
204
+ // Events
205
+
206
+ subscribe({ onStatusChanged, onUpdate }: SynchronizerCallbacks): void {
207
+ this.setListener('StatusEvent', onStatusChanged)
208
+ this.setListener('UpdateEvent', onUpdate)
209
+ }
210
+
211
+ private setListener<T>(
212
+ eventName: string,
213
+ callback: Callback = (t: any) => null
214
+ ): void {
215
+ this.subscriptions.push(
216
+ this.eventEmitter.addListener(eventName, arg =>
217
+ arg.alias === this.alias ? callback(arg) : null
218
+ )
219
+ )
220
+ }
221
+
222
+ unsubscribe(): void {
223
+ this.subscriptions.forEach(subscription => {
224
+ subscription.remove()
225
+ })
226
+ }
106
227
  }
107
228
 
108
229
  export const makeSynchronizer = async (
109
230
  initializerConfig: InitializerConfig
110
231
  ): Promise<Synchronizer> => {
111
- await snooze(0) // Hack to make typescript happy
112
- return new Synchronizer(initializerConfig)
232
+ const synchronizer = new Synchronizer(
233
+ initializerConfig.alias,
234
+ initializerConfig.networkName
235
+ )
236
+ await synchronizer.initialize(initializerConfig)
237
+ return synchronizer
113
238
  }
package/src/types.ts CHANGED
@@ -1,12 +1,101 @@
1
+ export type Network = 'mainnet' | 'testnet'
2
+
1
3
  export interface WalletBalance {
2
- availableBalance: string
3
- totalBalance: string
4
+ availableZatoshi: string
5
+ totalZatoshi: string
4
6
  }
5
7
 
6
8
  export interface InitializerConfig {
7
- host: string
8
- port: number
9
- fullViewingKey: string
10
- // alias: ??
9
+ networkName: Network
10
+ defaultHost: string
11
+ defaultPort: number
12
+ fullViewingKey: UnifiedViewingKey
13
+ alias: string
11
14
  birthdayHeight: number
12
15
  }
16
+
17
+ export interface SpendInfo {
18
+ zatoshi: string
19
+ toAddress: string
20
+ memo: string
21
+ fromAccountIndex: number
22
+ spendingKey?: string
23
+ }
24
+
25
+ export interface TransactionQuery {
26
+ offset?: number
27
+ limit?: number
28
+ startDate?: number
29
+ endDate?: number
30
+ }
31
+
32
+ export interface ZcashTransaction {
33
+ txId: string
34
+ accountIndex: number
35
+ fee: string
36
+ value: string
37
+ direction: 'inbound' | 'outbound'
38
+ toAddress: string
39
+ memo?: string
40
+ minedHeight: number // 0 for unconfirmed
41
+ blockTime: number // UNIX timestamp
42
+ }
43
+
44
+ export type PendingTransaction = ZcashTransaction & {
45
+ expiryHeight: number
46
+ cancelled: number
47
+ submitAttempts: number
48
+ errorMessage?: string
49
+ errorCode?: number
50
+ createTime: number
51
+ }
52
+
53
+ export enum SynchronizerStatus {
54
+ /** Indicates that [stop] has been called on this Synchronizer and it will no longer be used. */
55
+ STOPPED,
56
+ /** Indicates that this Synchronizer is disconnected from its lightwalletd server. When set, a UI element may want to turn red. */
57
+ DISCONNECTED,
58
+ /** Indicates that this Synchronizer is actively downloading new blocks from the server. */
59
+ DOWNLOADING,
60
+ /** Indicates that this Synchronizer is actively validating new blocks that were downloaded from the server. Blocks need to be verified before they are scanned. This confirms that each block is chain-sequential, thereby detecting missing blocks and reorgs. */
61
+ VALIDATING,
62
+ /** Indicates that this Synchronizer is actively decrypting new blocks that were downloaded from the server. */
63
+ SCANNING,
64
+ /** Indicates that this Synchronizer is actively enhancing newly scanned blocks with additional transaction details, fetched from the server. */
65
+ ENHANCING,
66
+ /** Indicates that this Synchronizer is fully up to date and ready for all wallet functions. When set, a UI element may want to turn green. In this state, the balance can be trusted. */
67
+ SYNCED
68
+ }
69
+
70
+ export interface UnifiedViewingKey {
71
+ extfvk: string
72
+ extpub: string
73
+ }
74
+
75
+ export interface UpdateEvent {
76
+ lastDownloadedHeight: number
77
+ lastScannedHeight: number
78
+ scanProgress: number // 0 - 100
79
+ networkBlockHeight: number
80
+ }
81
+
82
+ export interface SynchronizerCallbacks {
83
+ onShieldedBalanceChanged(walletBalance: WalletBalance): void
84
+ onStatusChanged(status: SynchronizerStatus): void
85
+ onUpdate(event: UpdateEvent): void
86
+ onTransactionsChanged(count: number): void
87
+ onPendingTransactionUpdated(tx: PendingTransaction): void
88
+ }
89
+
90
+ export interface BlockRange {
91
+ first: number
92
+ last: number
93
+ }
94
+
95
+ export interface ConfirmedTransaction {
96
+ value: string
97
+ memo?: string
98
+ minedHeight: number
99
+ rawTransactionId: string
100
+ toAddress?: string
101
+ }
@@ -1,94 +0,0 @@
1
- package com.reactlibrary;
2
-
3
- import com.facebook.react.bridge.ReactApplicationContext;
4
- import com.facebook.react.bridge.ReactContext;
5
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
6
- import com.facebook.react.bridge.ReactMethod;
7
- import com.facebook.react.bridge.Callback;
8
- import com.facebook.react.bridge.Promise;
9
- import com.facebook.react.bridge.ReactMethod;
10
- import com.facebook.react.modules.core.DeviceEventManagerModule;
11
- import com.facebook.react.bridge.WritableMap;
12
- import com.facebook.react.bridge.Arguments;
13
-
14
-
15
-
16
-
17
- public class RNZcashModule extends ReactContextBaseJavaModule {
18
- private final ReactApplicationContext reactContext;
19
-
20
- public RNZcashModule(ReactApplicationContext reactContext) {
21
- super(reactContext);
22
- this.reactContext = reactContext;
23
- }
24
-
25
- @Override
26
- public String getName() {
27
- return "RNZcash";
28
- }
29
-
30
- @ReactMethod
31
- public void getNumTransactions(
32
- Float N,
33
- Promise promise) {
34
- try {
35
- // (new Runnable(){
36
- // @Override
37
- // public void run() {
38
- // WritableMap params = Arguments.createMap();
39
- // params.putString("foo", "bar");
40
- // try {
41
- // Thread.sleep(20000);
42
- // }catch(InterruptedException e) {
43
- // //do nothing
44
- // }
45
- // sendEvent(reactContext, "FooEvent", params);
46
- // }
47
- // }).run();
48
- WritableMap params = Arguments.createMap();
49
- params.putString("foo", "bar3");
50
- sendEvent(reactContext, "FooEvent", params);
51
- try {
52
- Thread.sleep(20000);
53
- }catch(InterruptedException e) {
54
- //do nothing
55
- }
56
- promise.resolve(N+43);
57
- } catch (Exception e) {
58
- promise.reject("Err", e);
59
- }
60
- }
61
-
62
- @ReactMethod
63
- public void deriveViewKey(
64
- String seedBytesHex,
65
- Promise promise) {
66
- try {
67
- promise.resolve(seedBytesHex+"-viewKey");
68
- } catch (Exception e) {
69
- promise.reject("Err", e);
70
- }
71
- }
72
-
73
- @ReactMethod
74
- public void getShieldedBalance(
75
- Promise promise) {
76
- try {
77
- WritableMap params = Arguments.createMap();
78
- params.putString("availableBalance", "123");
79
- params.putString("totalBalance", "1234");
80
- promise.resolve(params);
81
- } catch (Exception e) {
82
- promise.reject("Err", e);
83
- }
84
- }
85
-
86
- private void sendEvent(ReactContext reactContext,
87
- String eventName,
88
- WritableMap params) {
89
- reactContext
90
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
91
- .emit(eventName, params);
92
-
93
- }
94
- }
@@ -1,21 +0,0 @@
1
- package com.reactlibrary;
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.Arrays;
8
- import java.util.Collections;
9
- import java.util.List;
10
-
11
- public class RNZcashPackage implements ReactPackage {
12
- @Override
13
- public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
14
- return Arrays.<NativeModule>asList(new RNZcashModule(reactContext));
15
- }
16
-
17
- @Override
18
- public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
19
- return Collections.emptyList();
20
- }
21
- }
package/ios/RNZcash.h DELETED
@@ -1,5 +0,0 @@
1
- #import <React/RCTBridgeModule.h>
2
-
3
- @interface RNZcash : NSObject <RCTBridgeModule>
4
-
5
- @end
@@ -1,3 +0,0 @@
1
- 'use strict';
2
-
3
- //# sourceMappingURL=rnzcash.cjs.js.map
@@ -1,3 +0,0 @@
1
- // @flow
2
-
3
- export * from '../src/index.flow.js'
@@ -1 +0,0 @@
1
- {"version":3,"file":"rnzcash.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/lib/rnzcash.js DELETED
@@ -1,2 +0,0 @@
1
-
2
- //# sourceMappingURL=rnzcash.js.map
@@ -1,3 +0,0 @@
1
- // @flow
2
-
3
- export * from '../src/index.flow.js'
@@ -1 +0,0 @@
1
- {"version":3,"file":"rnzcash.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- 'use strict';
2
-
3
- //# sourceMappingURL=rnzcash.web.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rnzcash.web.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}