@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.
- package/NitroMetamask.podspec +12 -3
- package/README.md +3 -1
- package/android/build.gradle +14 -32
- package/android/cargo-ecies.gradle +60 -88
- package/android/src/main/aidl/io/metamask/nativesdk/IMessegeService.aidl +8 -0
- package/android/src/main/aidl/io/metamask/nativesdk/IMessegeServiceCallback.aidl +8 -0
- package/android/src/main/java/com/margelo/nitro/nitrometamask/HybridNitroMetamask.kt +101 -3
- package/android/src/main/java/io/metamask/androidsdk/AnyRequest.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/ClientMessageServiceCallback.kt +12 -0
- package/android/src/main/java/io/metamask/androidsdk/ClientServiceConnection.kt +42 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClient.kt +525 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModule.kt +47 -0
- package/android/src/main/java/io/metamask/androidsdk/CommunicationClientModuleInterface.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/Constants.kt +5 -0
- package/android/src/main/java/io/metamask/androidsdk/Crypto.kt +35 -0
- package/android/src/main/java/io/metamask/androidsdk/DappMetadata.kt +36 -0
- package/android/src/main/java/io/metamask/androidsdk/Encryption.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/ErrorType.kt +41 -0
- package/android/src/main/java/io/metamask/androidsdk/Ethereum.kt +328 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumEventCallback.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumMethod.kt +80 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumRequest.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/EthereumState.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyExchange.kt +77 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyExchangeMessageType.kt +20 -0
- package/android/src/main/java/io/metamask/androidsdk/KeyStorage.kt +122 -0
- package/android/src/main/java/io/metamask/androidsdk/Logger.kt +18 -0
- package/android/src/main/java/io/metamask/androidsdk/Message.kt +3 -0
- package/android/src/main/java/io/metamask/androidsdk/MessageType.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/OriginatorInfo.kt +12 -0
- package/android/src/main/java/io/metamask/androidsdk/RequestError.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/RequestInfo.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/Result.kt +11 -0
- package/android/src/main/java/io/metamask/androidsdk/RpcRequest.kt +7 -0
- package/android/src/main/java/io/metamask/androidsdk/SDKInfo.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/SDKOptions.kt +6 -0
- package/android/src/main/java/io/metamask/androidsdk/SecureStorage.kt +9 -0
- package/android/src/main/java/io/metamask/androidsdk/SessionConfig.kt +10 -0
- package/android/src/main/java/io/metamask/androidsdk/SessionManager.kt +92 -0
- package/android/src/main/java/io/metamask/androidsdk/SubmittedRequest.kt +8 -0
- package/android/src/main/java/io/metamask/androidsdk/TimeStampGenerator.kt +7 -0
- package/android/src/main/jniLibs/arm64-v8a/libecies.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libecies.so +0 -0
- package/android/src/main/jniLibs/x86/libecies.so +0 -0
- package/android/src/main/jniLibs/x86_64/libecies.so +0 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/CancellationStateMachineTest.kt +128 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ChainIdParsingTest.kt +65 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ConfigureStateMachineTest.kt +140 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/ConnectSignJsonTest.kt +76 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/MetaMaskInstallationCheckTest.kt +42 -0
- package/android/src/test/java/com/margelo/nitro/nitrometamask/PersonalSignParamsTest.kt +75 -0
- package/ios/Frameworks/Ecies.xcframework/Info.plist +47 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/ecies.h +20 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/Headers/module.modulemap +4 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64/libecies.a +0 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/ecies.h +20 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/Headers/module.modulemap +4 -0
- package/ios/Frameworks/Ecies.xcframework/ios-arm64-simulator/libecies.a +0 -0
- package/ios/HybridNitroMetamask.swift +119 -54
- package/ios/NitroMetamaskTests/CancellationStateMachineTests.swift +150 -0
- package/ios/NitroMetamaskTests/ChainIdParsingTests.swift +117 -0
- package/ios/NitroMetamaskTests/ConfigureStateMachineTests.swift +174 -0
- package/ios/NitroMetamaskTests/ConnectSignJsonTests.swift +168 -0
- package/ios/NitroMetamaskTests/DefaultDappUrlTests.swift +80 -0
- package/ios/NitroMetamaskTests/PersonalSignParamsTests.swift +101 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommClient.swift +43 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommClientFactory.swift +17 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/CommLayer.swift +36 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/Deeplink.swift +26 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkClient.swift +199 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/DeeplinkManager.swift +83 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/String.swift +48 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/DeeplinkCommLayer/URLOpener.swift +19 -0
- package/ios/metamask-ios-sdk/CommunicationLayer/SocketClient.swift +27 -0
- package/ios/metamask-ios-sdk/Crypto/Crypto.swift +72 -0
- package/ios/metamask-ios-sdk/Crypto/Encoding.swift +15 -0
- package/ios/metamask-ios-sdk/Crypto/KeyExchange.swift +236 -0
- package/ios/metamask-ios-sdk/DeviceInfo/DeviceInfo.swift +11 -0
- package/ios/metamask-ios-sdk/Ethereum/AppMetadata.swift +28 -0
- package/ios/metamask-ios-sdk/Ethereum/ErrorType.swift +62 -0
- package/ios/metamask-ios-sdk/Ethereum/Ethereum.swift +810 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumMethod.swift +111 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumRequest.swift +40 -0
- package/ios/metamask-ios-sdk/Ethereum/EthereumWrapper.swift +10 -0
- package/ios/metamask-ios-sdk/Ethereum/RPCRequest.swift +14 -0
- package/ios/metamask-ios-sdk/Ethereum/RequestError.swift +88 -0
- package/ios/metamask-ios-sdk/Ethereum/ResponseMethod.swift +22 -0
- package/ios/metamask-ios-sdk/Ethereum/SubmitRequest.swift +26 -0
- package/ios/metamask-ios-sdk/Ethereum/TimestampGenerator.swift +16 -0
- package/ios/metamask-ios-sdk/Extensions/NSRecursiveLock.swift +14 -0
- package/ios/metamask-ios-sdk/Extensions/Notification.swift +10 -0
- package/ios/metamask-ios-sdk/Logger/Logging.swift +27 -0
- package/ios/metamask-ios-sdk/Models/AddChainParameters.swift +35 -0
- package/ios/metamask-ios-sdk/Models/Event.swift +19 -0
- package/ios/metamask-ios-sdk/Models/Mappable.swift +40 -0
- package/ios/metamask-ios-sdk/Models/NativeCurrency.swift +25 -0
- package/ios/metamask-ios-sdk/Models/OriginatorInfo.swift +26 -0
- package/ios/metamask-ios-sdk/Models/RequestInfo.swift +18 -0
- package/ios/metamask-ios-sdk/Models/SignContract.swift +48 -0
- package/ios/metamask-ios-sdk/Models/Typealiases.swift +9 -0
- package/ios/metamask-ios-sdk/Persistence/SecureStore.swift +134 -0
- package/ios/metamask-ios-sdk/Persistence/SessionConfig.swift +24 -0
- package/ios/metamask-ios-sdk/Persistence/SessionManager.swift +56 -0
- package/ios/metamask-ios-sdk/SDK/Dependencies.swift +35 -0
- package/ios/metamask-ios-sdk/SDK/MetaMaskSDK.swift +215 -0
- package/ios/metamask-ios-sdk/SDK/SDKInfo.swift +37 -0
- package/ios/metamask-ios-sdk/SDK/SDKOptions.swift +16 -0
- package/lib/commonjs/index.js +50 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +49 -3
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/__tests__/parseNitroError.test.d.ts +2 -0
- package/lib/typescript/src/__tests__/parseNitroError.test.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +43 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts +29 -1
- package/lib/typescript/src/specs/nitro-metamask.nitro.d.ts.map +1 -1
- package/package.json +21 -12
- package/react-native.config.js +5 -0
- package/rust/ecies-jni/Cargo.lock +50 -86
- package/rust/ecies-jni/Cargo.toml +1 -1
- package/rust/ecies-jni/src/lib.rs +164 -100
- package/src/__tests__/parseNitroError.test.ts +35 -0
- package/src/index.ts +53 -5
- package/src/specs/nitro-metamask.nitro.ts +29 -1
- package/scripts/verify-16k-page-alignment.py +0 -117
- package/scripts/verify-16k-page-alignment.sh +0 -5
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Ethereum.swift
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import UIKit
|
|
6
|
+
import Combine
|
|
7
|
+
import Foundation
|
|
8
|
+
|
|
9
|
+
typealias EthereumPublisher = AnyPublisher<Any, RequestError>
|
|
10
|
+
|
|
11
|
+
protocol EthereumEventsDelegate: AnyObject {
|
|
12
|
+
func chainIdChanged(_ chainId: String)
|
|
13
|
+
func accountChanged(_ account: String)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public class Ethereum {
|
|
17
|
+
static let CONNECTION_ID = TimestampGenerator.timestamp()
|
|
18
|
+
static let BATCH_CONNECTION_ID = TimestampGenerator.timestamp()
|
|
19
|
+
|
|
20
|
+
var submittedRequests: [String: SubmittedRequest] = [:]
|
|
21
|
+
private let queue = DispatchQueue(label: "submittedRequests.queue")
|
|
22
|
+
|
|
23
|
+
private var cancellables: Set<AnyCancellable> = []
|
|
24
|
+
private let cancellablesLock = NSRecursiveLock()
|
|
25
|
+
|
|
26
|
+
weak var delegate: EthereumEventsDelegate?
|
|
27
|
+
|
|
28
|
+
var connected: Bool = false
|
|
29
|
+
|
|
30
|
+
/// The active/selected MetaMask account chain
|
|
31
|
+
var chainId: String = ""
|
|
32
|
+
|
|
33
|
+
/// The active/selected MetaMask account address
|
|
34
|
+
var account: String = ""
|
|
35
|
+
|
|
36
|
+
let store: SecureStore
|
|
37
|
+
var appMetadata: AppMetadata?
|
|
38
|
+
var commClient: CommClient
|
|
39
|
+
public var transport: Transport
|
|
40
|
+
var commClientFactory: CommClientFactory
|
|
41
|
+
|
|
42
|
+
private let ACCOUNT_KEY = "ACCOUNT_KEY"
|
|
43
|
+
private let CHAINID_KEY = "CHAIN_ID_KEY"
|
|
44
|
+
|
|
45
|
+
private init(transport: Transport,
|
|
46
|
+
store: SecureStore,
|
|
47
|
+
commClientFactory: CommClientFactory) {
|
|
48
|
+
self.store = store
|
|
49
|
+
self.transport = transport
|
|
50
|
+
|
|
51
|
+
switch transport {
|
|
52
|
+
case .socket:
|
|
53
|
+
self.commClient = commClientFactory.socketClient()
|
|
54
|
+
case .deeplinking(let dappScheme):
|
|
55
|
+
self.commClient = commClientFactory.deeplinkClient(dappScheme: dappScheme)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
self.commClientFactory = commClientFactory
|
|
59
|
+
self.commClient.handleResponse = handleMessage
|
|
60
|
+
self.commClient.onClientsTerminated = terminateConnection
|
|
61
|
+
fetchCachedSession()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static func shared(transport: Transport,
|
|
65
|
+
store: SecureStore,
|
|
66
|
+
commClientFactory: CommClientFactory) -> Ethereum {
|
|
67
|
+
guard let ethereum = EthereumWrapper.shared.ethereum else {
|
|
68
|
+
let ethereum = Ethereum(
|
|
69
|
+
transport: transport,
|
|
70
|
+
store: store,
|
|
71
|
+
commClientFactory: commClientFactory)
|
|
72
|
+
EthereumWrapper.shared.ethereum = ethereum
|
|
73
|
+
return ethereum
|
|
74
|
+
}
|
|
75
|
+
return ethereum
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private func fetchCachedSession() {
|
|
79
|
+
if
|
|
80
|
+
let account = store.string(for: ACCOUNT_KEY),
|
|
81
|
+
let chainId = store.string(for: CHAINID_KEY)
|
|
82
|
+
{
|
|
83
|
+
self.account = account
|
|
84
|
+
self.chainId = chainId
|
|
85
|
+
connected = true
|
|
86
|
+
delegate?.accountChanged(account)
|
|
87
|
+
delegate?.chainIdChanged(chainId)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@discardableResult
|
|
92
|
+
func updateTransportLayer(_ transport: Transport) -> Ethereum {
|
|
93
|
+
self.transport = transport
|
|
94
|
+
|
|
95
|
+
switch transport {
|
|
96
|
+
case .deeplinking(let dappScheme):
|
|
97
|
+
commClient = commClientFactory.deeplinkClient(dappScheme: dappScheme)
|
|
98
|
+
case .socket:
|
|
99
|
+
commClient = commClientFactory.socketClient()
|
|
100
|
+
commClient.onClientsTerminated = terminateConnection
|
|
101
|
+
}
|
|
102
|
+
commClient.appMetadata = appMetadata
|
|
103
|
+
|
|
104
|
+
fetchCachedSession()
|
|
105
|
+
|
|
106
|
+
commClient.handleResponse = handleMessage
|
|
107
|
+
return self
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
func updateMetadata(_ metadata: AppMetadata) {
|
|
111
|
+
appMetadata = metadata
|
|
112
|
+
commClient.appMetadata = metadata
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func addRequest(_ submittedRequest: SubmittedRequest, id: String) {
|
|
116
|
+
queue.async { [weak self] in
|
|
117
|
+
self?.submittedRequests[id] = submittedRequest
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func getAllRequests() -> [String: SubmittedRequest] {
|
|
122
|
+
return queue.sync { [weak self] in
|
|
123
|
+
return self?.submittedRequests ?? [:]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func getRequest(id: String) -> SubmittedRequest? {
|
|
128
|
+
return queue.sync { [weak self] in
|
|
129
|
+
return self?.submittedRequests[id]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func removeRequest(id: String) {
|
|
134
|
+
queue.async { [weak self] in
|
|
135
|
+
self?.submittedRequests.removeValue(forKey: id)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func removeAllRequests() {
|
|
140
|
+
queue.async { [weak self] in
|
|
141
|
+
self?.submittedRequests.removeAll()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private func syncCancellables() -> Set<AnyCancellable> {
|
|
146
|
+
cancellablesLock.sync {
|
|
147
|
+
return cancellables
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// MARK: Session Management
|
|
152
|
+
|
|
153
|
+
@discardableResult
|
|
154
|
+
/// Connect to MetaMask mobile wallet. This method must be called first and once, to establish a connection before any requests can be made
|
|
155
|
+
/// - Returns: A Combine publisher that will emit a connection result or error once a response is received
|
|
156
|
+
func connect() -> EthereumPublisher? {
|
|
157
|
+
commClient.connect(with: nil)
|
|
158
|
+
connected = true
|
|
159
|
+
|
|
160
|
+
if commClient is SocketClient {
|
|
161
|
+
return requestAccounts()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let submittedRequest = SubmittedRequest(method: "")
|
|
165
|
+
addRequest(submittedRequest, id: Ethereum.CONNECTION_ID)
|
|
166
|
+
let publisher = getRequest(id: Ethereum.CONNECTION_ID)?.publisher
|
|
167
|
+
|
|
168
|
+
return publisher
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func performAsyncOperation<T>(_ publisher: EthereumPublisher?, defaultValue: T) async -> Result<T, RequestError> {
|
|
172
|
+
guard let publisher = publisher else {
|
|
173
|
+
return .failure(.genericError)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return await withCheckedContinuation { continuation in
|
|
177
|
+
let cancellable = publisher
|
|
178
|
+
.tryMap { output in
|
|
179
|
+
if let resultArray = output as? [Any?] {
|
|
180
|
+
let resultItems = resultArray
|
|
181
|
+
.filter({ !($0 is NSNull) })
|
|
182
|
+
.compactMap({ $0 })
|
|
183
|
+
guard let result = resultItems as? T else {
|
|
184
|
+
return defaultValue
|
|
185
|
+
}
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
188
|
+
guard let result = output as? T else {
|
|
189
|
+
return defaultValue
|
|
190
|
+
}
|
|
191
|
+
return result
|
|
192
|
+
}
|
|
193
|
+
.mapError { error in
|
|
194
|
+
error as? RequestError ?? RequestError.responseError
|
|
195
|
+
}
|
|
196
|
+
.sink(receiveCompletion: { completion in
|
|
197
|
+
switch completion {
|
|
198
|
+
case .finished:
|
|
199
|
+
break
|
|
200
|
+
case .failure(let error):
|
|
201
|
+
continuation.resume(returning: .failure(error))
|
|
202
|
+
}
|
|
203
|
+
}, receiveValue: { result in
|
|
204
|
+
continuation.resume(returning: .success(result))
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
cancellablesLock.sync {
|
|
208
|
+
cancellables.insert(cancellable)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func request(_ req: any RPCRequest) async -> Result<String, RequestError> {
|
|
214
|
+
let publisher = performRequest(req)
|
|
215
|
+
return await performAsyncOperation(publisher, defaultValue: String()) as Result<String, RequestError>
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func request(_ req: any RPCRequest) async -> Result<[String], RequestError> {
|
|
219
|
+
let publisher = performRequest(req)
|
|
220
|
+
return await performAsyncOperation(publisher, defaultValue: [String]()) as Result<[String], RequestError>
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@discardableResult
|
|
224
|
+
func connect() async -> Result<[String], RequestError> {
|
|
225
|
+
await performAsyncOperation(connect(), defaultValue: []) as Result<[String], RequestError>
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func connectAndSign(message: String) -> EthereumPublisher? {
|
|
229
|
+
let connectSignRequest = EthereumRequest(
|
|
230
|
+
method: .metaMaskConnectSign,
|
|
231
|
+
params: [message]
|
|
232
|
+
)
|
|
233
|
+
connected = true
|
|
234
|
+
|
|
235
|
+
let requestJson = connectSignRequest.toJsonString() ?? ""
|
|
236
|
+
|
|
237
|
+
if commClient is SocketClient {
|
|
238
|
+
commClient.connect(with: requestJson)
|
|
239
|
+
return performRequest(connectSignRequest)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let submittedRequest = SubmittedRequest(method: connectSignRequest.method)
|
|
243
|
+
addRequest(submittedRequest, id: connectSignRequest.id)
|
|
244
|
+
let publisher = getRequest(id: connectSignRequest.id)?.publisher
|
|
245
|
+
|
|
246
|
+
commClient.connect(with: requestJson)
|
|
247
|
+
|
|
248
|
+
return publisher
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func connectAndSign(message: String) async -> Result<String, RequestError> {
|
|
252
|
+
await performAsyncOperation(connectAndSign(message: message), defaultValue: String()) as Result<String, RequestError>
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func connectWith<T: CodableData>(_ req: EthereumRequest<T>) -> EthereumPublisher? {
|
|
256
|
+
let params: [EthereumRequest] = [req]
|
|
257
|
+
let connectWithRequest = EthereumRequest(
|
|
258
|
+
method: EthereumMethod.metamaskConnectWith.rawValue,
|
|
259
|
+
params: params
|
|
260
|
+
)
|
|
261
|
+
connected = true
|
|
262
|
+
|
|
263
|
+
switch transport {
|
|
264
|
+
case .socket:
|
|
265
|
+
if let paramsData = req.params as? Data {
|
|
266
|
+
let reqJson = String(data: paramsData, encoding: .utf8)?.trimEscapingChars() ?? ""
|
|
267
|
+
let requestItem: EthereumRequest = EthereumRequest(
|
|
268
|
+
id: req.id,
|
|
269
|
+
method: req.method,
|
|
270
|
+
params: reqJson
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
let connectWithParams = [requestItem]
|
|
274
|
+
let connectRequest = EthereumRequest(
|
|
275
|
+
id: connectWithRequest.id,
|
|
276
|
+
method: connectWithRequest.method,
|
|
277
|
+
params: connectWithParams
|
|
278
|
+
)
|
|
279
|
+
commClient.connect(with: connectRequest.toJsonString())
|
|
280
|
+
return performRequest(connectRequest)
|
|
281
|
+
} else {
|
|
282
|
+
commClient.connect(with: connectWithRequest.toJsonString())
|
|
283
|
+
return performRequest(connectWithRequest)
|
|
284
|
+
}
|
|
285
|
+
case .deeplinking:
|
|
286
|
+
let submittedRequest = SubmittedRequest(method: connectWithRequest.method)
|
|
287
|
+
addRequest(submittedRequest, id: connectWithRequest.id)
|
|
288
|
+
let publisher = getRequest(id: connectWithRequest.id)?.publisher
|
|
289
|
+
|
|
290
|
+
if let paramsData = req.params as? Data {
|
|
291
|
+
do {
|
|
292
|
+
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])
|
|
293
|
+
|
|
294
|
+
let requestDict: [String: Any] = [
|
|
295
|
+
"id": req.id,
|
|
296
|
+
"method": req.method,
|
|
297
|
+
"params": params
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
let jsonData = try JSONSerialization.data(withJSONObject: requestDict)
|
|
301
|
+
let jsonParams = try JSONSerialization.jsonObject(with: jsonData, options: [])
|
|
302
|
+
|
|
303
|
+
let connectWithParams = [jsonParams]
|
|
304
|
+
|
|
305
|
+
let connectWithDict: [String: Any] = [
|
|
306
|
+
"id": connectWithRequest.id,
|
|
307
|
+
"method": connectWithRequest.method,
|
|
308
|
+
"params": connectWithParams
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
let connectWithJson = json(from: connectWithDict) ?? ""
|
|
312
|
+
|
|
313
|
+
commClient.connect(with: connectWithJson)
|
|
314
|
+
} catch {
|
|
315
|
+
Logging.error("Ethereum:: error: \(error.localizedDescription)")
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
let requestJson = connectWithRequest.toJsonString() ?? ""
|
|
319
|
+
commClient.connect(with: requestJson)
|
|
320
|
+
}
|
|
321
|
+
return publisher
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
func connectWith<T: CodableData>(_ req: EthereumRequest<T>) async -> Result<String, RequestError> {
|
|
326
|
+
return await performAsyncOperation(connectWith(req), defaultValue: String()) as Result<String, RequestError>
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// MARK: Convenience Methods
|
|
330
|
+
|
|
331
|
+
func getChainId() async -> Result<String, RequestError> {
|
|
332
|
+
await ethereumRequest(method: .ethChainId, params: [String]())
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
func getEthAccounts() async -> Result<[String], RequestError> {
|
|
336
|
+
await ethereumRequest(method: .ethAccounts, params: [String]())
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
func getEthGasPrice() async -> Result<String, RequestError> {
|
|
340
|
+
await ethereumRequest(method: .ethGasPrice)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func getEthBalance(address: String, block: String) async -> Result<String, RequestError> {
|
|
344
|
+
await ethereumRequest(method: .ethGetBalance, params: [address, block])
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
func getEthBlockNumber() async -> Result<String, RequestError> {
|
|
348
|
+
await ethereumRequest(method: .ethBlockNumber)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
func getEthEstimateGas() async -> Result<String, RequestError> {
|
|
352
|
+
await ethereumRequest(method: .ethEstimateGas)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
func getWeb3ClientVersion() async -> Result<String, RequestError> {
|
|
356
|
+
await ethereumRequest(method: .web3ClientVersion, params: [String]())
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
func personalSign(message: String, address: String) async -> Result<String, RequestError> {
|
|
360
|
+
await ethereumRequest(method: .personalSign, params: [address, message])
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func signTypedDataV4(typedData: String, address: String) async -> Result<String, RequestError> {
|
|
364
|
+
await ethereumRequest(method: .ethSignTypedDataV4, params: [address, typedData])
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
func sendTransaction(from: String, to: String, value: String) async -> Result<String, RequestError> {
|
|
368
|
+
await ethereumRequest(method: .ethSendTransaction, params: [
|
|
369
|
+
[
|
|
370
|
+
"from": from,
|
|
371
|
+
"to": to,
|
|
372
|
+
"value": value
|
|
373
|
+
]
|
|
374
|
+
])
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func sendRawTransaction(signedTransaction: String) async -> Result<String, RequestError> {
|
|
378
|
+
await ethereumRequest(method: .ethSendRawTransaction, params: [signedTransaction])
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func getBlockTransactionCountByNumber(blockNumber: String) async -> Result<String, RequestError> {
|
|
382
|
+
await ethereumRequest(method: .ethGetBlockTransactionCountByNumber, params: [blockNumber])
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
func getBlockTransactionCountByHash(blockHash: String) async -> Result<String, RequestError> {
|
|
386
|
+
await ethereumRequest(method: .ethGetBlockTransactionCountByHash, params: [blockHash])
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
func getTransactionCount(address: String, tagOrblockNumber: String) async -> Result<String, RequestError> {
|
|
390
|
+
await ethereumRequest(method: .ethGetTransactionCount, params: [address, tagOrblockNumber])
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
func addEthereumChain(chainId: String,
|
|
394
|
+
chainName: String,
|
|
395
|
+
rpcUrls: [String],
|
|
396
|
+
iconUrls: [String]?,
|
|
397
|
+
blockExplorerUrls: [String]?,
|
|
398
|
+
nativeCurrency: NativeCurrency) async -> Result<String, RequestError> {
|
|
399
|
+
|
|
400
|
+
return await ethereumRequest(method: .addEthereumChain, params: [
|
|
401
|
+
AddChainParameters(
|
|
402
|
+
chainId: chainId,
|
|
403
|
+
chainName: chainName,
|
|
404
|
+
rpcUrls: rpcUrls,
|
|
405
|
+
iconUrls: iconUrls,
|
|
406
|
+
blockExplorerUrls: blockExplorerUrls,
|
|
407
|
+
nativeCurrency: nativeCurrency
|
|
408
|
+
)
|
|
409
|
+
])
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
func switchEthereumChain(chainId: String) async -> Result<String, RequestError> {
|
|
413
|
+
await ethereumRequest(method: .switchEthereumChain, params: [
|
|
414
|
+
["chainId": chainId]
|
|
415
|
+
])
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private func ethereumRequest<T: CodableData>(method: EthereumMethod, params: T = "") async -> Result<String, RequestError> {
|
|
419
|
+
let ethRequest = EthereumRequest(method: method, params: params)
|
|
420
|
+
return await request(ethRequest)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private func ethereumRequest<T: CodableData>(method: EthereumMethod, params: T = "") async -> Result<[String], RequestError> {
|
|
424
|
+
let ethRequest = EthereumRequest(method: method, params: params)
|
|
425
|
+
return await request(ethRequest)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/// Disconnect dapp
|
|
429
|
+
func disconnect() {
|
|
430
|
+
connected = false
|
|
431
|
+
commClient.disconnect()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
func clearSession() {
|
|
435
|
+
updateChainId("")
|
|
436
|
+
updateAccount("")
|
|
437
|
+
store.deleteData(for: ACCOUNT_KEY)
|
|
438
|
+
store.deleteData(for: CHAINID_KEY)
|
|
439
|
+
connected = false
|
|
440
|
+
commClient.clearSession()
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
func terminateConnection() {
|
|
444
|
+
let error = RequestError(from: ["message": "The connection request has been rejected"])
|
|
445
|
+
getAllRequests().forEach { key, _ in
|
|
446
|
+
getRequest(id: key)?.error(error)
|
|
447
|
+
}
|
|
448
|
+
removeAllRequests()
|
|
449
|
+
clearSession()
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// MARK: Request Sending
|
|
453
|
+
|
|
454
|
+
func sendRequest(_ request: any RPCRequest) {
|
|
455
|
+
switch transport {
|
|
456
|
+
case .socket:
|
|
457
|
+
if let paramsData = request.params as? Data {
|
|
458
|
+
do {
|
|
459
|
+
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])
|
|
460
|
+
|
|
461
|
+
let requestDict: [String: Any] = [
|
|
462
|
+
"id": request.id,
|
|
463
|
+
"method": request.method,
|
|
464
|
+
"params": params
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
let requestJson = json(from: requestDict) ?? ""
|
|
468
|
+
|
|
469
|
+
commClient.sendMessage(requestJson, encrypt: true, options: [:])
|
|
470
|
+
} catch {
|
|
471
|
+
Logging.error("Ethereum:: error: \(error.localizedDescription)")
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
commClient.sendMessage(request, encrypt: true, options: [:])
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
let authorise = EthereumMethod.requiresAuthorisation(request.methodType)
|
|
478
|
+
let skipAuthorisation = request.methodType == .ethRequestAccounts && !account.isEmpty
|
|
479
|
+
|
|
480
|
+
if authorise && !skipAuthorisation {
|
|
481
|
+
commClient.requestAuthorisation()
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
case .deeplinking:
|
|
485
|
+
if let paramsData = request.params as? Data {
|
|
486
|
+
do {
|
|
487
|
+
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])
|
|
488
|
+
|
|
489
|
+
let requestDict: [String: Any] = [
|
|
490
|
+
"id": request.id,
|
|
491
|
+
"method": request.method,
|
|
492
|
+
"params": params
|
|
493
|
+
]
|
|
494
|
+
|
|
495
|
+
let requestJson = json(from: requestDict) ?? ""
|
|
496
|
+
|
|
497
|
+
commClient.sendMessage(requestJson, encrypt: true, options: ["account": account, "chainId": chainId])
|
|
498
|
+
} catch {
|
|
499
|
+
Logging.error("Ethereum:: error: \(error.localizedDescription)")
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
guard let requestJson = request.toJsonString() else {
|
|
504
|
+
Logging.error("Ethereum:: could not convert request to JSON: \(request)")
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
commClient.sendMessage(requestJson, encrypt: true, options: ["account": account, "chainId": chainId])
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
@discardableResult
|
|
514
|
+
private func requestAccounts() -> EthereumPublisher? {
|
|
515
|
+
let requestAccountsRequest = EthereumRequest(
|
|
516
|
+
id: Ethereum.CONNECTION_ID,
|
|
517
|
+
method: .ethRequestAccounts
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
let submittedRequest = SubmittedRequest(method: requestAccountsRequest.method)
|
|
521
|
+
addRequest(submittedRequest, id: requestAccountsRequest.id)
|
|
522
|
+
let publisher = getRequest(id: requestAccountsRequest.id)?.publisher
|
|
523
|
+
|
|
524
|
+
commClient.addRequest { [weak self] in
|
|
525
|
+
self?.sendRequest(requestAccountsRequest)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return publisher
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
@discardableResult
|
|
532
|
+
/// Performs and Ethereum remote procedural call (RPC)
|
|
533
|
+
/// - Parameter request: The RPC request. It's `parameters` need to conform to `CodableData`
|
|
534
|
+
/// - Returns: A Combine publisher that will emit a result or error once a response is received
|
|
535
|
+
func performRequest(_ request: any RPCRequest) -> EthereumPublisher? {
|
|
536
|
+
let isConnectMethod = EthereumMethod.isConnectMethod(request.methodType)
|
|
537
|
+
|
|
538
|
+
if !connected && !isConnectMethod && account.isEmpty {
|
|
539
|
+
if request.methodType == .ethRequestAccounts {
|
|
540
|
+
commClient.connect(with: nil)
|
|
541
|
+
connected = true
|
|
542
|
+
return requestAccounts()
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return RequestError.failWithError(.connectError)
|
|
546
|
+
} else {
|
|
547
|
+
let id = request.id
|
|
548
|
+
let submittedRequest = SubmittedRequest(method: request.method)
|
|
549
|
+
addRequest(submittedRequest, id: id)
|
|
550
|
+
|
|
551
|
+
let publisher = getRequest(id: id)?.publisher
|
|
552
|
+
|
|
553
|
+
if connected || !account.isEmpty {
|
|
554
|
+
connected = true
|
|
555
|
+
sendRequest(request)
|
|
556
|
+
} else {
|
|
557
|
+
commClient.connect(with: nil)
|
|
558
|
+
connected = true
|
|
559
|
+
commClient.addRequest { [weak self] in
|
|
560
|
+
self?.sendRequest(request)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return publisher
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private func isRequestParamData<T: CodableData>(_ request: EthereumRequest<T>?) -> Bool {
|
|
568
|
+
if let content = request?.params as? [Any], !content.isEmpty {
|
|
569
|
+
return content.first is Data
|
|
570
|
+
}
|
|
571
|
+
return request?.params is Data
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
func batchRequest<T: CodableData>(_ requests: [EthereumRequest<T>]) async -> Result<[String], RequestError> {
|
|
575
|
+
if (isRequestParamData(requests.first)) {
|
|
576
|
+
var requestDicts: [[String: Any]] = []
|
|
577
|
+
|
|
578
|
+
for request in requests {
|
|
579
|
+
if let paramData = request.params as? Data {
|
|
580
|
+
do {
|
|
581
|
+
let requestParams = try JSONSerialization.jsonObject(with: paramData, options: [])
|
|
582
|
+
|
|
583
|
+
let dict: [String: Any] = [
|
|
584
|
+
"id": request.id,
|
|
585
|
+
"method": request.method,
|
|
586
|
+
"params": requestParams
|
|
587
|
+
]
|
|
588
|
+
requestDicts.append(dict)
|
|
589
|
+
} catch {
|
|
590
|
+
Logging.error("Ethereum:: error: \(error.localizedDescription)")
|
|
591
|
+
return .failure(RequestError(from: ["message": error.localizedDescription]))
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
do {
|
|
597
|
+
let jsonData = try JSONSerialization.data(withJSONObject: requestDicts)
|
|
598
|
+
let batchReq = EthereumRequest(
|
|
599
|
+
method: EthereumMethod.metamaskBatch.rawValue,
|
|
600
|
+
params: jsonData)
|
|
601
|
+
|
|
602
|
+
return await performAsyncOperation(performRequest(batchReq), defaultValue: [String]()) as Result<[String], RequestError>
|
|
603
|
+
} catch {
|
|
604
|
+
Logging.error("Ethereum:: error: \(error.localizedDescription)")
|
|
605
|
+
return .failure(RequestError(from: ["message": error.localizedDescription]))
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
let batchRequest = EthereumRequest(
|
|
609
|
+
method: EthereumMethod.metamaskBatch.rawValue,
|
|
610
|
+
params: requests)
|
|
611
|
+
|
|
612
|
+
return await performAsyncOperation(performRequest(batchRequest), defaultValue: [String]()) as Result<[String], RequestError>
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// MARK: Request Receiving
|
|
617
|
+
private func updateChainId(_ id: String) {
|
|
618
|
+
chainId = id
|
|
619
|
+
delegate?.chainIdChanged(id)
|
|
620
|
+
|
|
621
|
+
guard !id.isEmpty else { return }
|
|
622
|
+
store.save(string: id, key: CHAINID_KEY)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private func updateAccount(_ account: String) {
|
|
626
|
+
self.account = account
|
|
627
|
+
delegate?.accountChanged(account)
|
|
628
|
+
|
|
629
|
+
guard !account.isEmpty else { return }
|
|
630
|
+
store.save(string: account, key: ACCOUNT_KEY)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
func sendResult(_ result: Any, id: String) {
|
|
634
|
+
getRequest(id: id)?.send(result)
|
|
635
|
+
removeRequest(id: id)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
func sendError(_ error: RequestError, id: String) {
|
|
639
|
+
getRequest(id: id)?.error(error)
|
|
640
|
+
removeRequest(id: id)
|
|
641
|
+
|
|
642
|
+
if error.codeType == .unauthorisedRequest {
|
|
643
|
+
clearSession()
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
func handleMessage(_ message: [String: Any]) {
|
|
648
|
+
if let id = message["id"] {
|
|
649
|
+
if let identifier: Int64 = id as? Int64 {
|
|
650
|
+
let id: String = String(identifier)
|
|
651
|
+
receiveResponse(message, id: id)
|
|
652
|
+
} else if let identifier: String = id as? String {
|
|
653
|
+
receiveResponse(message, id: identifier)
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
receiveEvent(message)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
func receiveResponse(_ data: [String: Any], id: String) {
|
|
661
|
+
guard let request = getRequest(id: id) else { return }
|
|
662
|
+
|
|
663
|
+
if let error = data["error"] as? [String: Any] {
|
|
664
|
+
let requestError = RequestError(from: error)
|
|
665
|
+
sendError(requestError, id: id)
|
|
666
|
+
|
|
667
|
+
let accounts = data["accounts"] as? [String] ?? []
|
|
668
|
+
|
|
669
|
+
if let account = accounts.first {
|
|
670
|
+
updateAccount(account)
|
|
671
|
+
sendResult(account, id: id)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if let chainId = data["chainId"] as? String {
|
|
675
|
+
updateChainId(chainId)
|
|
676
|
+
sendResult(chainId, id: id)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
guard
|
|
683
|
+
let method = EthereumMethod(rawValue: request.method),
|
|
684
|
+
EthereumMethod.isResultMethod(method) else {
|
|
685
|
+
if let result = data["result"] {
|
|
686
|
+
sendResult(result, id: id)
|
|
687
|
+
} else {
|
|
688
|
+
sendResult(data, id: id)
|
|
689
|
+
}
|
|
690
|
+
return
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
switch method {
|
|
694
|
+
case .getMetamaskProviderState:
|
|
695
|
+
let result: [String: Any] = data["result"] as? [String: Any] ?? [:]
|
|
696
|
+
let accounts = result["accounts"] as? [String] ?? []
|
|
697
|
+
|
|
698
|
+
if let account = accounts.first {
|
|
699
|
+
updateAccount(account)
|
|
700
|
+
sendResult(account, id: id)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if let chainId = result["chainId"] as? String {
|
|
704
|
+
updateChainId(chainId)
|
|
705
|
+
sendResult(chainId, id: id)
|
|
706
|
+
}
|
|
707
|
+
case .ethRequestAccounts:
|
|
708
|
+
let result: [String] = data["result"] as? [String] ?? []
|
|
709
|
+
if let account = result.first {
|
|
710
|
+
updateAccount(account)
|
|
711
|
+
sendResult(result, id: id)
|
|
712
|
+
} else {
|
|
713
|
+
Logging.error("Ethereum:: Request accounts failure")
|
|
714
|
+
}
|
|
715
|
+
case .ethChainId:
|
|
716
|
+
if let result: String = data["result"] as? String {
|
|
717
|
+
updateChainId(result)
|
|
718
|
+
sendResult(result, id: id)
|
|
719
|
+
}
|
|
720
|
+
case .ethSignTypedDataV4,
|
|
721
|
+
.ethSignTypedDataV3,
|
|
722
|
+
.ethSendTransaction:
|
|
723
|
+
if let result: String = data["result"] as? String {
|
|
724
|
+
sendResult(result, id: id)
|
|
725
|
+
} else {
|
|
726
|
+
Logging.error("Unexpected response \(data)")
|
|
727
|
+
}
|
|
728
|
+
case .metamaskBatch:
|
|
729
|
+
if
|
|
730
|
+
id == Ethereum.BATCH_CONNECTION_ID,
|
|
731
|
+
let result = data["result"] as? [Any],
|
|
732
|
+
result.count == 2,
|
|
733
|
+
let accounts = result.first as? [String],
|
|
734
|
+
let chainId = result[1] as? String {
|
|
735
|
+
|
|
736
|
+
if let account = accounts.first {
|
|
737
|
+
updateAccount(account)
|
|
738
|
+
}
|
|
739
|
+
updateChainId(chainId)
|
|
740
|
+
} else {
|
|
741
|
+
if
|
|
742
|
+
let accounts = data["accounts"] as? [String],
|
|
743
|
+
let account = accounts.first {
|
|
744
|
+
updateAccount(account)
|
|
745
|
+
}
|
|
746
|
+
if let chainId = data["chainId"] as? String {
|
|
747
|
+
updateChainId(chainId)
|
|
748
|
+
}
|
|
749
|
+
if let result = data["result"] {
|
|
750
|
+
sendResult(result, id: id)
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
default:
|
|
754
|
+
if let chainId = data["chainId"] as? String {
|
|
755
|
+
updateChainId(chainId)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if
|
|
759
|
+
let accounts = data["accounts"] as? [String],
|
|
760
|
+
let selectedAddress = accounts.first {
|
|
761
|
+
updateAccount(selectedAddress)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if let result = data["result"] {
|
|
765
|
+
sendResult(result, id: id)
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
func receiveEvent(_ event: [String: Any]) {
|
|
771
|
+
if let error = event["error"] as? [String: Any] {
|
|
772
|
+
Logging.error("Ethereum:: receive error: \(error)")
|
|
773
|
+
let requestError = RequestError(from: error)
|
|
774
|
+
sendError(requestError, id: Ethereum.CONNECTION_ID)
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
guard
|
|
778
|
+
let method = event["method"] as? String,
|
|
779
|
+
let ethMethod = EthereumMethod(rawValue: method)
|
|
780
|
+
else {
|
|
781
|
+
if let chainId = event["chainId"] as? String {
|
|
782
|
+
updateChainId(chainId)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if
|
|
786
|
+
let accounts = event["accounts"] as? [String],
|
|
787
|
+
let selectedAddress = accounts.first {
|
|
788
|
+
updateAccount(selectedAddress)
|
|
789
|
+
sendResult(accounts, id: Ethereum.CONNECTION_ID)
|
|
790
|
+
}
|
|
791
|
+
return
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
switch ethMethod {
|
|
795
|
+
case .metaMaskAccountsChanged:
|
|
796
|
+
let accounts: [String] = event["params"] as? [String] ?? []
|
|
797
|
+
if let account = accounts.first {
|
|
798
|
+
updateAccount(account)
|
|
799
|
+
}
|
|
800
|
+
case .metaMaskChainChanged:
|
|
801
|
+
let params: [String: Any] = event["params"] as? [String: Any] ?? [:]
|
|
802
|
+
|
|
803
|
+
if let chainId = params["chainId"] as? String {
|
|
804
|
+
updateChainId(chainId)
|
|
805
|
+
}
|
|
806
|
+
default:
|
|
807
|
+
Logging.error("Unhandled case: \(event)")
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|