@novastera-oss/nitro-metamask 0.6.3 → 0.7.2

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 (127) hide show
  1. package/NitroMetamask.podspec +12 -3
  2. package/README.md +3 -1
  3. package/android/build.gradle +14 -32
  4. package/android/cargo-ecies.gradle +60 -88
  5. package/android/src/main/aidl/io/metamask/nativesdk/IMessegeService.aidl +8 -0
  6. package/android/src/main/aidl/io/metamask/nativesdk/IMessegeServiceCallback.aidl +8 -0
  7. package/android/src/main/java/com/margelo/nitro/nitrometamask/HybridNitroMetamask.kt +101 -3
  8. package/android/src/main/java/io/metamask/androidsdk/AnyRequest.kt +8 -0
  9. package/android/src/main/java/io/metamask/androidsdk/ClientMessageServiceCallback.kt +12 -0
  10. package/android/src/main/java/io/metamask/androidsdk/ClientServiceConnection.kt +42 -0
  11. package/android/src/main/java/io/metamask/androidsdk/CommunicationClient.kt +525 -0
  12. package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModule.kt +47 -0
  13. package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModuleInterface.kt +11 -0
  14. package/android/src/main/java/io/metamask/androidsdk/Constants.kt +5 -0
  15. package/android/src/main/java/io/metamask/androidsdk/Crypto.kt +35 -0
  16. package/android/src/main/java/io/metamask/androidsdk/DappMetadata.kt +36 -0
  17. package/android/src/main/java/io/metamask/androidsdk/Encryption.kt +9 -0
  18. package/android/src/main/java/io/metamask/androidsdk/ErrorType.kt +41 -0
  19. package/android/src/main/java/io/metamask/androidsdk/Ethereum.kt +328 -0
  20. package/android/src/main/java/io/metamask/androidsdk/EthereumEventCallback.kt +6 -0
  21. package/android/src/main/java/io/metamask/androidsdk/EthereumMethod.kt +80 -0
  22. package/android/src/main/java/io/metamask/androidsdk/EthereumRequest.kt +7 -0
  23. package/android/src/main/java/io/metamask/androidsdk/EthereumState.kt +7 -0
  24. package/android/src/main/java/io/metamask/androidsdk/KeyExchange.kt +77 -0
  25. package/android/src/main/java/io/metamask/androidsdk/KeyExchangeMessageType.kt +20 -0
  26. package/android/src/main/java/io/metamask/androidsdk/KeyStorage.kt +122 -0
  27. package/android/src/main/java/io/metamask/androidsdk/Logger.kt +18 -0
  28. package/android/src/main/java/io/metamask/androidsdk/Message.kt +3 -0
  29. package/android/src/main/java/io/metamask/androidsdk/MessageType.kt +11 -0
  30. package/android/src/main/java/io/metamask/androidsdk/OriginatorInfo.kt +12 -0
  31. package/android/src/main/java/io/metamask/androidsdk/RequestError.kt +8 -0
  32. package/android/src/main/java/io/metamask/androidsdk/RequestInfo.kt +9 -0
  33. package/android/src/main/java/io/metamask/androidsdk/Result.kt +11 -0
  34. package/android/src/main/java/io/metamask/androidsdk/RpcRequest.kt +7 -0
  35. package/android/src/main/java/io/metamask/androidsdk/SDKInfo.kt +6 -0
  36. package/android/src/main/java/io/metamask/androidsdk/SDKOptions.kt +6 -0
  37. package/android/src/main/java/io/metamask/androidsdk/SecureStorage.kt +9 -0
  38. package/android/src/main/java/io/metamask/androidsdk/SessionConfig.kt +10 -0
  39. package/android/src/main/java/io/metamask/androidsdk/SessionManager.kt +92 -0
  40. package/android/src/main/java/io/metamask/androidsdk/SubmittedRequest.kt +8 -0
  41. package/android/src/main/java/io/metamask/androidsdk/TimeStampGenerator.kt +7 -0
  42. package/android/src/main/jniLibs/arm64-v8a/libecies.so +0 -0
  43. package/android/src/main/jniLibs/armeabi-v7a/libecies.so +0 -0
  44. package/android/src/main/jniLibs/x86/libecies.so +0 -0
  45. package/android/src/main/jniLibs/x86_64/libecies.so +0 -0
  46. package/android/src/test/java/com/margelo/nitro/nitrometamask/CancellationStateMachineTest.kt +128 -0
  47. package/android/src/test/java/com/margelo/nitro/nitrometamask/ChainIdParsingTest.kt +65 -0
  48. package/android/src/test/java/com/margelo/nitro/nitrometamask/ConfigureStateMachineTest.kt +140 -0
  49. package/android/src/test/java/com/margelo/nitro/nitrometamask/ConnectSignJsonTest.kt +76 -0
  50. package/android/src/test/java/com/margelo/nitro/nitrometamask/MetaMaskInstallationCheckTest.kt +42 -0
  51. package/android/src/test/java/com/margelo/nitro/nitrometamask/PersonalSignParamsTest.kt +75 -0
  52. package/ios/Frameworks/Ecies.xcframework/Info.plist +47 -0
  53. package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/ecies.h +20 -0
  54. package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/module.modulemap +4 -0
  55. package/ios/Frameworks/Ecies.xcframework/ios-arm64/libecies.a +0 -0
  56. package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/ecies.h +20 -0
  57. package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/module.modulemap +4 -0
  58. package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/libecies.a +0 -0
  59. package/ios/HybridNitroMetamask.swift +119 -54
  60. package/ios/NitroMetamaskTests/CancellationStateMachineTests.swift +150 -0
  61. package/ios/NitroMetamaskTests/ChainIdParsingTests.swift +117 -0
  62. package/ios/NitroMetamaskTests/ConfigureStateMachineTests.swift +174 -0
  63. package/ios/NitroMetamaskTests/ConnectSignJsonTests.swift +168 -0
  64. package/ios/NitroMetamaskTests/DefaultDappUrlTests.swift +80 -0
  65. package/ios/NitroMetamaskTests/PersonalSignParamsTests.swift +101 -0
  66. package/ios/metamask-ios-sdk/CommunicationLayer/CommClient.swift +43 -0
  67. package/ios/metamask-ios-sdk/CommunicationLayer/CommClientFactory.swift +17 -0
  68. package/ios/metamask-ios-sdk/CommunicationLayer/CommLayer.swift +36 -0
  69. package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/Deeplink.swift +26 -0
  70. package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkClient.swift +199 -0
  71. package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkManager.swift +83 -0
  72. package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/String.swift +48 -0
  73. package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/URLOpener.swift +19 -0
  74. package/ios/metamask-ios-sdk/CommunicationLayer/SocketClient.swift +27 -0
  75. package/ios/metamask-ios-sdk/Crypto/Crypto.swift +72 -0
  76. package/ios/metamask-ios-sdk/Crypto/Encoding.swift +15 -0
  77. package/ios/metamask-ios-sdk/Crypto/KeyExchange.swift +236 -0
  78. package/ios/metamask-ios-sdk/DeviceInfo/DeviceInfo.swift +11 -0
  79. package/ios/metamask-ios-sdk/Ethereum/AppMetadata.swift +28 -0
  80. package/ios/metamask-ios-sdk/Ethereum/ErrorType.swift +62 -0
  81. package/ios/metamask-ios-sdk/Ethereum/Ethereum.swift +810 -0
  82. package/ios/metamask-ios-sdk/Ethereum/EthereumMethod.swift +111 -0
  83. package/ios/metamask-ios-sdk/Ethereum/EthereumRequest.swift +40 -0
  84. package/ios/metamask-ios-sdk/Ethereum/EthereumWrapper.swift +10 -0
  85. package/ios/metamask-ios-sdk/Ethereum/RPCRequest.swift +14 -0
  86. package/ios/metamask-ios-sdk/Ethereum/RequestError.swift +88 -0
  87. package/ios/metamask-ios-sdk/Ethereum/ResponseMethod.swift +22 -0
  88. package/ios/metamask-ios-sdk/Ethereum/SubmitRequest.swift +26 -0
  89. package/ios/metamask-ios-sdk/Ethereum/TimestampGenerator.swift +16 -0
  90. package/ios/metamask-ios-sdk/Extensions/NSRecursiveLock.swift +14 -0
  91. package/ios/metamask-ios-sdk/Extensions/Notification.swift +10 -0
  92. package/ios/metamask-ios-sdk/Logger/Logging.swift +27 -0
  93. package/ios/metamask-ios-sdk/Models/AddChainParameters.swift +35 -0
  94. package/ios/metamask-ios-sdk/Models/Event.swift +19 -0
  95. package/ios/metamask-ios-sdk/Models/Mappable.swift +40 -0
  96. package/ios/metamask-ios-sdk/Models/NativeCurrency.swift +25 -0
  97. package/ios/metamask-ios-sdk/Models/OriginatorInfo.swift +26 -0
  98. package/ios/metamask-ios-sdk/Models/RequestInfo.swift +18 -0
  99. package/ios/metamask-ios-sdk/Models/SignContract.swift +48 -0
  100. package/ios/metamask-ios-sdk/Models/Typealiases.swift +9 -0
  101. package/ios/metamask-ios-sdk/Persistence/SecureStore.swift +134 -0
  102. package/ios/metamask-ios-sdk/Persistence/SessionConfig.swift +24 -0
  103. package/ios/metamask-ios-sdk/Persistence/SessionManager.swift +56 -0
  104. package/ios/metamask-ios-sdk/SDK/Dependencies.swift +35 -0
  105. package/ios/metamask-ios-sdk/SDK/MetaMaskSDK.swift +215 -0
  106. package/ios/metamask-ios-sdk/SDK/SDKInfo.swift +37 -0
  107. package/ios/metamask-ios-sdk/SDK/SDKOptions.swift +16 -0
  108. package/lib/commonjs/index.js +50 -3
  109. package/lib/commonjs/index.js.map +1 -1
  110. package/lib/module/index.js +49 -3
  111. package/lib/module/index.js.map +1 -1
  112. package/lib/typescript/src/__tests__/parseNitroError.test.d.ts +2 -0
  113. package/lib/typescript/src/__tests__/parseNitroError.test.d.ts.map +1 -0
  114. package/lib/typescript/src/index.d.ts +43 -3
  115. package/lib/typescript/src/index.d.ts.map +1 -1
  116. package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts +29 -1
  117. package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts.map +1 -1
  118. package/package.json +21 -12
  119. package/react-native.config.js +5 -0
  120. package/rust/ecies-jni/Cargo.lock +50 -86
  121. package/rust/ecies-jni/Cargo.toml +1 -1
  122. package/rust/ecies-jni/src/lib.rs +164 -100
  123. package/src/__tests__/parseNitroError.test.ts +35 -0
  124. package/src/index.ts +53 -5
  125. package/src/specs/nitro-metamask.nitro.ts +29 -1
  126. package/scripts/verify-16k-page-alignment.py +0 -117
  127. package/scripts/verify-16k-page-alignment.sh +0 -5
@@ -1,127 +1,191 @@
1
1
  //! JNI bridge for `io.metamask.ecies.Ecies` (MetaMask Android SDK).
2
2
  //! Rebuilt with NDK r28+ / 16 KB page-size compatible linking (see Gradle + cargo-ndk).
3
+ //!
4
+ //! Symbol naming: Kotlin `@JvmStatic` methods in a `companion object` are exposed
5
+ //! directly on the enclosing class by the JVM, so the JNI symbol is
6
+ //! `Java_io_metamask_ecies_Ecies_<method>` — NOT `..._00024Companion_<method>`.
7
+ //! Verified against upstream libecies.so exports via `nm -D`.
3
8
 
4
9
  use ecies::{decrypt, encrypt, utils::generate_keypair};
10
+ use jni::errors::Result as JniResult;
11
+ use jni::errors::ThrowRuntimeExAndDefault;
12
+ use jni::objects::JClass;
5
13
  use jni::objects::JString;
6
14
  use jni::sys::jstring;
7
- use jni::JNIEnv;
15
+ use jni::{Env, EnvUnowned};
8
16
 
9
- fn empty_jstring(env: &mut JNIEnv) -> jstring {
10
- env.new_string("")
11
- .expect("empty jstring")
12
- .into_raw()
17
+ fn empty_jstring<'local>(env: &mut Env<'local>) -> jstring {
18
+ env.new_string("").expect("empty jstring").into_raw()
13
19
  }
