@novastera-oss/nitro-metamask 0.4.2 → 0.4.3
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/app.plugin.js +5 -5
- package/ios/HybridNitroMetamask.swift +152 -38
- package/package.json +1 -1
package/app.plugin.js
CHANGED
|
@@ -7,12 +7,12 @@ const withMetamaskAppDelegate = (config) => {
|
|
|
7
7
|
// Check if AppDelegate is Swift
|
|
8
8
|
if (modResults.language === 'swift') {
|
|
9
9
|
// Check if the method already exists
|
|
10
|
-
if (modResults.contents.includes('MetaMaskSDK.
|
|
10
|
+
if (modResults.contents.includes('MetaMaskSDK.sharedInstance?.handleUrl')) {
|
|
11
11
|
return config;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
// Add import if not present
|
|
15
|
-
if (!modResults.contents.includes('import
|
|
15
|
+
if (!modResults.contents.includes('import metamask_ios_sdk')) {
|
|
16
16
|
// Find the last import statement and add after it
|
|
17
17
|
const importRegex = /^import\s+.*$/gm;
|
|
18
18
|
const imports = modResults.contents.match(importRegex);
|
|
@@ -21,14 +21,14 @@ const withMetamaskAppDelegate = (config) => {
|
|
|
21
21
|
const lastImportIndex = modResults.contents.lastIndexOf(lastImport);
|
|
22
22
|
modResults.contents =
|
|
23
23
|
modResults.contents.slice(0, lastImportIndex + lastImport.length) +
|
|
24
|
-
'\nimport
|
|
24
|
+
'\nimport metamask_ios_sdk' +
|
|
25
25
|
modResults.contents.slice(lastImportIndex + lastImport.length);
|
|
26
26
|
} else {
|
|
27
27
|
// No imports found, add at the top after the first line
|
|
28
28
|
const firstLineIndex = modResults.contents.indexOf('\n');
|
|
29
29
|
modResults.contents =
|
|
30
30
|
modResults.contents.slice(0, firstLineIndex + 1) +
|
|
31
|
-
'import
|
|
31
|
+
'import metamask_ios_sdk\n' +
|
|
32
32
|
modResults.contents.slice(firstLineIndex + 1);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -47,7 +47,7 @@ const withMetamaskAppDelegate = (config) => {
|
|
|
47
47
|
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
|
48
48
|
components.host == "mmsdk" {
|
|
49
49
|
// Handle MetaMask deep link return
|
|
50
|
-
MetaMaskSDK.
|
|
50
|
+
MetaMaskSDK.sharedInstance?.handleUrl(url)
|
|
51
51
|
return true
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -1,36 +1,133 @@
|
|
|
1
1
|
import NitroModules
|
|
2
|
-
import
|
|
2
|
+
import metamask_ios_sdk
|
|
3
3
|
import Foundation
|
|
4
4
|
|
|
5
5
|
final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
6
|
-
|
|
6
|
+
// SDK instance - can be recreated when configure() is called
|
|
7
|
+
// Aligned with Android: SDK is recreated when configure() changes values
|
|
8
|
+
private var sdkInstance: MetaMaskSDK? = nil
|
|
9
|
+
private var lastUsedUrl: String? = nil
|
|
10
|
+
private var lastUsedScheme: String? = nil
|
|
7
11
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
12
|
+
// Get or create MetaMask SDK instance
|
|
13
|
+
// Aligned with Android: SDK is recreated when configure() changes values
|
|
14
|
+
private var sdk: MetaMaskSDK {
|
|
15
|
+
let currentUrl = dappUrl ?? "https://metamask.io"
|
|
16
|
+
let currentScheme = deepLinkScheme ?? getDefaultDappScheme()
|
|
17
|
+
|
|
18
|
+
// Check if we need to recreate the SDK
|
|
19
|
+
if let existing = sdkInstance,
|
|
20
|
+
lastUsedUrl == currentUrl,
|
|
21
|
+
lastUsedScheme == currentScheme {
|
|
22
|
+
return existing
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check if there's a shared instance we should use (only if it matches our config)
|
|
26
|
+
if let existing = MetaMaskSDK.sharedInstance,
|
|
27
|
+
lastUsedUrl == currentUrl,
|
|
28
|
+
lastUsedScheme == currentScheme {
|
|
29
|
+
sdkInstance = existing
|
|
30
|
+
return existing
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create new SDK instance with current configuration
|
|
34
|
+
let appMetadata = AppMetadata(
|
|
35
|
+
name: "NitroMetamask",
|
|
36
|
+
url: currentUrl,
|
|
37
|
+
iconUrl: nil,
|
|
38
|
+
base64Icon: nil,
|
|
39
|
+
apiVersion: nil
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
NSLog("NitroMetamask: Initializing SDK with url=\(currentUrl), scheme=\(currentScheme)")
|
|
43
|
+
|
|
44
|
+
let newSdk = MetaMaskSDK.shared(
|
|
45
|
+
appMetadata,
|
|
46
|
+
transport: .deeplinking(dappScheme: currentScheme),
|
|
47
|
+
enableDebug: true,
|
|
48
|
+
sdkOptions: nil
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
sdkInstance = newSdk
|
|
52
|
+
lastUsedUrl = currentUrl
|
|
53
|
+
lastUsedScheme = currentScheme
|
|
54
|
+
|
|
55
|
+
return newSdk
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Configurable dapp URL and deep link scheme
|
|
10
59
|
private var dappUrl: String? = nil
|
|
60
|
+
private var deepLinkScheme: String? = nil
|
|
11
61
|
|
|
12
62
|
func configure(dappUrl: String?, deepLinkScheme: String?) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
63
|
+
let urlToUse = dappUrl ?? "https://metamask.io"
|
|
64
|
+
let schemeToUse = deepLinkScheme ?? getDefaultDappScheme()
|
|
65
|
+
|
|
66
|
+
var changed = false
|
|
67
|
+
if self.dappUrl != urlToUse {
|
|
68
|
+
self.dappUrl = urlToUse
|
|
69
|
+
changed = true
|
|
70
|
+
}
|
|
71
|
+
if self.deepLinkScheme != schemeToUse {
|
|
72
|
+
self.deepLinkScheme = schemeToUse
|
|
73
|
+
changed = true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if changed {
|
|
77
|
+
// Invalidate existing instance to force recreation with new values
|
|
78
|
+
// This aligns with Android behavior
|
|
79
|
+
sdkInstance = nil
|
|
80
|
+
lastUsedUrl = nil
|
|
81
|
+
lastUsedScheme = nil
|
|
82
|
+
NSLog("NitroMetamask: configure: Dapp URL=\(urlToUse), Scheme=\(schemeToUse). SDK will be recreated on next access.")
|
|
83
|
+
} else {
|
|
84
|
+
NSLog("NitroMetamask: configure: No changes, keeping existing SDK instance.")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Helper to get default deep link scheme from Info.plist
|
|
89
|
+
private func getDefaultDappScheme() -> String {
|
|
90
|
+
// Try to get the first URL scheme from Info.plist
|
|
91
|
+
if let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]],
|
|
92
|
+
let firstType = urlTypes.first,
|
|
93
|
+
let schemes = firstType["CFBundleURLSchemes"] as? [String],
|
|
94
|
+
let firstScheme = schemes.first {
|
|
95
|
+
return firstScheme
|
|
96
|
+
}
|
|
97
|
+
// Fallback to a default scheme
|
|
98
|
+
return "nitrometamask"
|
|
18
99
|
}
|
|
19
100
|
|
|
20
101
|
func connect() -> Promise<ConnectResult> {
|
|
21
102
|
// Use Promise.async with Swift async/await for best practice in Nitro modules
|
|
22
103
|
// Reference: https://nitro.margelo.com/docs/types/promises
|
|
23
104
|
return Promise.async {
|
|
24
|
-
|
|
105
|
+
NSLog("NitroMetamask: connect() called")
|
|
106
|
+
|
|
107
|
+
// Check if MetaMask is installed before attempting to connect
|
|
108
|
+
if !self.sdk.isMetaMaskInstalled {
|
|
109
|
+
let errorMessage = "MetaMask is not installed. Please install MetaMask from the App Store to continue."
|
|
110
|
+
NSLog("NitroMetamask: MetaMask not installed - \(errorMessage)")
|
|
111
|
+
throw NSError(
|
|
112
|
+
domain: "MetamaskConnector",
|
|
113
|
+
code: -2,
|
|
114
|
+
userInfo: [NSLocalizedDescriptionKey: errorMessage]
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Based on MetaMask iOS SDK docs: connect() returns Result<[String], RequestError>
|
|
25
119
|
// Reference: https://github.com/MetaMask/metamask-ios-sdk
|
|
26
|
-
let connectResult =
|
|
120
|
+
let connectResult = await self.sdk.connect()
|
|
121
|
+
|
|
122
|
+
NSLog("NitroMetamask: connect() result: \(connectResult)")
|
|
27
123
|
|
|
28
124
|
switch connectResult {
|
|
29
125
|
case .success:
|
|
30
126
|
// After successful connection, get account info from SDK
|
|
31
|
-
// Note: sdk.account is a String (
|
|
127
|
+
// Note: sdk.account is a String (not optional), check if empty
|
|
32
128
|
// Reference: https://raw.githubusercontent.com/MetaMask/metamask-ios-sdk/924d91bb3e98a5383c3082d6d5ba3ddac9e1c565/README.md
|
|
33
|
-
|
|
129
|
+
let address = self.sdk.account
|
|
130
|
+
guard !address.isEmpty else {
|
|
34
131
|
throw NSError(
|
|
35
132
|
domain: "MetamaskConnector",
|
|
36
133
|
code: -1,
|
|
@@ -40,11 +137,9 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
40
137
|
|
|
41
138
|
// Parse chainId from hex string (e.g., "0x1") to number
|
|
42
139
|
// Nitro requires chainId to be a number, not a string, for type safety
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let chainIdInt = Int(chainIdHex.replacingOccurrences(of: "0x", with: ""), radix: 16)
|
|
47
|
-
else {
|
|
140
|
+
let chainIdHex = self.sdk.chainId
|
|
141
|
+
guard !chainIdHex.isEmpty,
|
|
142
|
+
let chainIdInt = Int(chainIdHex.replacingOccurrences(of: "0x", with: ""), radix: 16) else {
|
|
48
143
|
throw NSError(
|
|
49
144
|
domain: "MetamaskConnector",
|
|
50
145
|
code: -1,
|
|
@@ -58,6 +153,7 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
58
153
|
)
|
|
59
154
|
|
|
60
155
|
case .failure(let error):
|
|
156
|
+
NSLog("NitroMetamask: connect() failed: \(error)")
|
|
61
157
|
throw error
|
|
62
158
|
}
|
|
63
159
|
}
|
|
@@ -70,7 +166,8 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
70
166
|
// Use explicit sign() method (requires connection first via connect())
|
|
71
167
|
// This is more explicit and predictable than connectAndSign() which forces connection
|
|
72
168
|
// Nitro encourages explicit object state, not convenience shortcuts
|
|
73
|
-
|
|
169
|
+
let account = self.sdk.account
|
|
170
|
+
guard !account.isEmpty else {
|
|
74
171
|
throw NSError(
|
|
75
172
|
domain: "MetamaskConnector",
|
|
76
173
|
code: -1,
|
|
@@ -88,20 +185,19 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
88
185
|
)
|
|
89
186
|
|
|
90
187
|
// Make the request using the SDK's async request method
|
|
91
|
-
|
|
188
|
+
// request() returns Result<String, RequestError>
|
|
189
|
+
NSLog("NitroMetamask: signMessage() calling request")
|
|
190
|
+
let result = await self.sdk.request(request)
|
|
191
|
+
|
|
192
|
+
NSLog("NitroMetamask: signMessage() result: \(result)")
|
|
92
193
|
|
|
93
194
|
// Extract signature from response
|
|
94
|
-
|
|
95
|
-
|
|
195
|
+
switch result {
|
|
196
|
+
case .success(let signature):
|
|
96
197
|
return signature
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw NSError(
|
|
101
|
-
domain: "MetamaskConnector",
|
|
102
|
-
code: -1,
|
|
103
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid signature response format"]
|
|
104
|
-
)
|
|
198
|
+
case .failure(let error):
|
|
199
|
+
NSLog("NitroMetamask: signMessage() failed: \(error)")
|
|
200
|
+
throw error
|
|
105
201
|
}
|
|
106
202
|
}
|
|
107
203
|
}
|
|
@@ -133,12 +229,27 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
133
229
|
// Use the SDK's connectAndSign convenience method - it will connect if needed and sign the message
|
|
134
230
|
// This is the recommended approach per MetaMask iOS SDK documentation
|
|
135
231
|
// Reference: https://github.com/MetaMask/metamask-ios-sdk
|
|
136
|
-
|
|
232
|
+
// Check if MetaMask is installed before attempting to connect and sign
|
|
233
|
+
if !self.sdk.isMetaMaskInstalled {
|
|
234
|
+
let errorMessage = "MetaMask is not installed. Please install MetaMask from the App Store to continue."
|
|
235
|
+
NSLog("NitroMetamask: MetaMask not installed - \(errorMessage)")
|
|
236
|
+
throw NSError(
|
|
237
|
+
domain: "MetamaskConnector",
|
|
238
|
+
code: -2,
|
|
239
|
+
userInfo: [NSLocalizedDescriptionKey: errorMessage]
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
NSLog("NitroMetamask: connectSign() calling connectAndSign")
|
|
244
|
+
let connectSignResult = await self.sdk.connectAndSign(message: message)
|
|
245
|
+
|
|
246
|
+
NSLog("NitroMetamask: connectSign() result: \(connectSignResult)")
|
|
137
247
|
|
|
138
248
|
switch connectSignResult {
|
|
139
249
|
case .success(let signature):
|
|
140
250
|
// After connectSign completes, get the address and chainId from the SDK
|
|
141
|
-
|
|
251
|
+
let address = self.sdk.account
|
|
252
|
+
guard !address.isEmpty else {
|
|
142
253
|
throw NSError(
|
|
143
254
|
domain: "MetamaskConnector",
|
|
144
255
|
code: -1,
|
|
@@ -146,7 +257,8 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
146
257
|
)
|
|
147
258
|
}
|
|
148
259
|
|
|
149
|
-
|
|
260
|
+
let chainIdHex = self.sdk.chainId
|
|
261
|
+
guard !chainIdHex.isEmpty else {
|
|
150
262
|
throw NSError(
|
|
151
263
|
domain: "MetamaskConnector",
|
|
152
264
|
code: -1,
|
|
@@ -176,18 +288,20 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
176
288
|
|
|
177
289
|
func getAddress() -> Promise<Variant_NullType_String> {
|
|
178
290
|
return Promise.async {
|
|
179
|
-
|
|
291
|
+
let account = self.sdk.account
|
|
292
|
+
if !account.isEmpty {
|
|
180
293
|
return Variant_NullType_String.second(account)
|
|
181
294
|
} else {
|
|
182
|
-
return Variant_NullType_String.first(NullType
|
|
295
|
+
return Variant_NullType_String.first(NullType.null)
|
|
183
296
|
}
|
|
184
297
|
}
|
|
185
298
|
}
|
|
186
299
|
|
|
187
300
|
func getChainId() -> Promise<Variant_NullType_Int64> {
|
|
188
301
|
return Promise.async {
|
|
189
|
-
|
|
190
|
-
|
|
302
|
+
let chainIdHex = self.sdk.chainId
|
|
303
|
+
guard !chainIdHex.isEmpty else {
|
|
304
|
+
return Variant_NullType_Int64.first(NullType.null)
|
|
191
305
|
}
|
|
192
306
|
|
|
193
307
|
// Parse chainId from hex string (e.g., "0x1") to Int64 (bigint maps to Int64 in Swift)
|
|
@@ -195,7 +309,7 @@ final class HybridNitroMetamask: HybridNitroMetamaskSpec {
|
|
|
195
309
|
return Variant_NullType_Int64.second(chainIdInt)
|
|
196
310
|
} else {
|
|
197
311
|
NSLog("NitroMetamask: Invalid chainId format: \(chainIdHex)")
|
|
198
|
-
return Variant_NullType_Int64.first(NullType
|
|
312
|
+
return Variant_NullType_Int64.first(NullType.null)
|
|
199
313
|
}
|
|
200
314
|
}
|
|
201
315
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novastera-oss/nitro-metamask",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Native mobile MetaMask wallet integration for React Native. Part of Novastera CRM/ERP platform ecosystem. Provides secure authentication and message signing for Web3 mobile applications.",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|