react-native-monero 0.0.1 → 0.2.0

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
@@ -0,0 +1,13 @@
1
+ # react-native-monero
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.2.0 (2026-06-29)
6
+
7
+ - changed: Switch the package manager from yarn to npm (package-lock.json + `.npmrc` legacy-peer-deps), matching the other Edge currency repos.
8
+ - fixed: Monero Full Node (monerod) wallets now sync, calculate max, and send when the Nym mixnet is enabled. The Nym HTTP client rebuilt request URLs from an unreliable SSL flag and emitted `http://` on port 443, so every monerod RPC failed under Nym while the same wallet worked with Nym off. Derive the scheme from the daemon address (and treat port 443 as https).
9
+ - fixed: `getWalletStatus` now reads the live wallet balance on every call instead of only recomputing it when the synced block height changes, so a pending incoming transaction (or pending change after a send) is reflected immediately rather than after the next block.
10
+
11
+ ## 0.1.0 (2026-06-15)
12
+
13
+ - Initial release
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # react-native-monero-lwsf
1
+ # react-native-monero
2
2
 
3
3
  This library packages Monero C++ client for use on React Native.
4
4
 
@@ -14,7 +14,7 @@ First, add this library to your React Native app using NPM or Yarn, and run `pod
14
14
  Here is a simple usage example:
15
15
 
16
16
  ```js
17
- import { what } from 'react-native-monero-lwsf'
17
+ import { what } from 'react-native-monero'
18
18
 
19
19
  // ???
20
20
  ```
@@ -33,7 +33,7 @@ This script does the following tasks:
33
33
  - Compile shared libraries for Android.
34
34
  - Compile an iOS universal static library and put it into an XCFramework.
35
35
 
36
- The `build-native` script is also the place to make edits when upgrading any of the third-party dependencies. The react-native-monero-lwsf repo doesn't include these third-party C++ sources, since they are enormous.
36
+ The `build-native` script is also the place to make edits when upgrading any of the third-party dependencies. The react-native-monero repo doesn't include these third-party C++ sources, since they are enormous.
37
37
 
38
38
  For this to work, you need:
39
39
 
@@ -1,24 +1,36 @@
1
1
  package app.edge.rnmonero;
2
2
 
3
+ import com.facebook.react.bridge.Arguments;
3
4
  import com.facebook.react.bridge.ReadableArray;
4
5
  import com.facebook.react.bridge.Promise;
5
6
  import com.facebook.react.bridge.ReactApplicationContext;
6
7
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
7
8
  import com.facebook.react.bridge.ReactMethod;
9
+ import com.facebook.react.bridge.WritableMap;
10
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
8
11
  import java.util.HashMap;
9
12
  import java.util.Map;
13
+ import java.util.concurrent.ExecutorService;
14
+ import java.util.concurrent.Executors;
10
15
 
11
16
  public class RnMoneroModule extends ReactContextBaseJavaModule {
12
17
  private native String callMoneroJNI(String method, String[] arguments);
13
18
 
14
19
  private native String[] getMethodNames();
15
20
 
21
+ // Sets the C++ callback so WalletListener events route through onWalletEvent
22
+ private native void initEventCallback();
23
+
24
+ private final ExecutorService moneroExecutor = Executors.newSingleThreadExecutor();
25
+ private final ExecutorService nymCompletionExecutor = Executors.newSingleThreadExecutor();
26
+
16
27
  static {
17
28
  System.loadLibrary("rnmonero");
18
29
  }
19
30
 
20
31
  public RnMoneroModule(ReactApplicationContext reactContext) {
21
32
  super(reactContext);
33
+ initEventCallback();
22
34
  }
23
35
 
24
36
  @Override
@@ -42,10 +54,40 @@ public class RnMoneroModule extends ReactContextBaseJavaModule {
42
54
  strings[i] = arguments.getString(i);
43
55
  }
44
56
 
45
- try {
46
- promise.resolve(callMoneroJNI(method, strings));
47
- } catch (Exception e) {
48
- promise.reject("MoneroError", e);
49
- }
57
+ ExecutorService executor =
58
+ isNymCompletionMethod(method) ? nymCompletionExecutor : moneroExecutor;
59
+ executor.execute(() -> {
60
+ try {
61
+ promise.resolve(callMoneroJNI(method, strings));
62
+ } catch (Exception e) {
63
+ promise.reject("MoneroError", e);
64
+ }
65
+ });
66
+ }
67
+
68
+ private static boolean isNymCompletionMethod(String method) {
69
+ return "resolveFetch".equals(method) || "rejectFetch".equals(method);
70
+ }
71
+
72
+ // Required by React Native NativeEventEmitter on Android
73
+ @ReactMethod
74
+ public void addListener(String eventName) {}
75
+
76
+ @ReactMethod
77
+ public void removeListeners(int count) {}
78
+
79
+ // Called from C++ WalletListener via JNI on the SDK refresh thread.
80
+ // Forwards the event to JS through RCTDeviceEventEmitter.
81
+ public void onWalletEvent(String walletId, String eventName, String jsonPayload) {
82
+ ReactApplicationContext ctx = getReactApplicationContext();
83
+ if (ctx == null || !ctx.hasActiveReactInstance()) return;
84
+
85
+ WritableMap params = Arguments.createMap();
86
+ params.putString("walletId", walletId);
87
+ params.putString("eventName", eventName);
88
+ params.putString("data", jsonPayload);
89
+
90
+ ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
91
+ .emit("MoneroWalletEvent", params);
50
92
  }
51
93
  }
@@ -1,4 +1,5 @@
1
1
  #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
2
3
 
3
- @interface MoneroModule : NSObject <RCTBridgeModule>
4
+ @interface MoneroModule : RCTEventEmitter <RCTBridgeModule>
4
5
  @end
@@ -1,12 +1,64 @@
1
1
  #import "MoneroModule.h"
2
2
  #include "monero-methods.hpp"
3
3
 
4
+ @interface MoneroModule () {
5
+ dispatch_queue_t _moneroQueue;
6
+ dispatch_queue_t _nymCompletionQueue;
7
+ }
8
+ @end
9
+
10
+ static bool isNymCompletionMethod(const std::string& method) {
11
+ return method == "resolveFetch" || method == "rejectFetch";
12
+ }
13
+
14
+ // Global pointer so the C++ callback can reach the ObjC module instance
15
+ static __weak MoneroModule* g_module = nil;
16
+
4
17
  @implementation MoneroModule
5
18
 
6
- RCT_EXPORT_MODULE();
19
+ RCT_EXPORT_MODULE(MoneroLwsfModule);
7
20
 
8
21
  + (BOOL)requiresMainQueueSetup { return NO; }
9
22
 
23
+ - (instancetype)init {
24
+ self = [super init];
25
+ if (self) {
26
+ g_module = self;
27
+ _moneroQueue = dispatch_queue_create(
28
+ "app.edge.rnmonero.monero", DISPATCH_QUEUE_SERIAL);
29
+ _nymCompletionQueue = dispatch_queue_create(
30
+ "app.edge.rnmonero.nymCompletion", DISPATCH_QUEUE_SERIAL);
31
+
32
+ // Wire the C++ wallet-event callback to this module's event emitter
33
+ moneroSetEventCallback([](const std::string& walletId,
34
+ const std::string& eventName,
35
+ const std::string& jsonPayload) {
36
+ MoneroModule* module = g_module;
37
+ if (module == nil) return;
38
+
39
+ NSString *nsWalletId = [NSString stringWithUTF8String:walletId.c_str()];
40
+ NSString *nsEventName = [NSString stringWithUTF8String:eventName.c_str()];
41
+ NSString *nsPayload = [NSString stringWithUTF8String:jsonPayload.c_str()];
42
+
43
+ dispatch_async(dispatch_get_main_queue(), ^{
44
+ [module sendEventWithName:@"MoneroWalletEvent" body:@{
45
+ @"walletId": nsWalletId,
46
+ @"eventName": nsEventName,
47
+ @"data": nsPayload
48
+ }];
49
+ });
50
+ });
51
+ }
52
+ return self;
53
+ }
54
+
55
+ - (NSArray<NSString *> *)supportedEvents {
56
+ return @[@"MoneroWalletEvent"];
57
+ }
58
+
59
+ - (void)startObserving {}
60
+ - (void)stopObserving {}
61
+
10
62
  RCT_REMAP_METHOD(
11
63
  callMonero,
12
64
  callMoneroMethod:(NSString *)method
@@ -18,7 +70,7 @@ RCT_REMAP_METHOD(
18
70
 
19
71
  // Re-package the arguments:
20
72
  NSUInteger length = [arguments count];
21
- std::vector<const std::string> strings;
73
+ std::vector<std::string> strings;
22
74
  strings.reserve(length);
23
75
  for (NSUInteger i = 0; i < length; ++i) {
24
76
  NSString *string = [arguments objectAtIndex:i];
@@ -35,21 +87,26 @@ RCT_REMAP_METHOD(
35
87
  return;
36
88
  }
37
89
 
38
- // Call the method, with error handling:
39
- try {
40
- const std::string out = moneroMethods[i].method(strings);
41
- resolve(
42
- [NSString stringWithCString:out.c_str() encoding:NSUTF8StringEncoding]
43
- );
44
- } catch (std::exception &e) {
45
- reject(
46
- @"Error",
47
- [NSString stringWithCString:e.what() encoding:NSUTF8StringEncoding],
48
- nil
49
- );
50
- } catch (...) {
51
- reject(@"Error", @"monero threw a C++ exception", nil);
52
- }
90
+ const MoneroMethod methodInfo = moneroMethods[i];
91
+ dispatch_queue_t queue =
92
+ isNymCompletionMethod(methodString) ? _nymCompletionQueue : _moneroQueue;
93
+ dispatch_async(queue, ^{
94
+ // Call the method, with error handling:
95
+ try {
96
+ const std::string out = methodInfo.method(strings);
97
+ resolve(
98
+ [NSString stringWithCString:out.c_str() encoding:NSUTF8StringEncoding]
99
+ );
100
+ } catch (std::exception &e) {
101
+ reject(
102
+ @"Error",
103
+ [NSString stringWithCString:e.what() encoding:NSUTF8StringEncoding],
104
+ nil
105
+ );
106
+ } catch (...) {
107
+ reject(@"Error", @"monero threw a C++ exception", nil);
108
+ }
109
+ });
53
110
  return;
54
111
  }
55
112
 
@@ -0,0 +1,44 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>AvailableLibraries</key>
6
+ <array>
7
+ <dict>
8
+ <key>BinaryPath</key>
9
+ <string>libmonero-module.a</string>
10
+ <key>LibraryIdentifier</key>
11
+ <string>ios-arm64</string>
12
+ <key>LibraryPath</key>
13
+ <string>libmonero-module.a</string>
14
+ <key>SupportedArchitectures</key>
15
+ <array>
16
+ <string>arm64</string>
17
+ </array>
18
+ <key>SupportedPlatform</key>
19
+ <string>ios</string>
20
+ </dict>
21
+ <dict>
22
+ <key>BinaryPath</key>
23
+ <string>libmonero-module.a</string>
24
+ <key>LibraryIdentifier</key>
25
+ <string>ios-arm64_x86_64-simulator</string>
26
+ <key>LibraryPath</key>
27
+ <string>libmonero-module.a</string>
28
+ <key>SupportedArchitectures</key>
29
+ <array>
30
+ <string>arm64</string>
31
+ <string>x86_64</string>
32
+ </array>
33
+ <key>SupportedPlatform</key>
34
+ <string>ios</string>
35
+ <key>SupportedPlatformVariant</key>
36
+ <string>simulator</string>
37
+ </dict>
38
+ </array>
39
+ <key>CFBundlePackageType</key>
40
+ <string>XFWK</string>
41
+ <key>XCFrameworkFormatVersion</key>
42
+ <string>1.0</string>
43
+ </dict>
44
+ </plist>
@@ -0,0 +1,158 @@
1
+ import type { DerivedKeys, EncodeUriParams, GeneratedWallet, NetworkType, ParsedUri, Recipient, SignedTransaction, TransactionPriority, TransactionsPage, WalletBackend, WalletStatus } from './types';
2
+ /**
3
+ * The shape of the native C++ module exposed to React Native.
4
+ *
5
+ * You do not normally need this, but it is accessible as
6
+ * `require('react-native').NativeModules.MoneroLwsfModule`.
7
+ *
8
+ * Pass this object to the `CppBridge` constructor to re-assemble the API.
9
+ */
10
+ export interface NativeMoneroLwsfModule {
11
+ readonly callMonero: (name: string, jsonArguments: string[]) => Promise<string>;
12
+ readonly methodNames: string[];
13
+ readonly documentDirectory: string;
14
+ }
15
+ export declare class CppBridge {
16
+ private readonly module;
17
+ constructor(moneroLwsfModule: NativeMoneroLwsfModule);
18
+ /**
19
+ * Generate a new wallet's keys in memory (no disk I/O).
20
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
21
+ * @param language - Mnemonic language (e.g., "English")
22
+ * @returns Generated wallet with mnemonic and spend keys
23
+ */
24
+ generateWallet(nettype: NetworkType, language?: string): Promise<GeneratedWallet>;
25
+ /**
26
+ * Derive all keys from a mnemonic (no disk I/O).
27
+ * @param mnemonic - The 25-word mnemonic seed
28
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
29
+ * @returns All four keys (view and spend, public and secret)
30
+ */
31
+ seedAndKeysFromMnemonic(mnemonic: string, nettype: NetworkType): Promise<DerivedKeys>;
32
+ /**
33
+ * Get the current network blockchain height from a daemon.
34
+ * @param backend - Backend type ('lws' or 'monerod')
35
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
36
+ * @param daemonAddress - Daemon address to query
37
+ * @returns Current blockchain height
38
+ */
39
+ getNetworkBlockHeight(backend: WalletBackend, nettype: NetworkType, daemonAddress: string): Promise<number>;
40
+ /**
41
+ * Validate a Monero address.
42
+ * @param address - The address to validate
43
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
44
+ * @returns true if valid, false otherwise
45
+ */
46
+ isValidAddress(address: string, nettype: NetworkType): Promise<boolean>;
47
+ /**
48
+ * Open or create a wallet. If already open, returns current status.
49
+ * If wallet exists on disk, opens it. Otherwise creates from mnemonic.
50
+ * @param walletId - Unique identifier for the wallet
51
+ * @param backend - Backend type ("lws" or "monerod")
52
+ * @param mnemonic - The 25-word mnemonic seed
53
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
54
+ * @param restoreHeight - Block height to restore from
55
+ * @param daemonAddress - Daemon address to connect to
56
+ * @returns Current wallet status (heights and balances)
57
+ */
58
+ openWallet(walletId: string, backend: WalletBackend, mnemonic: string, password: string, nettype: NetworkType, restoreHeight: number, daemonAddress: string): Promise<WalletStatus>;
59
+ /**
60
+ * Get the current status of an open wallet.
61
+ * @param walletId - Unique identifier for the wallet
62
+ * @returns Current wallet status (heights and balances)
63
+ */
64
+ getWalletStatus(walletId: string): Promise<WalletStatus>;
65
+ /**
66
+ * Close an open wallet.
67
+ * @param walletId - Unique identifier for the wallet to close
68
+ */
69
+ closeWallet(walletId: string): Promise<void>;
70
+ /**
71
+ * Delete a wallet's files from disk. Closes the wallet first if it's open.
72
+ * @param walletId - Unique identifier for the wallet
73
+ * @param backend - Backend type ('lws' or 'monerod')
74
+ */
75
+ deleteWallet(walletId: string, backend: WalletBackend): Promise<void>;
76
+ /**
77
+ * Get all transactions with pagination.
78
+ * @param walletId - Unique identifier for the wallet
79
+ * @param page - Page number (0-indexed)
80
+ * @param pageSize - Number of transactions per page
81
+ * @param sort - Sort order: 'asc' (oldest first) or 'desc' (newest first), pending always at end
82
+ * @returns Paginated transactions with metadata
83
+ */
84
+ getAllTransactions(walletId: string, page: number, pageSize: number, sort?: 'asc' | 'desc'): Promise<TransactionsPage>;
85
+ /**
86
+ * Create a transaction (supports multiple recipients).
87
+ * The transaction is created and signed but not broadcast yet.
88
+ * @param walletId - Unique identifier for the wallet
89
+ * @param recipients - Array of recipients with addresses and amounts (atomic units)
90
+ * @param priority - Transaction priority (0=Default, 1=Low, 2=Medium, 3=High)
91
+ * @returns SignedTransaction with txid, signedTxHex, and fee (atomic units)
92
+ */
93
+ createTransaction(walletId: string, recipients: Recipient[], priority: TransactionPriority): Promise<SignedTransaction>;
94
+ /**
95
+ * Broadcast a previously created transaction.
96
+ * @param walletId - Unique identifier for the wallet
97
+ * @param signedTx - The signed transaction string from createTransaction
98
+ * @returns The transaction hash
99
+ * @throws Error if broadcast fails
100
+ */
101
+ broadcastTransaction(walletId: string, signedTx: string): Promise<string>;
102
+ /**
103
+ * Parse a monero: URI into its components.
104
+ * @param uri - The monero: URI to parse
105
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
106
+ * @returns Parsed URI components
107
+ * @throws Error if URI is invalid
108
+ */
109
+ parseUri(uri: string, nettype: NetworkType): Promise<ParsedUri>;
110
+ /**
111
+ * Encode a monero: URI from components.
112
+ * @param params - URI components (address, amount, etc.)
113
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
114
+ * @returns The encoded monero: URI
115
+ * @throws Error if parameters are invalid
116
+ */
117
+ encodeUri(params: EncodeUriParams, nettype: NetworkType): Promise<string>;
118
+ /**
119
+ * Set the API key for LWS (Light Wallet Server) requests.
120
+ * Once set, the key will be included in all subsequent LWS HTTP POST requests
121
+ * as an "api_key" field in the JSON body.
122
+ * @param apiKey - The API key to include in LWS requests
123
+ */
124
+ setLwsApiKey(apiKey: string): Promise<void>;
125
+ /**
126
+ * Enable or disable the Nym fetch interceptor.
127
+ *
128
+ * When enabled, all LWSF HTTP POST requests that the C++ wallet code
129
+ * would have issued are redirected through the native event bridge. The
130
+ * consumer must register a handler via `NativeEventEmitter` on the
131
+ * "MoneroWalletEvent" event with `eventName === 'nymFetchRequest'` and
132
+ * call `resolveFetch` / `rejectFetch` to complete the request.
133
+ *
134
+ * @param enabled - Whether to route HTTP through the JS fetch bridge
135
+ * @param baseUrl - scheme://host[:port] of the LWSF server (must match
136
+ * the daemon address used at openWallet time). Empty
137
+ * when disabling.
138
+ */
139
+ setNymEnabled(enabled: boolean, baseUrl: string): Promise<void>;
140
+ /**
141
+ * Resolve a pending nym fetch request that was emitted as a
142
+ * `nymFetchRequest` wallet event. Must be called with the same
143
+ * `requestId` carried on the incoming event.
144
+ *
145
+ * @param requestId - id forwarded via the native event
146
+ * @param status - HTTP status code returned from fetch
147
+ * @param bodyBase64 - response body encoded as base64
148
+ */
149
+ resolveFetch(requestId: string, status: number, bodyBase64: string): Promise<void>;
150
+ /**
151
+ * Reject a pending nym fetch request. The blocked C++ caller will
152
+ * receive a runtime_error bubbled as an RPC failure.
153
+ *
154
+ * @param requestId - id forwarded via the native event
155
+ * @param errorMessage - human-readable error description
156
+ */
157
+ rejectFetch(requestId: string, errorMessage: string): Promise<void>;
158
+ }
@@ -0,0 +1,266 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CppBridge = void 0;
4
+ const types_1 = require("./types");
5
+ class CppBridge {
6
+ constructor(moneroLwsfModule) {
7
+ this.module = moneroLwsfModule;
8
+ }
9
+ /**
10
+ * Generate a new wallet's keys in memory (no disk I/O).
11
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
12
+ * @param language - Mnemonic language (e.g., "English")
13
+ * @returns Generated wallet with mnemonic and spend keys
14
+ */
15
+ async generateWallet(nettype, language = 'English') {
16
+ const response = await this.module.callMonero('generateWallet', [
17
+ (0, types_1.networkTypeToIntString)(nettype),
18
+ language
19
+ ]);
20
+ return JSON.parse(response);
21
+ }
22
+ /**
23
+ * Derive all keys from a mnemonic (no disk I/O).
24
+ * @param mnemonic - The 25-word mnemonic seed
25
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
26
+ * @returns All four keys (view and spend, public and secret)
27
+ */
28
+ async seedAndKeysFromMnemonic(mnemonic, nettype) {
29
+ const response = await this.module.callMonero('seedAndKeysFromMnemonic', [
30
+ mnemonic,
31
+ (0, types_1.networkTypeToIntString)(nettype)
32
+ ]);
33
+ return JSON.parse(response);
34
+ }
35
+ /**
36
+ * Get the current network blockchain height from a daemon.
37
+ * @param backend - Backend type ('lws' or 'monerod')
38
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
39
+ * @param daemonAddress - Daemon address to query
40
+ * @returns Current blockchain height
41
+ */
42
+ async getNetworkBlockHeight(backend, nettype, daemonAddress) {
43
+ const response = await this.module.callMonero('getNetworkBlockHeight', [
44
+ backend,
45
+ (0, types_1.networkTypeToIntString)(nettype),
46
+ daemonAddress
47
+ ]);
48
+ return parseInt(response, 10);
49
+ }
50
+ /**
51
+ * Validate a Monero address.
52
+ * @param address - The address to validate
53
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
54
+ * @returns true if valid, false otherwise
55
+ */
56
+ async isValidAddress(address, nettype) {
57
+ const response = await this.module.callMonero('isValidAddress', [
58
+ address,
59
+ (0, types_1.networkTypeToIntString)(nettype)
60
+ ]);
61
+ return response === 'true';
62
+ }
63
+ /**
64
+ * Open or create a wallet. If already open, returns current status.
65
+ * If wallet exists on disk, opens it. Otherwise creates from mnemonic.
66
+ * @param walletId - Unique identifier for the wallet
67
+ * @param backend - Backend type ("lws" or "monerod")
68
+ * @param mnemonic - The 25-word mnemonic seed
69
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
70
+ * @param restoreHeight - Block height to restore from
71
+ * @param daemonAddress - Daemon address to connect to
72
+ * @returns Current wallet status (heights and balances)
73
+ */
74
+ async openWallet(walletId, backend, mnemonic, password, nettype, restoreHeight, daemonAddress) {
75
+ const response = await this.module.callMonero('openWallet', [
76
+ this.module.documentDirectory,
77
+ walletId,
78
+ backend,
79
+ mnemonic,
80
+ password,
81
+ (0, types_1.networkTypeToIntString)(nettype),
82
+ restoreHeight.toString(),
83
+ daemonAddress
84
+ ]);
85
+ return JSON.parse(response);
86
+ }
87
+ /**
88
+ * Get the current status of an open wallet.
89
+ * @param walletId - Unique identifier for the wallet
90
+ * @returns Current wallet status (heights and balances)
91
+ */
92
+ async getWalletStatus(walletId) {
93
+ const response = await this.module.callMonero('getWalletStatus', [walletId]);
94
+ return JSON.parse(response);
95
+ }
96
+ /**
97
+ * Close an open wallet.
98
+ * @param walletId - Unique identifier for the wallet to close
99
+ */
100
+ async closeWallet(walletId) {
101
+ await this.module.callMonero('closeWallet', [walletId]);
102
+ }
103
+ /**
104
+ * Delete a wallet's files from disk. Closes the wallet first if it's open.
105
+ * @param walletId - Unique identifier for the wallet
106
+ * @param backend - Backend type ('lws' or 'monerod')
107
+ */
108
+ async deleteWallet(walletId, backend) {
109
+ await this.module.callMonero('deleteWallet', [
110
+ this.module.documentDirectory,
111
+ walletId,
112
+ backend
113
+ ]);
114
+ }
115
+ /**
116
+ * Get all transactions with pagination.
117
+ * @param walletId - Unique identifier for the wallet
118
+ * @param page - Page number (0-indexed)
119
+ * @param pageSize - Number of transactions per page
120
+ * @param sort - Sort order: 'asc' (oldest first) or 'desc' (newest first), pending always at end
121
+ * @returns Paginated transactions with metadata
122
+ */
123
+ async getAllTransactions(walletId, page, pageSize, sort = 'asc') {
124
+ const response = await this.module.callMonero('getAllTransactions', [
125
+ walletId,
126
+ page.toString(),
127
+ pageSize.toString(),
128
+ sort
129
+ ]);
130
+ return JSON.parse(response);
131
+ }
132
+ /**
133
+ * Create a transaction (supports multiple recipients).
134
+ * The transaction is created and signed but not broadcast yet.
135
+ * @param walletId - Unique identifier for the wallet
136
+ * @param recipients - Array of recipients with addresses and amounts (atomic units)
137
+ * @param priority - Transaction priority (0=Default, 1=Low, 2=Medium, 3=High)
138
+ * @returns SignedTransaction with txid, signedTxHex, and fee (atomic units)
139
+ */
140
+ async createTransaction(walletId, recipients, priority) {
141
+ const addresses = recipients.map(r => r.address).join(',');
142
+ const amounts = recipients.map(r => r.amount).join(',');
143
+ const response = await this.module.callMonero('createTransaction', [
144
+ walletId,
145
+ addresses,
146
+ amounts,
147
+ priority.toString(),
148
+ this.module.documentDirectory
149
+ ]);
150
+ return JSON.parse(response);
151
+ }
152
+ /**
153
+ * Broadcast a previously created transaction.
154
+ * @param walletId - Unique identifier for the wallet
155
+ * @param signedTx - The signed transaction string from createTransaction
156
+ * @returns The transaction hash
157
+ * @throws Error if broadcast fails
158
+ */
159
+ async broadcastTransaction(walletId, signedTx) {
160
+ const response = await this.module.callMonero('broadcastTransaction', [
161
+ walletId,
162
+ signedTx,
163
+ this.module.documentDirectory
164
+ ]);
165
+ return response;
166
+ }
167
+ /**
168
+ * Parse a monero: URI into its components.
169
+ * @param uri - The monero: URI to parse
170
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
171
+ * @returns Parsed URI components
172
+ * @throws Error if URI is invalid
173
+ */
174
+ async parseUri(uri, nettype) {
175
+ const response = await this.module.callMonero('parseUri', [
176
+ uri,
177
+ (0, types_1.networkTypeToIntString)(nettype)
178
+ ]);
179
+ const parsed = JSON.parse(response);
180
+ if (typeof parsed === 'object' && 'error' in parsed) {
181
+ throw new Error(parsed.error);
182
+ }
183
+ return parsed;
184
+ }
185
+ /**
186
+ * Encode a monero: URI from components.
187
+ * @param params - URI components (address, amount, etc.)
188
+ * @param nettype - Network type (0=mainnet, 1=testnet, 2=stagenet)
189
+ * @returns The encoded monero: URI
190
+ * @throws Error if parameters are invalid
191
+ */
192
+ async encodeUri(params, nettype) {
193
+ const response = await this.module.callMonero('encodeUri', [
194
+ params.address,
195
+ params.paymentId ?? '',
196
+ params.amount,
197
+ params.txDescription ?? '',
198
+ params.recipientName ?? '',
199
+ (0, types_1.networkTypeToIntString)(nettype)
200
+ ]);
201
+ // Check for error response (JSON object with error field)
202
+ if (response.startsWith('{')) {
203
+ const parsed = JSON.parse(response);
204
+ if (typeof parsed === 'object' && 'error' in parsed) {
205
+ throw new Error(parsed.error);
206
+ }
207
+ }
208
+ return response;
209
+ }
210
+ /**
211
+ * Set the API key for LWS (Light Wallet Server) requests.
212
+ * Once set, the key will be included in all subsequent LWS HTTP POST requests
213
+ * as an "api_key" field in the JSON body.
214
+ * @param apiKey - The API key to include in LWS requests
215
+ */
216
+ async setLwsApiKey(apiKey) {
217
+ await this.module.callMonero('setLwsApiKey', [apiKey]);
218
+ }
219
+ /**
220
+ * Enable or disable the Nym fetch interceptor.
221
+ *
222
+ * When enabled, all LWSF HTTP POST requests that the C++ wallet code
223
+ * would have issued are redirected through the native event bridge. The
224
+ * consumer must register a handler via `NativeEventEmitter` on the
225
+ * "MoneroWalletEvent" event with `eventName === 'nymFetchRequest'` and
226
+ * call `resolveFetch` / `rejectFetch` to complete the request.
227
+ *
228
+ * @param enabled - Whether to route HTTP through the JS fetch bridge
229
+ * @param baseUrl - scheme://host[:port] of the LWSF server (must match
230
+ * the daemon address used at openWallet time). Empty
231
+ * when disabling.
232
+ */
233
+ async setNymEnabled(enabled, baseUrl) {
234
+ await this.module.callMonero('setNymEnabled', [
235
+ enabled ? 'true' : 'false',
236
+ baseUrl
237
+ ]);
238
+ }
239
+ /**
240
+ * Resolve a pending nym fetch request that was emitted as a
241
+ * `nymFetchRequest` wallet event. Must be called with the same
242
+ * `requestId` carried on the incoming event.
243
+ *
244
+ * @param requestId - id forwarded via the native event
245
+ * @param status - HTTP status code returned from fetch
246
+ * @param bodyBase64 - response body encoded as base64
247
+ */
248
+ async resolveFetch(requestId, status, bodyBase64) {
249
+ await this.module.callMonero('resolveFetch', [
250
+ requestId,
251
+ status.toString(),
252
+ bodyBase64
253
+ ]);
254
+ }
255
+ /**
256
+ * Reject a pending nym fetch request. The blocked C++ caller will
257
+ * receive a runtime_error bubbled as an RPC failure.
258
+ *
259
+ * @param requestId - id forwarded via the native event
260
+ * @param errorMessage - human-readable error description
261
+ */
262
+ async rejectFetch(requestId, errorMessage) {
263
+ await this.module.callMonero('rejectFetch', [requestId, errorMessage]);
264
+ }
265
+ }
266
+ exports.CppBridge = CppBridge;
@@ -0,0 +1,4 @@
1
+ import { CppBridge, type NativeMoneroLwsfModule } from './CppBridge';
2
+ export declare function makeMonero(): CppBridge;
3
+ export type { CppBridge, NativeMoneroLwsfModule };
4
+ export * from './types';
package/lib/src/index.js CHANGED
@@ -1 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.makeMonero = makeMonero;
18
+ const react_native_1 = require("react-native");
19
+ const CppBridge_1 = require("./CppBridge");
20
+ function makeMonero() {
21
+ const { MoneroLwsfModule } = react_native_1.NativeModules;
22
+ if (MoneroLwsfModule == null) {
23
+ throw new Error('react-native-monero native module not linked');
24
+ }
25
+ return new CppBridge_1.CppBridge(MoneroLwsfModule);
26
+ }
27
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,103 @@
1
+ export type NetworkType = 'MAINNET' | 'TESTNET' | 'STAGENET';
2
+ export declare function networkTypeToIntString(type: NetworkType): string;
3
+ export type WalletBackend = 'lws' | 'monerod';
4
+ export interface GeneratedWallet {
5
+ mnemonic: string;
6
+ secretSpendKey: string;
7
+ publicSpendKey: string;
8
+ }
9
+ /** Return type for seedAndKeysFromMnemonic. */
10
+ export interface DerivedKeys {
11
+ address: string;
12
+ secretViewKey: string;
13
+ publicViewKey: string;
14
+ secretSpendKey: string;
15
+ publicSpendKey: string;
16
+ }
17
+ /** Return type for openWallet and getWalletStatus. */
18
+ export interface WalletStatus {
19
+ syncedHeight: number;
20
+ networkHeight: number;
21
+ balance: string;
22
+ unlockedBalance: string;
23
+ }
24
+ /** Transaction direction. */
25
+ export type TransactionDirection = 0 | 1;
26
+ /** Single transaction info. */
27
+ export interface TransactionInfo {
28
+ hash: string;
29
+ direction: TransactionDirection;
30
+ isPending: boolean;
31
+ isFailed: boolean;
32
+ isCoinbase: boolean;
33
+ amount: string;
34
+ fee: string;
35
+ blockHeight: number;
36
+ confirmations: number;
37
+ timestamp: number;
38
+ paymentId: string;
39
+ description: string;
40
+ label: string;
41
+ unlockTime: number;
42
+ subaddrAccount: number;
43
+ txKey?: string;
44
+ }
45
+ /** Return type for getAllTransactions. */
46
+ export interface TransactionsPage {
47
+ transactions: TransactionInfo[];
48
+ totalCount: number;
49
+ page: number;
50
+ pageSize: number;
51
+ }
52
+ /** Transaction priority levels. */
53
+ export type TransactionPriority = 0 | 1 | 2 | 3;
54
+ /** Recipient for createTransaction. */
55
+ export interface Recipient {
56
+ address: string;
57
+ amount: string;
58
+ }
59
+ /** Return type for createTransaction. */
60
+ export interface SignedTransaction {
61
+ txid: string;
62
+ signedTxHex: string;
63
+ fee: string;
64
+ }
65
+ /** Parsed Monero URI (parseUri result). */
66
+ export interface ParsedUri {
67
+ address: string;
68
+ paymentId: string;
69
+ amount: string;
70
+ txDescription: string;
71
+ recipientName: string;
72
+ unknownParameters: string[];
73
+ }
74
+ /** Params for encodeUri (make monero: URI). */
75
+ export interface EncodeUriParams {
76
+ address: string;
77
+ paymentId?: string;
78
+ amount: string;
79
+ txDescription?: string;
80
+ recipientName?: string;
81
+ }
82
+ /** Wallet event names emitted by the native WalletListener. */
83
+ export type WalletEventName = 'pendingTransactionReceived' | 'nymFetchRequest';
84
+ /** Payload delivered by "MoneroWalletEvent" NativeEventEmitter events. */
85
+ export interface WalletEventData {
86
+ walletId: string;
87
+ eventName: WalletEventName;
88
+ /**
89
+ * JSON string whose shape depends on `eventName`:
90
+ * - pendingTransactionReceived: { txId: string, amount: number }
91
+ * - nymFetchRequest: { url, method, headers, bodyBase64 } — in this
92
+ * case `walletId` holds the nym requestId that must be passed to
93
+ * `resolveFetch` / `rejectFetch`.
94
+ */
95
+ data: string;
96
+ }
97
+ /** Parsed payload for the `nymFetchRequest` wallet event. */
98
+ export interface NymFetchRequestPayload {
99
+ url: string;
100
+ method: string;
101
+ headers: Record<string, string>;
102
+ bodyBase64: string;
103
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.networkTypeToIntString = networkTypeToIntString;
4
+ const networkTypeMap = {
5
+ MAINNET: 0,
6
+ TESTNET: 1,
7
+ STAGENET: 2
8
+ };
9
+ function networkTypeToIntString(type) {
10
+ return networkTypeMap[type]?.toString() ?? '0';
11
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-monero",
3
- "version": "0.0.1",
4
- "description": "React Native bindings for the Monero blockchain",
3
+ "version": "0.2.0",
4
+ "description": "React Native bindings for the monero-lwsf library",
5
5
  "homepage": "https://github.com/EdgeApp/react-native-monero",
6
6
  "repository": {
7
7
  "type": "git",
@@ -18,7 +18,9 @@
18
18
  "/ios/",
19
19
  "/lib/src/",
20
20
  "/package.json",
21
- "/README.md"
21
+ "/react-native-monero.podspec",
22
+ "/README.md",
23
+ "/src/monero-wrapper/monero-methods.hpp"
22
24
  ],
23
25
  "lint-staged": {
24
26
  "*.{js,ts}": "eslint"
@@ -26,7 +28,7 @@
26
28
  "scripts": {
27
29
  "fix": "npm run lint -- --fix",
28
30
  "lint": "eslint .",
29
- "-prepack": "npm run build-native",
31
+ "prepack": "npm run build-native",
30
32
  "prepare": "husky && lint-staged && rimraf lib && tsc",
31
33
  "precommit": "npm run lint",
32
34
  "build-native": "ZERO_AR_DATE=1 node -r sucrase/register ./scripts/build-native.ts"
@@ -44,5 +46,8 @@
44
46
  },
45
47
  "dependencies": {
46
48
  "cleaners": "^0.3.17"
49
+ },
50
+ "peerDependencies": {
51
+ "react-native": ">=0.47.0 <1.0.0"
47
52
  }
48
53
  }
@@ -0,0 +1,28 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = package['name']
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.homepage = package['homepage']
10
+ s.license = package['license']
11
+ s.authors = package['author']
12
+
13
+ s.platform = :ios, "13.0"
14
+ s.requires_arc = true
15
+ s.source = {
16
+ :git => "https://github.com/EdgeApp/react-native-monero.git",
17
+ :tag => "v#{s.version}"
18
+ }
19
+ s.source_files =
20
+ "ios/MoneroModule.h",
21
+ "ios/MoneroModule.mm",
22
+ "src/monero-wrapper/monero-methods.hpp"
23
+ s.vendored_frameworks = "ios/MoneroModule.xcframework"
24
+ s.libraries = "c++"
25
+
26
+ s.dependency "React-Core"
27
+ s.frameworks = 'Security'
28
+ end
@@ -0,0 +1,25 @@
1
+ #ifndef MONERO_METHODS_HPP_INCLUDED
2
+ #define MONERO_METHODS_HPP_INCLUDED
3
+
4
+ #include <string>
5
+ #include <vector>
6
+ #include <functional>
7
+ #include <mutex>
8
+
9
+ struct MoneroMethod {
10
+ const char *name;
11
+ int argc;
12
+ std::string (*method)(const std::vector<std::string> &args);
13
+ };
14
+ extern const MoneroMethod moneroMethods[];
15
+ extern const unsigned moneroMethodCount;
16
+
17
+ // Callback type for wallet events (walletId, eventName, jsonPayload)
18
+ using WalletEventCallback = std::function<void(
19
+ const std::string&, const std::string&, const std::string&)>;
20
+
21
+ // Set the global callback invoked by WalletListener on the SDK refresh thread.
22
+ // Thread-safe: the callback is guarded by a mutex.
23
+ void moneroSetEventCallback(WalletEventCallback cb);
24
+
25
+ #endif