14
20
 
15
- fn jstring_to_string(env: &mut JNIEnv, j: JString) -> Result<String, jni::errors::Error> {
16
- Ok(env.get_string(&j)?.into())
21
+ fn jstring_to_string<'local>(
22
+ _env: &mut Env<'local>,
23
+ j: JString<'local>,
24
+ ) -> Result<String, jni::errors::Error> {
25
+ Ok(j.to_string())
17
26
  }
18
27
 
19
- /// Kotlin: `Ecies.companion.generateSecretKey`
28
+ /// Kotlin: `Ecies.generateSecretKey()` (@JvmStatic in companion object)
20
29
  #[no_mangle]
21
- pub extern "system" fn Java_io_metamask_ecies_Ecies_00024Companion_generateSecretKey(
22
- mut env: JNIEnv,
23
- _class: jni::objects::JClass,
30
+ pub extern "system" fn Java_io_metamask_ecies_Ecies_generateSecretKey<'local>(
31
+ mut unowned_env: EnvUnowned<'local>,
32
+ _class: JClass<'local>,
24
33
  ) -> jstring {
25
- let (sk, _pk) = generate_keypair();
26
- let hex = hex::encode(sk.serialize());
27
- match env.new_string(hex) {
28
- Ok(s) => s.into_raw(),
29
- Err(_) => empty_jstring(&mut env),
30
- }
34
+ unowned_env
35
+ .with_env(|mut env| -> JniResult<jstring> {
36
+ let (sk, _pk) = generate_keypair();
37
+ let hex = hex::encode(sk.serialize());
38
+ match env.new_string(hex) {
39
+ Ok(s) => Ok(s.into_raw()),
40
+ Err(_) => Ok(empty_jstring(&mut env)),
41
+ }
42
+ })
43
+ .resolve::<ThrowRuntimeExAndDefault>()
31
44
  }
