From 37b398e6fc7025aec308ff914d65e27983fd8f04 Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 9 Oct 2020 12:52:08 +0300 Subject: [PATCH 01/15] Fix --- Sources/PublicInterface/Client.swift | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index 7bfed1fa2..9cdecee95 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -216,8 +216,14 @@ public class Client: WalletConnect { override func onTextReceive(_ text: String, from url: WCURL) { if let response = try? communicator.response(from: text, url: url) { log(response) - if let completion = responses.find(requestID: response.internalID) { - completion(response) + // Rainbow (at least) application send connect response with random id + // because of that correlated request can't be found by id. Here is crutch + let isConnectResponse = (try? response.result(as: Session.WalletInfo.self)) != nil + let completion = responses.find(requestID: response.internalID, isConnectResponse: isConnectResponse) + completion?(response) + if isConnectResponse { + responses.removeAll() + } else { responses.remove(requestID: response.internalID) } } else if let request = try? communicator.request(from: text, url: url) { @@ -287,11 +293,16 @@ public class Client: WalletConnect { } } - func find(requestID: JSONRPC_2_0.IDType) -> RequestResponse? { + func find(requestID: JSONRPC_2_0.IDType, isConnectResponse: Bool) -> RequestResponse? { var result: RequestResponse? dispatchPrecondition(condition: .notOnQueue(queue)) queue.sync { [unowned self] in - result = self.responses[requestID] + if let response = self.responses[requestID] { + result = response + } + if isConnectResponse, responses.count == 1, let response = responses.first { + result = response.value + } } return result } @@ -303,6 +314,12 @@ public class Client: WalletConnect { } } + func removeAll() { + dispatchPrecondition(condition: .notOnQueue(queue)) + queue.sync { [unowned self] in + self.responses.removeAll() + } + } } /// https://docs.walletconnect.org/json-rpc/ethereum#parameters-3 From 1dd53eea645ab2567ebf2c7b8a405a3d034d2b22 Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 9 Oct 2020 13:02:22 +0300 Subject: [PATCH 02/15] Make WalletInfo.peerMeta optional --- Sources/PublicInterface/Session.swift | 4 ++-- Tests/SessionTests.swift | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/PublicInterface/Session.swift b/Sources/PublicInterface/Session.swift index ff0039bcf..f6874a8fc 100644 --- a/Sources/PublicInterface/Session.swift +++ b/Sources/PublicInterface/Session.swift @@ -54,9 +54,9 @@ public struct Session: Codable { public let accounts: [String] public let chainId: Int public let peerId: String - public let peerMeta: ClientMeta + public let peerMeta: ClientMeta? - public init(approved: Bool, accounts: [String], chainId: Int, peerId: String, peerMeta: ClientMeta) { + public init(approved: Bool, accounts: [String], chainId: Int, peerId: String, peerMeta: ClientMeta?) { self.approved = approved self.accounts = accounts self.chainId = chainId diff --git a/Tests/SessionTests.swift b/Tests/SessionTests.swift index c93105893..3ee4a0947 100644 --- a/Tests/SessionTests.swift +++ b/Tests/SessionTests.swift @@ -29,11 +29,11 @@ class SessionTests: XCTestCase { XCTAssertEqual(session.walletInfo?.accounts, ["0xCF4140193531B8b2d6864cA7486Ff2e18da5cA95"]) XCTAssertEqual(session.walletInfo?.chainId, 4) XCTAssertEqual(session.walletInfo?.peerId, "Gnosis Safe ID") - XCTAssertEqual(session.walletInfo?.peerMeta.name, "Gnosis Safe") - XCTAssertEqual(session.walletInfo?.peerMeta.url, URL(string: "https://safe.gnosis.io")!) - XCTAssertEqual(session.walletInfo?.peerMeta.icons, [URL(string: "https://example.com/1.png")!, + XCTAssertEqual(session.walletInfo?.peerMeta?.name, "Gnosis Safe") + XCTAssertEqual(session.walletInfo?.peerMeta?.url, URL(string: "https://safe.gnosis.io")!) + XCTAssertEqual(session.walletInfo?.peerMeta?.icons, [URL(string: "https://example.com/1.png")!, URL(string: "https://example.com/2.png")!]) - XCTAssertEqual(session.walletInfo?.peerMeta.description, "Secure 2FA Wallet") + XCTAssertEqual(session.walletInfo?.peerMeta?.description, "Secure 2FA Wallet") } func test_creationResponse() throws { From f510bf62cb212d7d584c656636bc58885f85faa6 Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 9 Oct 2020 14:09:40 +0300 Subject: [PATCH 03/15] New delegate method --- Sources/PublicInterface/Client.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index 9cdecee95..c6caba8b0 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -5,6 +5,7 @@ import Foundation public protocol ClientDelegate: class { + func client(_ client: Client, didConnect url: WCURL) func client(_ client: Client, didFailToConnect url: WCURL) func client(_ client: Client, didConnect session: Session) func client(_ client: Client, didDisconnect session: Session) @@ -179,6 +180,7 @@ public class Client: WalletConnect { override func onConnect(to url: WCURL) { LogService.shared.log("WC: client didConnect url: \(url.bridgeURL.absoluteString)") + delegate.client(self, didConnect: url) if let existingSession = communicator.session(by: url) { communicator.subscribe(on: existingSession.dAppInfo.peerId, url: existingSession.url) delegate.client(self, didConnect: existingSession) From 00194dd097f52281015fcee9fc5bf2f76d128e01 Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 30 Oct 2020 20:34:50 +0300 Subject: [PATCH 04/15] Handle session update --- Sources/Internal/UpdateSessionHandler.swift | 2 + Sources/PublicInterface/Client.swift | 51 ++++++++++++++------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Sources/Internal/UpdateSessionHandler.swift b/Sources/Internal/UpdateSessionHandler.swift index 382ba74f1..d1ff1dd29 100644 --- a/Sources/Internal/UpdateSessionHandler.swift +++ b/Sources/Internal/UpdateSessionHandler.swift @@ -32,4 +32,6 @@ class UpdateSessionHandler: RequestHandler { struct SessionInfo: Decodable { var approved: Bool + var accounts: [String]? + var chainId: Int } diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index c6caba8b0..64044c21a 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -9,12 +9,13 @@ public protocol ClientDelegate: class { func client(_ client: Client, didFailToConnect url: WCURL) func client(_ client: Client, didConnect session: Session) func client(_ client: Client, didDisconnect session: Session) + func client(_ client: Client, didUpdate session: Session) } public class Client: WalletConnect { public typealias RequestResponse = (Response) -> Void - private(set) weak var delegate: ClientDelegate! + private(set) weak var delegate: ClientDelegate? private let dAppInfo: Session.DAppInfo private var responses: Responses @@ -180,10 +181,10 @@ public class Client: WalletConnect { override func onConnect(to url: WCURL) { LogService.shared.log("WC: client didConnect url: \(url.bridgeURL.absoluteString)") - delegate.client(self, didConnect: url) + delegate?.client(self, didConnect: url) if let existingSession = communicator.session(by: url) { communicator.subscribe(on: existingSession.dAppInfo.peerId, url: existingSession.url) - delegate.client(self, didConnect: existingSession) + delegate?.client(self, didConnect: existingSession) } else { // establishing new connection, handshake in process communicator.subscribe(on: dAppInfo.peerId, url: url) let request = try! Request(url: url, method: "wc_sessionRequest", params: [dAppInfo], id: UUID().hashValue) @@ -203,15 +204,15 @@ public class Client: WalletConnect { guard walletInfo.approved else { // TODO: handle Error - delegate.client(self, didFailToConnect: response.url) + delegate?.client(self, didFailToConnect: response.url) return } communicator.addSession(session) - delegate.client(self, didConnect: session) + delegate?.client(self, didConnect: session) } catch { // TODO: handle error - delegate.client(self, didFailToConnect: response.url) + delegate?.client(self, didFailToConnect: response.url) } } @@ -236,16 +237,34 @@ public class Client: WalletConnect { private func expectUpdateSessionRequest(_ request: Request) { if request.method == "wc_sessionUpdate" { - guard let approval = sessionApproval(from: request) else { + guard let info = sessionInfo(from: request) else { // TODO: error handling try! send(Response(request: request, error: .invalidJSON)) return } - guard let session = communicator.session(by: request.url), !approval else { return } - do { - try disconnect(from: session) - } catch { // session already disconnected - delegate.client(self, didDisconnect: session) + guard let session = communicator.session(by: request.url) else { return } + if !info.approved { + do { + try disconnect(from: session) + } catch { // session already disconnected + delegate?.client(self, didDisconnect: session) + } + } else { + if let walletInfo = session.walletInfo { + let updatedInfo = Session.WalletInfo( + approved: info.approved, + accounts: info.accounts ?? [], + chainId: info.chainId, + peerId: walletInfo.peerId, + peerMeta: walletInfo.peerMeta + ) + var updatedSesson = session + updatedSesson.walletInfo = updatedInfo + communicator.addSession(updatedSesson) + delegate?.client(self, didUpdate: updatedSesson) + } else { + delegate?.client(self, didUpdate: session) + } } } else { // TODO: error handling @@ -254,10 +273,10 @@ public class Client: WalletConnect { } } - private func sessionApproval(from request: Request) -> Bool? { + private func sessionInfo(from request: Request) -> SessionInfo? { do { let info = try request.parameter(of: SessionInfo.self, at: 0) - return info.approved + return info } catch { LogService.shared.log("WC: incoming approval cannot be parsed: \(error)") return nil @@ -271,11 +290,11 @@ public class Client: WalletConnect { } override func failedToConnect(_ url: WCURL) { - delegate.client(self, didFailToConnect: url) + delegate?.client(self, didFailToConnect: url) } override func didDisconnect(_ session: Session) { - delegate.client(self, didDisconnect: session) + delegate?.client(self, didDisconnect: session) } /// Thread-safe collection of client reponses From 31b3c117495d7a789e74c36f98c2bfcb4eea7130 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 18 Nov 2020 20:48:33 +0300 Subject: [PATCH 05/15] Add scheme --- Sources/PublicInterface/Session.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/PublicInterface/Session.swift b/Sources/PublicInterface/Session.swift index f6874a8fc..d3a7efe74 100644 --- a/Sources/PublicInterface/Session.swift +++ b/Sources/PublicInterface/Session.swift @@ -40,12 +40,14 @@ public struct Session: Codable { public let description: String? public let icons: [URL] public let url: URL + public let scheme: String? - public init(name: String, description: String?, icons: [URL], url: URL) { + public init(name: String, description: String?, icons: [URL], url: URL, scheme: String? ) { self.name = name self.description = description self.icons = icons self.url = url + self.scheme = scheme } } From 3ba17b8fb1d47ce39a0be6421355b16ce9c41e5d Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 24 Feb 2021 17:54:26 +0300 Subject: [PATCH 06/15] Starscream update --- WalletConnectSwift.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletConnectSwift.podspec b/WalletConnectSwift.podspec index 5393c519f..a0af94dfe 100644 --- a/WalletConnectSwift.podspec +++ b/WalletConnectSwift.podspec @@ -17,5 +17,5 @@ Pod::Spec.new do |spec| spec.source_files = "Sources/**/*.swift" spec.requires_arc = true spec.dependency "CryptoSwift", "~> 1.2" - spec.dependency "Starscream", "~> 3.1" + spec.dependency "Starscream", "~> 4.0" end From 654b584fda69dc3baacd47af784d6b84801305e9 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 24 Feb 2021 18:12:02 +0300 Subject: [PATCH 07/15] Starscream update --- Sources/Internal/WebSocketConnection.swift | 45 +++++++++++----------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/Sources/Internal/WebSocketConnection.swift b/Sources/Internal/WebSocketConnection.swift index cbf9ccee9..968b866a8 100644 --- a/Sources/Internal/WebSocketConnection.swift +++ b/Sources/Internal/WebSocketConnection.swift @@ -15,15 +15,15 @@ class WebSocketConnection { private var pingTimer: Timer? // TODO: make injectable on server creation private let pingInterval: TimeInterval = 30 - + private var isConnected = false private var requestSerializer: RequestSerializer = JSONRPCSerializer() private var responseSerializer: ResponseSerializer = JSONRPCSerializer() - + // serial queue for receiving the calls. private let serialCallbackQueue: DispatchQueue var isOpen: Bool { - return socket.isConnected + return isConnected } init(url: WCURL, @@ -35,7 +35,7 @@ class WebSocketConnection { self.onDisconnect = onDisconnect self.onTextReceive = onTextReceive serialCallbackQueue = DispatchQueue(label: "org.walletconnect.swift.connection-\(url.bridgeURL)-\(url.topic)") - socket = WebSocket(url: url.bridgeURL) + socket = WebSocket(request: URLRequest(url: url.bridgeURL)) socket.delegate = self socket.callbackQueue = serialCallbackQueue } @@ -49,7 +49,7 @@ class WebSocketConnection { } func send(_ text: String) { - guard socket.isConnected else { return } + guard isConnected else { return } socket.write(string: text) log(text) } @@ -66,24 +66,23 @@ class WebSocketConnection { } extension WebSocketConnection: WebSocketDelegate { - func websocketDidConnect(socket: WebSocketClient) { - pingTimer = Timer.scheduledTimer(withTimeInterval: pingInterval, repeats: true) { [weak self] _ in - LogService.shared.log("WC: ==> ping") - self?.socket.write(ping: Data()) + func didReceive(event: WebSocketEvent, client: WebSocket) { + switch event { + case .connected: + isConnected = true + pingTimer = Timer.scheduledTimer(withTimeInterval: pingInterval, repeats: true) { [weak self] _ in + LogService.shared.log("WC: ==> ping") + self?.socket.write(ping: Data()) + } + onConnect?() + case .disconnected: + isConnected = false + pingTimer?.invalidate() + onDisconnect?(nil) + case let .text(text): + onTextReceive?(text) + default: + break } - onConnect?() - } - - func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { - pingTimer?.invalidate() - onDisconnect?(error) - } - - func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { - onTextReceive?(text) - } - - func websocketDidReceiveData(socket: WebSocketClient, data: Data) { - // no-op } } From 94be0a316e454b08484357a3a34b0b4dbdad40a2 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 6 Jul 2021 13:20:38 +0300 Subject: [PATCH 08/15] Include optional chainId in DappInfo --- Sources/PublicInterface/Session.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/PublicInterface/Session.swift b/Sources/PublicInterface/Session.swift index d3a7efe74..42568e38d 100644 --- a/Sources/PublicInterface/Session.swift +++ b/Sources/PublicInterface/Session.swift @@ -20,17 +20,20 @@ public struct Session: Codable { public struct DAppInfo: Codable, Equatable { public let peerId: String public let peerMeta: ClientMeta + public let chainId: Int? public let approved: Bool? - public init(peerId: String, peerMeta: ClientMeta, approved: Bool? = nil) { + public init(peerId: String, peerMeta: ClientMeta, chainId: Int? = nil, approved: Bool? = nil) { self.peerId = peerId self.peerMeta = peerMeta + self.chainId = chainId self.approved = approved } func with(approved: Bool) -> DAppInfo { return DAppInfo(peerId: self.peerId, peerMeta: self.peerMeta, + chainId: self.chainId, approved: approved) } } From 6e4cb7fcfa4f06c558c9a36a8e6433c954d2af62 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 6 Jul 2021 13:25:32 +0300 Subject: [PATCH 09/15] Revert "Starscream update" This reverts commit 3ba17b8fb1d47ce39a0be6421355b16ce9c41e5d. --- WalletConnectSwift.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletConnectSwift.podspec b/WalletConnectSwift.podspec index a0af94dfe..5393c519f 100644 --- a/WalletConnectSwift.podspec +++ b/WalletConnectSwift.podspec @@ -17,5 +17,5 @@ Pod::Spec.new do |spec| spec.source_files = "Sources/**/*.swift" spec.requires_arc = true spec.dependency "CryptoSwift", "~> 1.2" - spec.dependency "Starscream", "~> 4.0" + spec.dependency "Starscream", "~> 3.1" end From 3611c9e4a03ab876f8a7a3b68d9fa0090a1bec54 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 6 Jul 2021 13:26:23 +0300 Subject: [PATCH 10/15] Revert "Starscream update" This reverts commit 654b584fda69dc3baacd47af784d6b84801305e9. --- Sources/Internal/WebSocketConnection.swift | 45 +++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Sources/Internal/WebSocketConnection.swift b/Sources/Internal/WebSocketConnection.swift index 968b866a8..cbf9ccee9 100644 --- a/Sources/Internal/WebSocketConnection.swift +++ b/Sources/Internal/WebSocketConnection.swift @@ -15,15 +15,15 @@ class WebSocketConnection { private var pingTimer: Timer? // TODO: make injectable on server creation private let pingInterval: TimeInterval = 30 - private var isConnected = false + private var requestSerializer: RequestSerializer = JSONRPCSerializer() private var responseSerializer: ResponseSerializer = JSONRPCSerializer() - + // serial queue for receiving the calls. private let serialCallbackQueue: DispatchQueue var isOpen: Bool { - return isConnected + return socket.isConnected } init(url: WCURL, @@ -35,7 +35,7 @@ class WebSocketConnection { self.onDisconnect = onDisconnect self.onTextReceive = onTextReceive serialCallbackQueue = DispatchQueue(label: "org.walletconnect.swift.connection-\(url.bridgeURL)-\(url.topic)") - socket = WebSocket(request: URLRequest(url: url.bridgeURL)) + socket = WebSocket(url: url.bridgeURL) socket.delegate = self socket.callbackQueue = serialCallbackQueue } @@ -49,7 +49,7 @@ class WebSocketConnection { } func send(_ text: String) { - guard isConnected else { return } + guard socket.isConnected else { return } socket.write(string: text) log(text) } @@ -66,23 +66,24 @@ class WebSocketConnection { } extension WebSocketConnection: WebSocketDelegate { - func didReceive(event: WebSocketEvent, client: WebSocket) { - switch event { - case .connected: - isConnected = true - pingTimer = Timer.scheduledTimer(withTimeInterval: pingInterval, repeats: true) { [weak self] _ in - LogService.shared.log("WC: ==> ping") - self?.socket.write(ping: Data()) - } - onConnect?() - case .disconnected: - isConnected = false - pingTimer?.invalidate() - onDisconnect?(nil) - case let .text(text): - onTextReceive?(text) - default: - break + func websocketDidConnect(socket: WebSocketClient) { + pingTimer = Timer.scheduledTimer(withTimeInterval: pingInterval, repeats: true) { [weak self] _ in + LogService.shared.log("WC: ==> ping") + self?.socket.write(ping: Data()) } + onConnect?() + } + + func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { + pingTimer?.invalidate() + onDisconnect?(error) + } + + func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { + onTextReceive?(text) + } + + func websocketDidReceiveData(socket: WebSocketClient, data: Data) { + // no-op } } From 3ab80d1334da8734bfcc48f97f0df4bb9e45f6c0 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 6 Jul 2021 19:24:23 +0300 Subject: [PATCH 11/15] Add chain id to transaction --- Sources/PublicInterface/Client.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index 64044c21a..7bca53c74 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -352,14 +352,16 @@ public class Client: WalletConnect { var gasPrice: String? var value: String? var nonce: String? - + var chainId: String? + public init(from: String, to: String?, data: String, gas: String?, gasPrice: String?, value: String?, - nonce: String?) { + nonce: String?, + chainId: String?) { self.from = from self.to = to self.data = data @@ -367,6 +369,7 @@ public class Client: WalletConnect { self.gasPrice = gasPrice self.value = value self.nonce = nonce + self.chainId = chainId } } } From 2b51dca34497fdb37862e4dec643fbb145d7835b Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 6 Jul 2021 19:43:20 +0300 Subject: [PATCH 12/15] Revert "Add chain id to transaction" This reverts commit 3ab80d1334da8734bfcc48f97f0df4bb9e45f6c0. --- Sources/PublicInterface/Client.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index 7bca53c74..64044c21a 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -352,16 +352,14 @@ public class Client: WalletConnect { var gasPrice: String? var value: String? var nonce: String? - var chainId: String? - + public init(from: String, to: String?, data: String, gas: String?, gasPrice: String?, value: String?, - nonce: String?, - chainId: String?) { + nonce: String?) { self.from = from self.to = to self.data = data @@ -369,7 +367,6 @@ public class Client: WalletConnect { self.gasPrice = gasPrice self.value = value self.nonce = nonce - self.chainId = chainId } } } From df2dccb7f18182f621f3caadc3ad3298d4e0d48f Mon Sep 17 00:00:00 2001 From: Andrey Scherbovich Date: Fri, 9 Jul 2021 12:56:31 +0200 Subject: [PATCH 13/15] fix unit tests --- Sources/Internal/UpdateSessionHandler.swift | 2 +- Sources/PublicInterface/Session.swift | 2 +- Tests/ClientTests.swift | 16 ++++++++++++++++ Tests/SessionTests.swift | 8 ++++---- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Sources/Internal/UpdateSessionHandler.swift b/Sources/Internal/UpdateSessionHandler.swift index cd27701df..9775eb889 100644 --- a/Sources/Internal/UpdateSessionHandler.swift +++ b/Sources/Internal/UpdateSessionHandler.swift @@ -4,7 +4,7 @@ import Foundation -protocol UpdateSessionHandlerDelegate: class { +protocol UpdateSessionHandlerDelegate: AnyObject { func handler(_ handler: UpdateSessionHandler, didUpdateSessionByURL: WCURL, approved: Bool) } diff --git a/Sources/PublicInterface/Session.swift b/Sources/PublicInterface/Session.swift index 34435e182..5d1b07ec0 100644 --- a/Sources/PublicInterface/Session.swift +++ b/Sources/PublicInterface/Session.swift @@ -45,7 +45,7 @@ public struct Session: Codable { public let url: URL public let scheme: String? - public init(name: String, description: String?, icons: [URL], url: URL, scheme: String?) { + public init(name: String, description: String?, icons: [URL], url: URL, scheme: String? = nil) { self.name = name self.description = description self.icons = icons diff --git a/Tests/ClientTests.swift b/Tests/ClientTests.swift index 927336964..fc4cb0415 100644 --- a/Tests/ClientTests.swift +++ b/Tests/ClientTests.swift @@ -79,6 +79,12 @@ class ClientTests: XCTestCase { XCTAssertEqual(communicator.subscribedOn?.url, Session.testSession.url) } + func test_onConnect_callsDelegate() { + XCTAssertNil(delegate.connectedUrl) + client.onConnect(to: WCURL.testURL) + XCTAssertEqual(delegate.connectedUrl, WCURL.testURL) + } + func test_onConnect_whenSessionExists_thenCallsDelegate() { communicator.addSession(Session.testSession) XCTAssertNil(delegate.connectedSession) @@ -104,6 +110,11 @@ class MockClientDelegate: ClientDelegate { didFailToConnect = true } + var connectedUrl: WCURL? + func client(_ client: Client, didConnect url: WCURL) { + connectedUrl = url + } + var connectedSession: Session? func client(_ client: Client, didConnect session: Session) { connectedSession = session @@ -113,4 +124,9 @@ class MockClientDelegate: ClientDelegate { func client(_ client: Client, didDisconnect session: Session) { disconnectedSession = session } + + var didUpdateSession: Session? + func client(_ client: Client, didUpdate session: Session) { + didUpdateSession = session + } } diff --git a/Tests/SessionTests.swift b/Tests/SessionTests.swift index 3ee4a0947..c93105893 100644 --- a/Tests/SessionTests.swift +++ b/Tests/SessionTests.swift @@ -29,11 +29,11 @@ class SessionTests: XCTestCase { XCTAssertEqual(session.walletInfo?.accounts, ["0xCF4140193531B8b2d6864cA7486Ff2e18da5cA95"]) XCTAssertEqual(session.walletInfo?.chainId, 4) XCTAssertEqual(session.walletInfo?.peerId, "Gnosis Safe ID") - XCTAssertEqual(session.walletInfo?.peerMeta?.name, "Gnosis Safe") - XCTAssertEqual(session.walletInfo?.peerMeta?.url, URL(string: "https://safe.gnosis.io")!) - XCTAssertEqual(session.walletInfo?.peerMeta?.icons, [URL(string: "https://example.com/1.png")!, + XCTAssertEqual(session.walletInfo?.peerMeta.name, "Gnosis Safe") + XCTAssertEqual(session.walletInfo?.peerMeta.url, URL(string: "https://safe.gnosis.io")!) + XCTAssertEqual(session.walletInfo?.peerMeta.icons, [URL(string: "https://example.com/1.png")!, URL(string: "https://example.com/2.png")!]) - XCTAssertEqual(session.walletInfo?.peerMeta?.description, "Secure 2FA Wallet") + XCTAssertEqual(session.walletInfo?.peerMeta.description, "Secure 2FA Wallet") } func test_creationResponse() throws { From 2b9693e24a54bb2b15e92958b6a5d1e7b9326a28 Mon Sep 17 00:00:00 2001 From: Andrey Scherbovich Date: Fri, 9 Jul 2021 13:07:05 +0200 Subject: [PATCH 14/15] fix client example app --- ExampleApps/ClientApp/WalletConnect.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ExampleApps/ClientApp/WalletConnect.swift b/ExampleApps/ClientApp/WalletConnect.swift index 009b4e9a3..93a5e47a3 100644 --- a/ExampleApps/ClientApp/WalletConnect.swift +++ b/ExampleApps/ClientApp/WalletConnect.swift @@ -70,6 +70,10 @@ extension WalletConnect: ClientDelegate { delegate.failedToConnect() } + func client(_ client: Client, didConnect url: WCURL) { + // do nothing + } + func client(_ client: Client, didConnect session: Session) { self.session = session let sessionData = try! JSONEncoder().encode(session) @@ -81,4 +85,8 @@ extension WalletConnect: ClientDelegate { UserDefaults.standard.removeObject(forKey: sessionKey) delegate.didDisconnect() } + + func client(_ client: Client, didUpdate session: Session) { + // do nothing + } } From f2eefeb07ed352f884b6246da1915b54e9286df2 Mon Sep 17 00:00:00 2001 From: Andrey Scherbovich Date: Fri, 9 Jul 2021 14:36:32 +0200 Subject: [PATCH 15/15] improve UpdateSessionHandler --- Sources/Internal/Communicator.swift | 10 +++--- Sources/Internal/HandshakeHandler.swift | 2 +- Sources/Internal/UpdateSessionHandler.swift | 11 ++++-- Sources/PublicInterface/Client.swift | 34 +++++++++---------- Sources/PublicInterface/Server.swift | 23 ++++++++++--- Sources/PublicInterface/Session.swift | 2 +- Sources/PublicInterface/WalletConnect.swift | 4 +-- Tests/ClientTests.swift | 10 +++--- Tests/Helpers.swift | 2 +- Tests/WalletConnectTests.swift | 2 +- WalletConnectSwift.xcodeproj/project.pbxproj | 4 ++- .../xcschemes/WalletConnectSwift.xcscheme | 28 +++++++-------- 12 files changed, 74 insertions(+), 58 deletions(-) diff --git a/Sources/Internal/Communicator.swift b/Sources/Internal/Communicator.swift index b948d3f7a..513a20196 100644 --- a/Sources/Internal/Communicator.swift +++ b/Sources/Internal/Communicator.swift @@ -29,8 +29,8 @@ class Communicator { return sessions.find(url: url) } - func addSession(_ session: Session) { - sessions.add(session) + func addOrUpdateSession(_ session: Session) { + sessions.addOrUpdate(session) } func removeSession(by url: WCURL) { @@ -53,8 +53,8 @@ class Communicator { return pendingDisconnectSessions.find(url: url) } - func addPendingDisconnectSession(_ session: Session) { - pendingDisconnectSessions.add(session) + func addOrUpdatePendingDisconnectSession(_ session: Session) { + pendingDisconnectSessions.addOrUpdate(session) } func removePendingDisconnectSession(by url: WCURL) { @@ -107,7 +107,7 @@ class Communicator { self.queue = queue } - func add(_ session: Session) { + func addOrUpdate(_ session: Session) { dispatchPrecondition(condition: .notOnQueue(queue)) queue.sync { [unowned self] in self.sessions[session.url] = session diff --git a/Sources/Internal/HandshakeHandler.swift b/Sources/Internal/HandshakeHandler.swift index 817c7d5c3..a0db0a0df 100644 --- a/Sources/Internal/HandshakeHandler.swift +++ b/Sources/Internal/HandshakeHandler.swift @@ -4,7 +4,7 @@ import Foundation -protocol HandshakeHandlerDelegate: class { +protocol HandshakeHandlerDelegate: AnyObject { // TODO: instead of IDType, use RequestID func handler(_ handler: HandshakeHandler, didReceiveRequestToCreateSession: Session, requestId: RequestID) } diff --git a/Sources/Internal/UpdateSessionHandler.swift b/Sources/Internal/UpdateSessionHandler.swift index 9775eb889..57859c356 100644 --- a/Sources/Internal/UpdateSessionHandler.swift +++ b/Sources/Internal/UpdateSessionHandler.swift @@ -5,7 +5,7 @@ import Foundation protocol UpdateSessionHandlerDelegate: AnyObject { - func handler(_ handler: UpdateSessionHandler, didUpdateSessionByURL: WCURL, approved: Bool) + func handler(_ handler: UpdateSessionHandler, didUpdateSessionByURL: WCURL, sessionInfo: SessionInfo) } class UpdateSessionHandler: RequestHandler { @@ -22,7 +22,7 @@ class UpdateSessionHandler: RequestHandler { func handle(request: Request) { do { let sessionInfo = try request.parameter(of: SessionInfo.self, at: 0) - delegate?.handler(self, didUpdateSessionByURL: request.url, approved: sessionInfo.approved) + delegate?.handler(self, didUpdateSessionByURL: request.url, sessionInfo: sessionInfo) } catch { LogService.shared.log("WC: wrong format of wc_sessionUpdate request: \(error)") // TODO: send error response @@ -30,8 +30,13 @@ class UpdateSessionHandler: RequestHandler { } } +/// https://docs.walletconnect.org/tech-spec#session-update struct SessionInfo: Decodable { var approved: Bool var accounts: [String]? - var chainId: Int + var chainId: Int? +} + +enum ChainID { + static let mainnet = 1 } diff --git a/Sources/PublicInterface/Client.swift b/Sources/PublicInterface/Client.swift index b014f14ae..09ad07854 100644 --- a/Sources/PublicInterface/Client.swift +++ b/Sources/PublicInterface/Client.swift @@ -4,7 +4,7 @@ import Foundation -public protocol ClientDelegate: class { +public protocol ClientDelegate: AnyObject { func client(_ client: Client, didFailToConnect url: WCURL) func client(_ client: Client, didConnect url: WCURL) func client(_ client: Client, didConnect session: Session) @@ -208,7 +208,7 @@ public class Client: WalletConnect { return } - communicator.addSession(session) + communicator.addOrUpdateSession(session) delegate?.client(self, didConnect: session) } catch { // TODO: handle error @@ -236,7 +236,9 @@ public class Client: WalletConnect { try! send(Response(request: request, error: .invalidJSON)) return } + guard let session = communicator.session(by: request.url) else { return } + if !info.approved { do { try disconnect(from: session) @@ -244,21 +246,19 @@ public class Client: WalletConnect { delegate?.client(self, didDisconnect: session) } } else { - if let walletInfo = session.walletInfo { - let updatedInfo = Session.WalletInfo( - approved: info.approved, - accounts: info.accounts ?? [], - chainId: info.chainId, - peerId: walletInfo.peerId, - peerMeta: walletInfo.peerMeta - ) - var updatedSesson = session - updatedSesson.walletInfo = updatedInfo - communicator.addSession(updatedSesson) - delegate?.client(self, didUpdate: updatedSesson) - } else { - delegate?.client(self, didUpdate: session) - } + // we do not add sessions without walletInfo + let walletInfo = session.walletInfo! + let updatedInfo = Session.WalletInfo( + approved: info.approved, + accounts: info.accounts ?? [], + chainId: info.chainId ?? ChainID.mainnet, + peerId: walletInfo.peerId, + peerMeta: walletInfo.peerMeta + ) + var updatedSesson = session + updatedSesson.walletInfo = updatedInfo + communicator.addOrUpdateSession(updatedSesson) + delegate?.client(self, didUpdate: updatedSesson) } } else { // TODO: error handling diff --git a/Sources/PublicInterface/Server.swift b/Sources/PublicInterface/Server.swift index a03c3d21b..f35b7d7ed 100644 --- a/Sources/PublicInterface/Server.swift +++ b/Sources/PublicInterface/Server.swift @@ -4,12 +4,12 @@ import Foundation -public protocol RequestHandler: class { +public protocol RequestHandler: AnyObject { func canHandle(request: Request) -> Bool func handle(request: Request) } -public protocol ServerDelegate: class { +public protocol ServerDelegate: AnyObject { /// Websocket connection was dropped during handshake. The connectoin process should be initiated again. func server(_ server: Server, didFailToConnect url: WCURL) @@ -172,7 +172,7 @@ extension Server: HandshakeHandlerDelegate { self.communicator.send(response, topic: session.dAppInfo.peerId) if walletInfo.approved { let updatedSession = Session(url: session.url, dAppInfo: session.dAppInfo, walletInfo: walletInfo) - self.communicator.addSession(updatedSession) + self.communicator.addOrUpdateSession(updatedSession) self.communicator.subscribe(on: walletInfo.peerId, url: updatedSession.url) self.delegate?.server(self, didConnect: updatedSession) } @@ -181,14 +181,27 @@ extension Server: HandshakeHandlerDelegate { } extension Server: UpdateSessionHandlerDelegate { - func handler(_ handler: UpdateSessionHandler, didUpdateSessionByURL url: WCURL, approved: Bool) { + func handler(_ handler: UpdateSessionHandler, didUpdateSessionByURL url: WCURL, sessionInfo: SessionInfo) { guard let session = communicator.session(by: url) else { return } - if !approved { + if !sessionInfo.approved { do { try disconnect(from: session) } catch { // session already disconnected delegate?.server(self, didDisconnect: session) } + } else { + // we do not add sessions without walletInfo + let walletInfo = session.walletInfo! + let updatedInfo = Session.WalletInfo( + approved: sessionInfo.approved, + accounts: sessionInfo.accounts ?? [], + chainId: sessionInfo.chainId ?? ChainID.mainnet, + peerId: walletInfo.peerId, + peerMeta: walletInfo.peerMeta + ) + var updatedSesson = session + updatedSesson.walletInfo = updatedInfo + communicator.addOrUpdateSession(updatedSesson) } } } diff --git a/Sources/PublicInterface/Session.swift b/Sources/PublicInterface/Session.swift index 5d1b07ec0..946353c5b 100644 --- a/Sources/PublicInterface/Session.swift +++ b/Sources/PublicInterface/Session.swift @@ -69,7 +69,7 @@ public struct Session: Codable { self.peerMeta = peerMeta } - func with(approved: Bool) -> WalletInfo { + public func with(approved: Bool) -> WalletInfo { return WalletInfo(approved: approved, accounts: self.accounts, chainId: self.chainId, diff --git a/Sources/PublicInterface/WalletConnect.swift b/Sources/PublicInterface/WalletConnect.swift index 79719b167..6873047d6 100644 --- a/Sources/PublicInterface/WalletConnect.swift +++ b/Sources/PublicInterface/WalletConnect.swift @@ -33,7 +33,7 @@ open class WalletConnect { guard session.walletInfo != nil else { throw WalletConnectError.missingWalletInfoInSession } - communicator.addSession(session) + communicator.addOrUpdateSession(session) listen(on: session.url) } @@ -46,7 +46,7 @@ open class WalletConnect { throw WalletConnectError.tryingToDisconnectInactiveSession } try sendDisconnectSessionRequest(for: session) - communicator.addPendingDisconnectSession(session) + communicator.addOrUpdatePendingDisconnectSession(session) communicator.disconnect(from: session.url) } diff --git a/Tests/ClientTests.swift b/Tests/ClientTests.swift index fc4cb0415..7f082e67e 100644 --- a/Tests/ClientTests.swift +++ b/Tests/ClientTests.swift @@ -23,7 +23,7 @@ class ClientTests: XCTestCase { } func test_sendRequest_whenNoWalletInfo_thenThrows() { - communicator.addSession(Session.testSessionWithoutWalletInfo) + communicator.addOrUpdateSession(Session.testSessionWithoutWalletInfo) XCTAssertThrowsError(try client.send(Request.testRequest, completion: nil), "missingWalletInfoInSession") { error in XCTAssertEqual(error as? Client.ClientError, .missingWalletInfoInSession) @@ -31,7 +31,7 @@ class ClientTests: XCTestCase { } func test_sendRequest_callsCommunicator() { - communicator.addSession(Session.testSession) + communicator.addOrUpdateSession(Session.testSession) try? client.send(Request.testRequest, completion: nil) XCTAssertNotNil(communicator.sentRequest) } @@ -68,12 +68,12 @@ class ClientTests: XCTestCase { @discardableResult private func prepareAccountWithTestSession() -> String { - communicator.addSession(Session.testSession) + communicator.addOrUpdateSession(Session.testSession) return Session.testSession.walletInfo!.accounts[0] } func test_onConnect_whenSessionExists_thenSubscribesOnDappPeerIdTopic() { - communicator.addSession(Session.testSession) + communicator.addOrUpdateSession(Session.testSession) client.onConnect(to: WCURL.testURL) XCTAssertEqual(communicator.subscribedOn?.topic, Session.testSession.dAppInfo.peerId) XCTAssertEqual(communicator.subscribedOn?.url, Session.testSession.url) @@ -86,7 +86,7 @@ class ClientTests: XCTestCase { } func test_onConnect_whenSessionExists_thenCallsDelegate() { - communicator.addSession(Session.testSession) + communicator.addOrUpdateSession(Session.testSession) XCTAssertNil(delegate.connectedSession) client.onConnect(to: WCURL.testURL) XCTAssertEqual(delegate.connectedSession, Session.testSession) diff --git a/Tests/Helpers.swift b/Tests/Helpers.swift index 5da1f78c5..2ac61e89c 100644 --- a/Tests/Helpers.swift +++ b/Tests/Helpers.swift @@ -16,7 +16,7 @@ class MockCommunicator: Communicator { didListen = true } - override func addSession(_ session: Session) { + override func addOrUpdateSession(_ session: Session) { sessions.append(session) } diff --git a/Tests/WalletConnectTests.swift b/Tests/WalletConnectTests.swift index 7a6f3d50b..88f3f444c 100644 --- a/Tests/WalletConnectTests.swift +++ b/Tests/WalletConnectTests.swift @@ -21,7 +21,7 @@ class WalletConnectTests: XCTestCase { } func test_whenConnectingToExistingURL_thenThrows() { - mockCommunicator.addSession(Session.testSession) + mockCommunicator.addOrUpdateSession(Session.testSession) XCTAssertThrowsError(try wc.connect(to: WCURL.testURL)) } diff --git a/WalletConnectSwift.xcodeproj/project.pbxproj b/WalletConnectSwift.xcodeproj/project.pbxproj index 650c82d34..91b5d649d 100644 --- a/WalletConnectSwift.xcodeproj/project.pbxproj +++ b/WalletConnectSwift.xcodeproj/project.pbxproj @@ -265,7 +265,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1030; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = "Gnosis Ltd."; TargetAttributes = { 0A92784F230BFB2600FDCC0D = { @@ -487,6 +487,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -548,6 +549,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/WalletConnectSwift.xcodeproj/xcshareddata/xcschemes/WalletConnectSwift.xcscheme b/WalletConnectSwift.xcodeproj/xcshareddata/xcschemes/WalletConnectSwift.xcscheme index 163c4f4d0..e8b81a6ad 100644 --- a/WalletConnectSwift.xcodeproj/xcshareddata/xcschemes/WalletConnectSwift.xcscheme +++ b/WalletConnectSwift.xcodeproj/xcshareddata/xcschemes/WalletConnectSwift.xcscheme @@ -1,6 +1,6 @@ + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + - - - - - - - -