@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
@@ -0,0 +1,134 @@
1
+ //
2
+ // SecureStore.swift
3
+ // metamask-ios-sdk
4
+ //
5
+ import Foundation
6
+
7
+ public protocol SecureStore {
8
+ func string(for key: String) -> String?
9
+
10
+ func data(for key: String) -> Data?
11
+
12
+ @discardableResult
13
+ func deleteData(for key: String) -> Bool
14
+
15
+ @discardableResult
16
+ func deleteAll() -> Bool
17
+
18
+ @discardableResult
19
+ func save(string: String, key: String) -> Bool
20
+
21
+ @discardableResult
22
+ func save(data: Data, key: String) -> Bool
23
+
24
+ func model<T: Decodable>(for key: String) -> T?
25
+ }
26
+
27
+ public struct Keychain: SecureStore {
28
+ private let service: String
29
+
30
+ public init(service: String) {
31
+ self.service = service
32
+ }
33
+
34
+ public func string(for key: String) -> String? {
35
+ guard
36
+ let data = data(for: key),
37
+ let string = String(data: data, encoding: .utf8)
38
+ else { return nil }
39
+ return string
40
+ }
41
+
42
+ public func deleteData(for key: String) -> Bool {
43
+ let request = deletionRequestForKey(key)
44
+ let status: OSStatus = SecItemDelete(request)
45
+ return status == errSecSuccess
46
+ }
47
+
48
+ public func deleteAll() -> Bool {
49
+ let request = deletionRequestForAll()
50
+ let status: OSStatus = SecItemDelete(request)
51
+ return status == errSecSuccess
52
+ }
53
+
54
+ public func save(string: String, key: String) -> Bool {
55
+ guard let data = string.data(using: .utf8) else { return false }
56
+ return save(data: data, key: key)
57
+ }
58
+
59
+ @discardableResult
60
+ public func save(data: Data, key: String) -> Bool {
61
+ guard let attributes = attributes(for: data, key: key) else { return false }
62
+
63
+ let status: OSStatus = SecItemAdd(attributes, nil)
64
+
65
+ switch status {
66
+ case noErr:
67
+ return true
68
+ case errSecDuplicateItem:
69
+ guard deleteData(for: key) else { return false }
70
+ return save(data: data, key: key)
71
+ default:
72
+ return false
73
+ }
74
+ }
75
+
76
+ public func model<T: Decodable>(for key: String) -> T? {
77
+ guard
78
+ let data = data(for: key),
79
+ let model = try? JSONDecoder().decode(T.self, from: data)
80
+ else { return nil }
81
+
82
+ return model
83
+ }
84
+
85
+ // MARK: Helper functions
86
+
87
+ public func data(for key: String) -> Data? {
88
+ let request = requestForKey(key)
89
+ var dataTypeRef: CFTypeRef?
90
+ let status: OSStatus = SecItemCopyMatching(request, &dataTypeRef)
91
+
92
+ switch status {
93
+ case errSecSuccess:
94
+ return dataTypeRef as? Data
95
+ default:
96
+ return nil
97
+ }
98
+ }
99
+
100
+ private func requestForKey(_ key: String) -> CFDictionary {
101
+ [
102
+ kSecReturnData: true,
103
+ kSecAttrAccount: key,
104
+ kSecAttrService: service,
105
+ kSecMatchLimit: kSecMatchLimitOne,
106
+ kSecClass: kSecClassGenericPassword
107
+ ] as CFDictionary
108
+ }
109
+
110
+ private func deletionRequestForKey(_ key: String) -> CFDictionary {
111
+ [
112
+ kSecAttrAccount: key,
113
+ kSecAttrService: service,
114
+ kSecClass: kSecClassGenericPassword
115
+ ] as CFDictionary
116
+ }
117
+
118
+ private func deletionRequestForAll() -> CFDictionary {
119
+ [
120
+ kSecAttrService: service,
121
+ kSecClass: kSecClassGenericPassword
122
+ ] as CFDictionary
123
+ }
124
+
125
+ private func attributes(for data: Data, key: String) -> CFDictionary? {
126
+ [
127
+ kSecValueData: data,
128
+ kSecAttrAccount: key,
129
+ kSecAttrService: service,
130
+ kSecClass: kSecClassGenericPassword,
131
+ kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
132
+ ] as CFDictionary
133
+ }
134
+ }
@@ -0,0 +1,24 @@
1
+ //
2
+ // SessionConfig.swift
3
+ // metamask-ios-sdk
4
+ //
5
+
6
+ import Foundation
7
+
8
+ public class SessionConfig: Codable, Equatable {
9
+ public static func == (lhs: SessionConfig, rhs: SessionConfig) -> Bool {
10
+ lhs.sessionId == rhs.sessionId && lhs.expiry == rhs.expiry
11
+ }
12
+
13
+ public let sessionId: String
14
+ public let expiry: Date
15
+
16
+ public var isValid: Bool {
17
+ expiry > Date()
18
+ }
19
+
20
+ public init(sessionId: String, expiry: Date) {
21
+ self.sessionId = sessionId
22
+ self.expiry = expiry
23
+ }
24
+ }
@@ -0,0 +1,56 @@
1
+ //
2
+ // SessionManager.swift
3
+ // metamask-ios-sdk
4
+ //
5
+
6
+ import Foundation
7
+
8
+ public class SessionManager {
9
+ private let store: SecureStore
10
+ private let SESSION_KEY = "session_id"
11
+ private let DEFAULT_SESSION_DURATION: TimeInterval = 24 * 7 * 3600
12
+
13
+ public var sessionDuration: TimeInterval
14
+
15
+ public init(store: SecureStore,
16
+ sessionDuration: TimeInterval) {
17
+ self.store = store
18
+ self.sessionDuration = sessionDuration
19
+ }
20
+
21
+ public func fetchCurrentSessionConfig() -> SessionConfig? {
22
+ let config: SessionConfig? = store.model(for: SESSION_KEY)
23
+ return config
24
+ }
25
+
26
+ public func createNewSessionConfig() {
27
+ // update session expiry date
28
+ let config = SessionConfig(sessionId: UUID().uuidString.lowercased(),
29
+ expiry: Date(timeIntervalSinceNow: sessionDuration))
30
+ if !config.isValid {
31
+ sessionDuration = DEFAULT_SESSION_DURATION
32
+ createNewSessionConfig()
33
+ }
34
+ // persist session config
35
+ if let configData = try? JSONEncoder().encode(config) {
36
+ store.save(data: configData, key: SESSION_KEY)
37
+ }
38
+ }
39
+
40
+ public func fetchSessionConfig() -> (SessionConfig, Bool) {
41
+
42
+ if let config = fetchCurrentSessionConfig(), config.isValid {
43
+ return (config, true)
44
+ } else {
45
+ // purge any existing session info
46
+ store.deleteData(for: SESSION_KEY)
47
+ createNewSessionConfig()
48
+ let config = fetchSessionConfig().0
49
+ return (config, false)
50
+ }
51
+ }
52
+
53
+ public func clear() {
54
+ store.deleteAll()
55
+ }
56
+ }
@@ -0,0 +1,35 @@
1
+ //
2
+ // Dependencies.swift
3
+ // metamask-ios-sdk
4
+ //
5
+
6
+ import Foundation
7
+
8
+ public final class Dependencies {
9
+ public static let shared = Dependencies()
10
+
11
+ public lazy var store: SecureStore = Keychain(service: SDKInfo.bundleIdentifier ?? UUID().uuidString)
12
+ public lazy var sessionManager: SessionManager = SessionManager(store: store, sessionDuration: 24 * 3600 * 30)
13
+
14
+ public lazy var commClientFactory: CommClientFactory = CommClientFactory()
15
+
16
+ public func ethereum(transport: Transport, sdkOptions: SDKOptions?) -> Ethereum {
17
+ Ethereum.shared(
18
+ transport: transport,
19
+ store: store,
20
+ commClientFactory: commClientFactory
21
+ ).updateTransportLayer(transport)
22
+ }
23
+
24
+ public lazy var keyExchange: KeyExchange = KeyExchange(storage: store)
25
+
26
+ public lazy var deeplinkManager: DeeplinkManager = DeeplinkManager()
27
+
28
+ public func deeplinkClient(dappScheme: String) -> DeeplinkClient {
29
+ DeeplinkClient(
30
+ session: sessionManager,
31
+ keyExchange: keyExchange,
32
+ deeplinkManager: deeplinkManager,
33
+ dappScheme: dappScheme)
34
+ }
35
+ }
@@ -0,0 +1,215 @@
1
+ //
2
+ // MetaMaskSDK.swift
3
+ //
4
+
5
+ import SwiftUI
6
+ import UIKit
7
+ import Combine
8
+ import Foundation
9
+
10
+ class SDKWrapper {
11
+ var sdk: MetaMaskSDK?
12
+ static let shared = SDKWrapper()
13
+ }
14
+
15
+ public class MetaMaskSDK: ObservableObject {
16
+ private var ethereum: Ethereum!
17
+
18
+ /// The active/selected MetaMask account chain
19
+ @Published public var chainId: String = ""
20
+ /// Indicated whether connected to MetaMask
21
+ @Published public var connected: Bool = false
22
+
23
+ /// The active/selected MetaMask account address
24
+ @Published public var account: String = ""
25
+
26
+ public static var sharedInstance: MetaMaskSDK? = SDKWrapper.shared.sdk
27
+
28
+ public var transport: Transport
29
+
30
+ public var networkUrl: String {
31
+ get {
32
+ (ethereum.commClient as? SocketClient)?.networkUrl ?? ""
33
+ } set {
34
+ (ethereum.commClient as? SocketClient)?.networkUrl = newValue
35
+ }
36
+ }
37
+
38
+ public var useDeeplinks: Bool = false {
39
+ didSet {
40
+ (ethereum.commClient as? SocketClient)?.useDeeplinks = useDeeplinks
41
+ }
42
+ }
43
+
44
+ public var sessionDuration: TimeInterval {
45
+ get {
46
+ ethereum.commClient.sessionDuration
47
+ } set {
48
+ ethereum.commClient.sessionDuration = newValue
49
+ }
50
+ }
51
+
52
+ private init(appMetadata: AppMetadata, transport: Transport, enableDebug: Bool, sdkOptions: SDKOptions?) {
53
+ self.ethereum = Dependencies.shared.ethereum(transport: transport, sdkOptions: sdkOptions)
54
+ self.transport = transport
55
+ self.ethereum.delegate = self
56
+ self.ethereum.updateMetadata(appMetadata)
57
+ self.account = ethereum.account
58
+ self.chainId = ethereum.chainId
59
+ }
60
+
61
+ public var isMetaMaskInstalled: Bool {
62
+ guard let url = URL(string: "metamask://") else {
63
+ return false
64
+ }
65
+ return UIApplication.shared.canOpenURL(url)
66
+ }
67
+
68
+ public func handleUrl(_ url: URL) {
69
+ (ethereum.commClient as? DeeplinkClient)?.handleUrl(url)
70
+ }
71
+
72
+ public static func shared(_ appMetadata: AppMetadata,
73
+ transport: Transport,
74
+ enableDebug: Bool = true,
75
+ sdkOptions: SDKOptions?) -> MetaMaskSDK {
76
+ guard let sdk = SDKWrapper.shared.sdk else {
77
+ let metamaskSdk = MetaMaskSDK(
78
+ appMetadata: appMetadata,
79
+ transport: transport,
80
+ enableDebug: enableDebug,
81
+ sdkOptions: sdkOptions)
82
+ SDKWrapper.shared.sdk = metamaskSdk
83
+ return metamaskSdk
84
+ }
85
+ return sdk
86
+ }
87
+
88
+ public func updateTransportLayer(_ transport: Transport) {
89
+ self.ethereum.updateTransportLayer(transport)
90
+ }
91
+ }
92
+
93
+ public extension MetaMaskSDK {
94
+ func connect() async -> Result<[String], RequestError> {
95
+ await ethereum.connect()
96
+ }
97
+
98
+ func connectAndSign(message: String) async -> Result<String, RequestError> {
99
+ await ethereum.connectAndSign(message: message)
100
+ }
101
+
102
+ func connectWith<T: CodableData>(_ request: EthereumRequest<T>) async -> Result<String, RequestError> {
103
+ await ethereum.connectWith(request)
104
+ }
105
+
106
+ func disconnect() {
107
+ ethereum.disconnect()
108
+ }
109
+
110
+ func clearSession() {
111
+ ethereum.clearSession()
112
+ connected = false
113
+ }
114
+
115
+ func terminateConnection() {
116
+ ethereum.terminateConnection()
117
+ }
118
+
119
+ func request<T: CodableData>(_ request: EthereumRequest<T>) async -> Result<String, RequestError> {
120
+ await ethereum.request(request)
121
+ }
122
+
123
+ func batchRequest<T: CodableData>(_ requests: [EthereumRequest<T>]) async -> Result<[String], RequestError> {
124
+ await ethereum.batchRequest(requests)
125
+ }
126
+
127
+ func getChainId() async -> Result<String, RequestError> {
128
+ await ethereum.getChainId()
129
+ }
130
+
131
+ func getEthAccounts() async -> Result<[String], RequestError> {
132
+ await ethereum.getEthAccounts()
133
+ }
134
+
135
+ func getEthGasPrice() async -> Result<String, RequestError> {
136
+ await ethereum.getEthGasPrice()
137
+ }
138
+
139
+ func getEthBalance(address: String, block: String) async -> Result<String, RequestError> {
140
+ await ethereum.getEthBalance(address: address, block: block)
141
+ }
142
+
143
+ func getEthBlockNumber() async -> Result<String, RequestError> {
144
+ await ethereum.getEthBlockNumber()
145
+ }
146
+
147
+ func getEthEstimateGas() async -> Result<String, RequestError> {
148
+ await ethereum.getEthEstimateGas()
149
+ }
150
+
151
+ func getWeb3ClientVersion() async -> Result<String, RequestError> {
152
+ await ethereum.getWeb3ClientVersion()
153
+ }
154
+
155
+ func personalSign(message: String, address: String) async -> Result<String, RequestError> {
156
+ await ethereum.personalSign(message: message, address: address)
157
+ }
158
+
159
+ func signTypedDataV4(typedData: String, address: String) async -> Result<String, RequestError> {
160
+ await ethereum.signTypedDataV4(typedData: typedData, address: address)
161
+ }
162
+
163
+ func sendTransaction(from: String, to: String, value: String) async -> Result<String, RequestError> {
164
+ await ethereum.sendTransaction(from: from, to: to, value: value)
165
+ }
166
+
167
+ func sendRawTransaction(signedTransaction: String) async -> Result<String, RequestError> {
168
+ await ethereum.sendRawTransaction(signedTransaction: signedTransaction)
169
+ }
170
+
171
+ func getBlockTransactionCountByNumber(blockNumber: String) async -> Result<String, RequestError> {
172
+ await ethereum.getBlockTransactionCountByNumber(blockNumber: blockNumber)
173
+ }
174
+
175
+ func getBlockTransactionCountByHash(blockHash: String) async -> Result<String, RequestError> {
176
+ await ethereum.getBlockTransactionCountByHash(blockHash: blockHash)
177
+ }
178
+
179
+ func getTransactionCount(address: String, tagOrblockNumber: String) async -> Result<String, RequestError> {
180
+ await ethereum.getTransactionCount(address: address, tagOrblockNumber: tagOrblockNumber)
181
+ }
182
+
183
+ func addEthereumChain(chainId: String,
184
+ chainName: String,
185
+ rpcUrls: [String],
186
+ iconUrls: [String]?,
187
+ blockExplorerUrls: [String]?,
188
+ nativeCurrency: NativeCurrency) async -> Result<String, RequestError> {
189
+ await ethereum.addEthereumChain(
190
+ chainId: chainId,
191
+ chainName: chainName,
192
+ rpcUrls: rpcUrls,
193
+ iconUrls: iconUrls,
194
+ blockExplorerUrls: blockExplorerUrls,
195
+ nativeCurrency: nativeCurrency
196
+ )
197
+ }
198
+
199
+ func switchEthereumChain(chainId: String) async -> Result<String, RequestError> {
200
+ await ethereum.switchEthereumChain(chainId: chainId)
201
+ }
202
+ }
203
+
204
+ extension MetaMaskSDK: EthereumEventsDelegate {
205
+ func chainIdChanged(_ chainId: String) {
206
+ self.chainId = chainId
207
+ NotificationCenter.default.post(name: .MetaMaskChainIdChanged, object: nil, userInfo: ["chainId": chainId])
208
+ }
209
+
210
+ func accountChanged(_ account: String) {
211
+ self.account = account
212
+ connected = true
213
+ NotificationCenter.default.post(name: .MetaMaskAccountChanged, object: nil, userInfo: ["account": account])
214
+ }
215
+ }
@@ -0,0 +1,37 @@
1
+ //
2
+ // SDKInfo.swift
3
+ //
4
+
5
+ import UIKit
6
+ import Foundation
7
+
8
+ public enum SDKInfo {
9
+ /// Bundle with SDK plist
10
+ public static var sdkBundle: [String: Any] {
11
+ Bundle(for: MetaMaskSDK.self).infoDictionary ?? [:]
12
+ }
13
+
14
+ /// The version number of the SDK e.g `1.0.0`
15
+ public static var version: String {
16
+ sdkBundle["CFBundleShortVersionString"] as? String ?? ""
17
+ }
18
+
19
+ /// The bundle identifier of the dapp
20
+ public static var bundleIdentifier: String? {
21
+ Bundle.main.bundleIdentifier
22
+ }
23
+
24
+ /// The platform OS on which the SDK is run e.g `ios, ipados`
25
+ public static var platform: String {
26
+ UIDevice.current.systemName.lowercased()
27
+ }
28
+
29
+ // Checks if Dapp is configured for Deeplink communication layer
30
+ public static func isConfiguredForURLScheme(_ scheme: String) -> Bool {
31
+ guard let urlTypes = sdkBundle["CFBundleURLTypes"] as? [AnyObject],
32
+ let urlTypeDictionary = urlTypes.first as? [String: AnyObject],
33
+ let urlSchemes = urlTypeDictionary["CFBundleURLSchemes"] as? [String]
34
+ else { return false }
35
+ return urlSchemes.contains(scheme)
36
+ }
37
+ }
@@ -0,0 +1,16 @@
1
+ //
2
+ // SDKOptions.swift
3
+ // metamask-ios-sdk
4
+ //
5
+
6
+ import Foundation
7
+
8
+ public struct SDKOptions {
9
+ public let infuraAPIKey: String
10
+ public let readonlyRPCMap: [String: String]
11
+
12
+ public init(infuraAPIKey: String, readonlyRPCMap: [String: String] = [:]) {
13
+ self.infuraAPIKey = infuraAPIKey
14
+ self.readonlyRPCMap = readonlyRPCMap
15
+ }
16
+ }
@@ -4,17 +4,64 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.NitroMetamask = void 0;
7
+ exports.parseNitroError = parseNitroError;
7
8
  var _reactNativeNitroModules = require("react-native-nitro-modules");