32
45
 
33
- /// Kotlin: `Ecies.companion.derivePublicKeyFrom`
46
+ /// Kotlin: `Ecies.derivePublicKeyFrom(secret)` (@JvmStatic in companion object)
34
47
  #[no_mangle]
35
- pub extern "system" fn Java_io_metamask_ecies_Ecies_00024Companion_derivePublicKeyFrom(
36
- mut env: JNIEnv,
37
- _class: jni::objects::JClass,
38
- secret: JString,
48
+ pub extern "system" fn Java_io_metamask_ecies_Ecies_derivePublicKeyFrom<'local>(
49
+ mut unowned_env: EnvUnowned<'local>,
50
+ _class: JClass<'local>,
51
+ secret: JString<'local>,
39
52
  ) -> jstring {
40
- let secret_str = match jstring_to_string(&mut env, secret) {
41
- Ok(s) => s,
42
- Err(_) => return empty_jstring(&mut env),
43
- };
44
- let sk_bytes = match hex::decode(secret_str.trim()) {
45
- Ok(b) => b,
46
- Err(_) => return empty_jstring(&mut env),
47
- };
48
- let sk = match ecies::SecretKey::parse_slice(&sk_bytes) {
49
- Ok(k) => k,
50
- Err(_) => return empty_jstring(&mut env),
51
- };
52
- let pk = ecies::PublicKey::from_secret_key(&sk);
53
- let hex = hex::encode(pk.serialize());
54
- match env.new_string(hex) {
55
- Ok(s) => s.into_raw(),
56
- Err(_) => empty_jstring(&mut env),
57
- }
53
+ unowned_env
54
+ .with_env(|mut env| -> JniResult<jstring> {
55
+ let secret_str = match jstring_to_string(&mut env, secret) {
56
+ Ok(s) => s,
57
+ Err(_) => return Ok(empty_jstring(&mut env)),
58
+ };
59
+ let sk_bytes = match hex::decode(secret_str.trim()) {
60
+ Ok(b) => b,
61
+ Err(_) => return Ok(empty_jstring(&mut env)),
62
+ };
63
+ let sk = match ecies::SecretKey::parse_slice(&sk_bytes) {
64
+ Ok(k) => k,
65
+ Err(_) => return Ok(empty_jstring(&mut env)),
66
+ };
67
+ let pk = ecies::PublicKey::from_secret_key(&sk);
68
+ let hex = hex::encode(pk.serialize());
69
+ match env.new_string(hex) {
70
+ Ok(s) => Ok(s.into_raw()),
71
+ Err(_) => Ok(empty_jstring(&mut env)),
72
+ }
73
+ })
74
+ .resolve::<ThrowRuntimeExAndDefault>()
58
75
  }
59
76
 
60
- /// Kotlin: `Ecies.companion.encryptMessage`
77
+ /// Kotlin: `Ecies.encryptMessage(public, message)` (@JvmStatic in companion object)
61
78
  #[no_mangle]
62
- pub extern "system" fn Java_io_metamask_ecies_Ecies_00024Companion_encryptMessage(
63
- mut env: JNIEnv,
64
- _class: jni::objects::JClass,
65
- public: JString,
66
- message: JString,
79
+ pub extern "system" fn Java_io_metamask_ecies_Ecies_encryptMessage<'local>(
80
+ mut unowned_env: EnvUnowned<'local>,
81
+ _class: JClass<'local>,
82
+ public: JString<'local>,
83
+ message: JString<'local>,
67
84
  ) -> jstring {
68
- let pub_hex = match jstring_to_string(&mut env, public) {
69
- Ok(s) => s,
70
- Err(_) => return empty_jstring(&mut env),
71
- };
72
- let msg_str = match jstring_to_string(&mut env, message) {
73
- Ok(s) => s,
74
- Err(_) => return empty_jstring(&mut env),
75
- };
76
- let pk_bytes = match hex::decode(pub_hex.trim()) {
77
- Ok(b) => b,
78
- Err(_) => return empty_jstring(&mut env),
79
- };
80
- let ct = match encrypt(&pk_bytes, msg_str.as_bytes()) {
81
- Ok(c) => c,
82
- Err(_) => return empty_jstring(&mut env),
83
- };
84
- let hex = hex::encode(ct);
85
- match env.new_string(hex) {
86
- Ok(s) => s.into_raw(),
87
- Err(_) => empty_jstring(&mut env),
88
- }
85
+ unowned_env
86
+ .with_env(|mut env| -> JniResult<jstring> {
87
+ let pub_hex = match jstring_to_string(&mut env, public) {
88
+ Ok(s) => s,
89
+ Err(_) => return Ok(empty_jstring(&mut env)),
90
+ };
91
+ let msg_str = match jstring_to_string(&mut env, message) {
92
+ Ok(s) => s,
93
+ Err(_) => return Ok(empty_jstring(&mut env)),
94
+ };
95
+ let pk_bytes = match hex::decode(pub_hex.trim()) {
96
+ Ok(b) => b,
97
+ Err(_) => return Ok(empty_jstring(&mut env)),
98
+ };
99
+ let ct = match encrypt(&pk_bytes, msg_str.as_bytes()) {
100
+ Ok(c) => c,
101
+ Err(_) => return Ok(empty_jstring(&mut env)),
102
+ };
103
+ let hex = hex::encode(ct);
104
+ match env.new_string(hex) {
105
+ Ok(s) => Ok(s.into_raw()),
106
+ Err(_) => Ok(empty_jstring(&mut env)),
107
+ }
108
+ })
109
+ .resolve::<ThrowRuntimeExAndDefault>()
89
110
  }
90
111
 
91
- /// Kotlin: `Ecies.companion.decryptMessage`
112
+ /// Kotlin: `Ecies.decryptMessage(secret, message)` (@JvmStatic in companion object)
92
113
  #[no_mangle]