8
9
  /**
9
10
  * NitroMetamask - MetaMask connector for React Native
10
- *
11
+ *
12
+ * Provides native MetaMask wallet integration via Nitro modules.
13
+ *
11
14
  * @example
12
15
  * ```ts
13
16
  * import { NitroMetamask } from '@novastera-oss/nitro-metamask'
14
- *
15
- * const result = await NitroMetamask.connect()
17
+ *
18
+ * // Optional: configure before use
19
+ * NitroMetamask.configure('https://yourdomain.com', 'yourscheme')
20
+ *
21
+ * // Connect wallet
22
+ * const { address, chainId } = await NitroMetamask.connect()
23
+ *
24
+ * // Sign a message
16
25
  * const signature = await NitroMetamask.signMessage('Hello')
26
+ *
27
+ * // Connect and sign in one step (SIWE)
28
+ * const result = await NitroMetamask.connectSign('my-nonce', BigInt(Date.now()))
17
29
  * ```
18
30
  */
19
31
  const NitroMetamask = exports.NitroMetamask = _reactNativeNitroModules.NitroModules.createHybridObject('NitroMetamask');
32
+ /**
33
+ * Parses a structured Nitro error message that contains a numeric error code prefix.
34
+ *
35
+ * Nitro errors from the native layer are formatted as `"[<code>] <message>"`.
36
+ * This utility extracts the code and message so the JS layer can handle them
37
+ * programmatically (e.g., detecting `code: 2` for MetaMask not installed).
38
+ *
39
+ * @param e - The error to parse. Can be an `Error`, a string, or any value.
40
+ * @returns An object `{ code: number; message: string }` if the error message
41
+ * matches the `[code] message` pattern, or `null` if it does not.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { parseNitroError } from '@novastera-oss/nitro-metamask'
46
+ *
47
+ * try {
48
+ * await NitroMetamask.connect()
49
+ * } catch (e) {
50
+ * const parsed = parseNitroError(e)
51
+ * if (parsed?.code === 2) {
52
+ * // MetaMask is not installed — redirect user to App Store / Play Store
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ function parseNitroError(e) {
58
+ const raw = e instanceof Error ? e.message : typeof e === 'string' ? e : null;
59
+ if (raw === null) return null;
60
+ const match = raw.match(/^\[(\d+)\]\s*(.*)/);
61
+ if (!match) return null;
62
+ return {
63
+ code: Number(match[1]),
64
+ message: match[2] ?? ''
65
+ };
66
+ }
20
67
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNativeNitroModules","require","NitroMetamask","exports","NitroModules","createHybridObject"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAGE,qCAAY,CAACC,kBAAkB,CAAoB,eAAe,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_reactNativeNitroModules","require","NitroMetamask","exports","NitroModules","createHybridObject","parseNitroError","e","raw","Error","message","match","code","Number"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAGE,qCAAY,CAACC,kBAAkB,CAAoB,eAAe,CAAC;AAIhG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,eAAeA,CAACC,CAAU,EAA4C;EACpF,MAAMC,GAAG,GAAGD,CAAC,YAAYE,KAAK,GAAGF,CAAC,CAACG,OAAO,GAAG,OAAOH,CAAC,KAAK,QAAQ,GAAGA,CAAC,GAAG,IAAI;EAC7E,IAAIC,GAAG,KAAK,IAAI,EAAE,OAAO,IAAI;EAC7B,MAAMG,KAAK,GAAGH,GAAG,CAACG,KAAK,CAAC,mBAAmB,CAAC;EAC5C,IAAI,CAACA,KAAK,EAAE,OAAO,IAAI;EACvB,OAAO;IAAEC,IAAI,EAAEC,MAAM,CAACF,KAAK,CAAC,CAAC,CAAC,CAAC;IAAED,OAAO,EAAEC,KAAK,CAAC,CAAC,CAAC,IAAI;EAAG,CAAC;AAC5D","ignoreList":[]}
@@ -3,14 +3,60 @@
3
3
  import { NitroModules } from 'react-native-nitro-modules';
4
4
  /**
5
5
  * NitroMetamask - MetaMask connector for React Native
6
- *
6
+ *
7
+ * Provides native MetaMask wallet integration via Nitro modules.
8
+ *
7
9
  * @example
8
10
  * ```ts
9
11
  * import { NitroMetamask } from '@novastera-oss/nitro-metamask'
10
- *
11
- * const result = await NitroMetamask.connect()
12
+ *
13
+ * // Optional: configure before use
14
+ * NitroMetamask.configure('https://yourdomain.com', 'yourscheme')
15
+ *
16
+ * // Connect wallet
17
+ * const { address, chainId } = await NitroMetamask.connect()
18
+ *
19
+ * // Sign a message
12
20
  * const signature = await NitroMetamask.signMessage('Hello')
21
+ *
22
+ * // Connect and sign in one step (SIWE)
23
+ * const result = await NitroMetamask.connectSign('my-nonce', BigInt(Date.now()))
13
24
  * ```
14
25
  */