93
- pub extern "system" fn Java_io_metamask_ecies_Ecies_00024Companion_decryptMessage(
94
- mut env: JNIEnv,
95
- _class: jni::objects::JClass,
96
- secret: JString,
97
- message: JString,
114
+ pub extern "system" fn Java_io_metamask_ecies_Ecies_decryptMessage<'local>(
115
+ mut unowned_env: EnvUnowned<'local>,
116
+ _class: JClass<'local>,
117
+ secret: JString<'local>,
118
+ message: JString<'local>,
98
119
  ) -> jstring {
99
- let sec_hex = match jstring_to_string(&mut env, secret) {
100
- Ok(s) => s,
101
- Err(_) => return empty_jstring(&mut env),
102
- };
103
- let ct_hex = match jstring_to_string(&mut env, message) {
104
- Ok(s) => s,
105
- Err(_) => return empty_jstring(&mut env),
106
- };
107
- let sk_bytes = match hex::decode(sec_hex.trim()) {
108
- Ok(b) => b,
109
- Err(_) => return empty_jstring(&mut env),
110
- };
111
- let ct_bytes = match hex::decode(ct_hex.trim()) {
112
- Ok(b) => b,
113
- Err(_) => return empty_jstring(&mut env),
114
- };
115
- let plain = match decrypt(&sk_bytes, &ct_bytes) {
116
- Ok(p) => p,
117
- Err(_) => return empty_jstring(&mut env),
118
- };
119
- let text = match String::from_utf8(plain) {
120
- Ok(t) => t,
121
- Err(_) => return empty_jstring(&mut env),
122
- };
123
- match env.new_string(text) {
124
- Ok(s) => s.into_raw(),
125
- Err(_) => empty_jstring(&mut env),
120
+ unowned_env
121
+ .with_env(|mut env| -> JniResult<jstring> {
122
+ let sec_hex = match jstring_to_string(&mut env, secret) {
123
+ Ok(s) => s,
124
+ Err(_) => return Ok(empty_jstring(&mut env)),
125
+ };
126
+ let ct_hex = match jstring_to_string(&mut env, message) {
127
+ Ok(s) => s,
128
+ Err(_) => return Ok(empty_jstring(&mut env)),
129
+ };
130
+ let sk_bytes = match hex::decode(sec_hex.trim()) {
131
+ Ok(b) => b,
132
+ Err(_) => return Ok(empty_jstring(&mut env)),
133
+ };
134
+ let ct_bytes = match hex::decode(ct_hex.trim()) {
135
+ Ok(b) => b,
136
+ Err(_) => return Ok(empty_jstring(&mut env)),
137
+ };
138
+ let plain = match decrypt(&sk_bytes, &ct_bytes) {
139
+ Ok(p) => p,
140
+ Err(_) => return Ok(empty_jstring(&mut env)),
141
+ };
142
+ let text = match String::from_utf8(plain) {
143
+ Ok(t) => t,
144
+ Err(_) => return Ok(empty_jstring(&mut env)),
145
+ };
146
+ match env.new_string(text) {
147
+ Ok(s) => Ok(s.into_raw()),
148
+ Err(_) => Ok(empty_jstring(&mut env)),
149
+ }
150
+ })
151
+ .resolve::<ThrowRuntimeExAndDefault>()
152
+ }
153
+
154
+ #[cfg(test)]
155
+ mod tests {
156
+ macro_rules! assert_jni_symbol {
157
+ ($fn_name:ident) => {{
158
+ let name = stringify!($fn_name);
159
+ assert!(
160
+ !name.contains("_00024Companion_"),
161
+ "Symbol `{}` must NOT contain `_00024Companion_`",
162
+ name
163
+ );
164
+ assert!(
165
+ name.starts_with("Java_io_metamask_ecies_Ecies_"),
166
+ "Symbol `{}` must start with `Java_io_metamask_ecies_Ecies_`",
167
+ name
168
+ );
169
+ }};
170
+ }
171
+
172
+ #[test]
173
+ fn generate_secret_key_symbol_has_no_companion_mangling() {
174
+ assert_jni_symbol!(Java_io_metamask_ecies_Ecies_generateSecretKey);
175
+ }
176
+
177
+ #[test]
178
+ fn derive_public_key_from_symbol_has_no_companion_mangling() {
179
+ assert_jni_symbol!(Java_io_metamask_ecies_Ecies_derivePublicKeyFrom);
180
+ }
181
+
182
+ #[test]
183
+ fn encrypt_message_symbol_has_no_companion_mangling() {
184
+ assert_jni_symbol!(Java_io_metamask_ecies_Ecies_encryptMessage);
185
+ }
186
+
187
+ #[test]
188
+ fn decrypt_message_symbol_has_no_companion_mangling() {
189
+ assert_jni_symbol!(Java_io_metamask_ecies_Ecies_decryptMessage);
126
190
  }
127
191
  }
@@ -0,0 +1,35 @@
1
+ import { parseNitroError } from '../index'
2
+
3
+ // Mock the NitroModules dependency so the module can be imported in a Node/Jest environment
4
+ jest.mock('react-native-nitro-modules', () => ({
5
+ NitroModules: {
6
+ createHybridObject: () => ({}),
7
+ },
8
+ }))
9
+
10
+ describe('parseNitroError', () => {
11
+ it('parses "[2] MetaMask is not installed" into code 2 and the message', () => {
12
+ const result = parseNitroError(new Error('[2] MetaMask is not installed'))
13
+ expect(result).toEqual({ code: 2, message: 'MetaMask is not installed' })
14
+ })
15
+
16
+ it('returns null for a plain error message with no [code] prefix', () => {
17
+ const result = parseNitroError(new Error('Something went wrong'))
18
+ expect(result).toBeNull()
19
+ })
20
+
21
+ it('parses "[0] some error" into code 0 and the message', () => {
22
+ const result = parseNitroError(new Error('[0] some error'))
23
+ expect(result).toEqual({ code: 0, message: 'some error' })
24
+ })
25
+
26
+ it('parses a string input with [code] prefix', () => {
27
+ const result = parseNitroError('[42] timeout')
28
+ expect(result).toEqual({ code: 42, message: 'timeout' })
29
+ })
30
+
31
+ it('returns null for a non-string, non-Error input', () => {
32
+ const result = parseNitroError(null)
33
+ expect(result).toBeNull()
34
+ })
35
+ })
package/src/index.ts CHANGED
@@ -1,17 +1,65 @@
1
1
  import { NitroModules } from 'react-native-nitro-modules'
2
- import type { NitroMetamask as NitroMetamaskSpec, ConnectResult } from './specs/nitro-metamask.nitro'
2
+ import type {
3
+ NitroMetamask as NitroMetamaskSpec,
4
+ ConnectResult,
5
+ ConnectSignResult,
6
+ } from './specs/nitro-metamask.nitro'
3
7
 
4
8
  /**
5
9
  * NitroMetamask - MetaMask connector for React Native
6
- *
10
+ *
11
+ * Provides native MetaMask wallet integration via Nitro modules.
12
+ *
7
13
  * @example
8
14
  * ```ts
9
15
  * import { NitroMetamask } from '@novastera-oss/nitro-metamask'
10
- *
11
- * const result = await NitroMetamask.connect()
16
+ *
17
+ * // Optional: configure before use
18
+ * NitroMetamask.configure('https://yourdomain.com', 'yourscheme')
19
+ *
20
+ * // Connect wallet
21
+ * const { address, chainId } = await NitroMetamask.connect()
22
+ *
23
+ * // Sign a message
12
24
  * const signature = await NitroMetamask.signMessage('Hello')
25
+ *
26
+ * // Connect and sign in one step (SIWE)
27
+ * const result = await NitroMetamask.connectSign('my-nonce', BigInt(Date.now()))
13
28
  * ```
14
29
  */
15
30
  export const NitroMetamask = NitroModules.createHybridObject<NitroMetamaskSpec>('NitroMetamask')
16
31
 
17
- export type { ConnectResult, NitroMetamaskSpec }
32
+ export type { ConnectResult, ConnectSignResult, NitroMetamaskSpec }
33
+
34
+ /**
35
+ * Parses a structured Nitro error message that contains a numeric error code prefix.
36
+ *
37
+ * Nitro errors from the native layer are formatted as `"[<code>] <message>"`.
38
+ * This utility extracts the code and message so the JS layer can handle them
39
+ * programmatically (e.g., detecting `code: 2` for MetaMask not installed).
40
+ *
41
+ * @param e - The error to parse. Can be an `Error`, a string, or any value.
42
+ * @returns An object `{ code: number; message: string }` if the error message
43
+ * matches the `[code] message` pattern, or `null` if it does not.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { parseNitroError } from '@novastera-oss/nitro-metamask'
48
+ *
49
+ * try {
50
+ * await NitroMetamask.connect()
51
+ * } catch (e) {
52
+ * const parsed = parseNitroError(e)
53
+ * if (parsed?.code === 2) {
54
+ * // MetaMask is not installed — redirect user to App Store / Play Store
55
+ * }
56
+ * }
57
+ * ```
58
+ */
59
+ export function parseNitroError(e: unknown): { code: number; message: string } | null {
60
+ const raw = e instanceof Error ? e.message : typeof e === 'string' ? e : null
61
+ if (raw === null) return null
62
+ const match = raw.match(/^\[(\d+)\]\s*(.*)/)
63
+ if (!match) return null
64
+ return { code: Number(match[1]), message: match[2] ?? '' }
65
+ }
@@ -23,14 +23,42 @@ export interface NitroMetamask extends HybridObject<{ ios: 'swift', android: 'ko
23
23
  * This is used to return to your app after MetaMask operations.
24
24
  */
25
25
  configure(dappUrl?: string, deepLinkScheme?: string): void
26
+ /**
27
+ * Connect to the user's MetaMask wallet.
28
+ *
29
+ * Opens the MetaMask app via deep link to request wallet connection.
30
+ * If MetaMask is not installed, the promise rejects with `code: 2`.
31
+ *
32
+ * @returns Promise resolving to a `ConnectResult` containing the wallet
33
+ * `address` and `chainId`.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const { address, chainId } = await NitroMetamask.connect()
38
+ * ```
39
+ */
26
40
  connect(): Promise<ConnectResult>
41
+ /**
42
+ * Request a `personal_sign` signature from the connected wallet.
43
+ *
44
+ * Must be called after a successful `connect()`. Opens MetaMask to prompt
45
+ * the user to sign the provided message.
46
+ *
47
+ * @param message - The plain-text message to sign.
48
+ * @returns Promise resolving to the hex-encoded signature string.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const signature = await NitroMetamask.signMessage('Sign in to MyApp')
53
+ * ```
54
+ */
27
55
  signMessage(message: string): Promise<string>
28
56
  /**
29
57
  * Connect to MetaMask (if not already connected) and sign a message containing nonce and expiration.
30
58
  * Returns the signature along with the address and chainId that were used to sign.
31
59
  *
32
60
  * @param nonce - A unique nonce for this signing request
33
- * @param exp - Expiration timestamp (as bigint)
61
+ * @param exp - Expiration timestamp (as Int64)
34
62
  * @returns Promise resolving to ConnectSignResult containing signature, address, and chainId
35
63
  */
36
64
  connectSign(nonce: string, exp: Int64): Promise<ConnectSignResult>
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Verify 16 KB ELF program-header alignment for Google Play (PT_LOAD p_align).
4
-
5
- Uses NDK llvm-readobj (reliable). readelf(1) column positions vary by version;
6
- do not parse readelf -l by $NF.
7
- """
8
- from __future__ import annotations
9
-
10
- import os
11
- import re
12
- import shutil
13
- import subprocess
14
- import sys
15
-
16
- MIN_ALIGN = 16 * 1024
17
-
18
-
19
- def find_llvm_readobj() -> str | None:
20
- env = os.environ.get("ANDROID_NDK_HOME") or os.environ.get("ANDROID_NDK_ROOT")
21
- if env:
22
- prebuilt = os.path.join(env, "toolchains", "llvm", "prebuilt")
23
- if os.path.isdir(prebuilt):
24
- for host in (
25
- "linux-x86_64",
26
- "darwin-x86_64",
27
- "darwin-arm64",
28
- "windows-x86_64",
29
- ):
30
- cand = os.path.join(prebuilt, host, "bin", "llvm-readobj")
31
- if os.path.isfile(cand):
32
- return cand
33
- sdk = os.environ.get("ANDROID_SDK_ROOT") or os.environ.get("ANDROID_HOME")
34
- if sdk:
35
- for ver in ("28.2.13676358",):
36
- for host in ("linux-x86_64", "windows-x86_64", "darwin-x86_64"):
37
- cand = os.path.join(
38
- sdk, "ndk", ver, "toolchains", "llvm", "prebuilt", host, "bin", "llvm-readobj"
39
- )
40
- if os.path.isfile(cand):
41
- return cand
42
- return shutil.which("llvm-readobj")
43
-
44
-
45
- def verify_so(llvm_readobj: str, path: str) -> bool:
46
- try:
47
- out = subprocess.check_output(
48
- [llvm_readobj, "--program-headers", path],
49
- text=True,
50
- stderr=subprocess.DEVNULL,
51
- )
52
- except (subprocess.CalledProcessError, FileNotFoundError) as e:
53
- print(f"ERROR: llvm-readobj failed for {path}: {e}", file=sys.stderr)
54
- return False
55
-
56
- saw_nonzero = False
57
- # Each "ProgramHeader {" block: match PT_LOAD and its Alignment (not readelf columns).
58
- for block in out.split("ProgramHeader {")[1:]:
59
- if "Type: PT_LOAD" not in block:
60
- continue
61
- m = re.search(r"Alignment:\s*(\d+)", block)
62
- if not m:
63
- continue
64
- a = int(m.group(1))
65
- if a == 0:
66
- # p_align 0 = default; other LOADs carry explicit alignment on 16K builds
67
- continue
68
- saw_nonzero = True
69
- if a < MIN_ALIGN:
70
- print(
71
- f"FAIL {path}: PT_LOAD Alignment {a} < {MIN_ALIGN}",
72
- file=sys.stderr,
73
- )
74
- return False
75
-
76
- if not saw_nonzero:
77
- print(
78
- f"FAIL {path}: no PT_LOAD with non-zero Alignment (unexpected for .so)",
79
- file=sys.stderr,
80
- )
81
- return False
82
- return True
83
-
84
-
85
- def main() -> int:
86
- if len(sys.argv) < 2:
87
- print("Usage: verify-16k-page-alignment.py <shared-object> [...]", file=sys.stderr)
88
- return 2
89
-
90
- lr = find_llvm_readobj()
91
- if not lr:
92
- print(
93
- "ERROR: llvm-readobj not found. Set ANDROID_NDK_HOME or install Android NDK.",
94
- file=sys.stderr,
95
- )
96
- return 2
97
-
98
- ok = True
99
- for path in sys.argv[1:]:
100
- if not os.path.isfile(path):
101
- print(f"ERROR: not a file: {path}", file=sys.stderr)
102
- return 1
103
- print(f"Checking: {path}")
104
- if not verify_so(lr, path):
105
- ok = False
106
- else:
107
- print(f" OK (llvm-readobj PT_LOAD Alignment >= {MIN_ALIGN} where specified)")
108
-
109
- if not ok:
110
- print("verify-16k-page-alignment: FAILED", file=sys.stderr)
111
- return 1
112
- print("verify-16k-page-alignment: all checks passed")
113
- return 0
114
-
115
-
116
- if __name__ == "__main__":
117
- sys.exit(main())
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Thin wrapper; real check is scripts/verify-16k-page-alignment.py (llvm-readobj).
3
- set -euo pipefail
4
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
- exec python3 "$ROOT/scripts/verify-16k-page-alignment.py" "$@"