15
26
  export const NitroMetamask = NitroModules.createHybridObject('NitroMetamask');
27
+ /**
28
+ * Parses a structured Nitro error message that contains a numeric error code prefix.
29
+ *
30
+ * Nitro errors from the native layer are formatted as `"[<code>] <message>"`.
31
+ * This utility extracts the code and message so the JS layer can handle them
32
+ * programmatically (e.g., detecting `code: 2` for MetaMask not installed).
33
+ *
34
+ * @param e - The error to parse. Can be an `Error`, a string, or any value.
35
+ * @returns An object `{ code: number; message: string }` if the error message
36
+ * matches the `[code] message` pattern, or `null` if it does not.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { parseNitroError } from '@novastera-oss/nitro-metamask'
41
+ *
42
+ * try {
43
+ * await NitroMetamask.connect()
44
+ * } catch (e) {
45
+ * const parsed = parseNitroError(e)
46
+ * if (parsed?.code === 2) {
47
+ * // MetaMask is not installed — redirect user to App Store / Play Store
48
+ * }
49
+ * }
50
+ * ```
51
+ */
52
+ export function parseNitroError(e) {
53
+ const raw = e instanceof Error ? e.message : typeof e === 'string' ? e : null;
54
+ if (raw === null) return null;
55
+ const match = raw.match(/^\[(\d+)\]\s*(.*)/);
56
+ if (!match) return null;
57
+ return {
58
+ code: Number(match[1]),
59
+ message: match[2] ?? ''
60
+ };
61
+ }
16
62
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NitroModules","NitroMetamask","createHybridObject"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAGzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,aAAa,GAAGD,YAAY,CAACE,kBAAkB,CAAoB,eAAe,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["NitroModules","NitroMetamask","createHybridObject","parseNitroError","e","raw","Error","message","match","code","Number"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAOzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,aAAa,GAAGD,YAAY,CAACE,kBAAkB,CAAoB,eAAe,CAAC;AAIhG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAACC,CAAU,EAA4C;EACpF,MAAMC,GAAG,GAAGD,CAAC,YAAYE,KAAK,GAAGF,CAAC,CAACG,OAAO,GAAG,OAAOH,CAAC,KAAK,QAAQ,GAAGA,CAAC,GAAG,IAAI;EAC7E,IAAIC,GAAG,KAAK,IAAI,EAAE,OAAO,IAAI;EAC7B,MAAMG,KAAK,GAAGH,GAAG,CAACG,KAAK,CAAC,mBAAmB,CAAC;EAC5C,IAAI,CAACA,KAAK,EAAE,OAAO,IAAI;EACvB,OAAO;IAAEC,IAAI,EAAEC,MAAM,CAACF,KAAK,CAAC,CAAC,CAAC,CAAC;IAAED,OAAO,EAAEC,KAAK,CAAC,CAAC,CAAC,IAAI;EAAG,CAAC;AAC5D","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parseNitroError.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseNitroError.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/parseNitroError.test.ts"],"names":[],"mappings":""}