From c378ac0532c2192a7a8c686f5a205377ea67b4b1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Dec 2022 15:02:15 +0500 Subject: [PATCH 01/74] SDK structure --- Package.swift | 8 +++- Sources/Commons/AnyCodable.swift | 9 ++++ .../ChatClient/ChatClientProxy.swift | 21 ++++++++ .../ChatClient/ChatClientRequest.swift | 10 ++++ .../ChatClientRequestSubscriper.swift | 25 ++++++++++ Sources/Web3Inbox/Web3Inbox.swift | 48 +++++++++++++++++++ Sources/Web3Inbox/WebView/WebViewProxy.swift | 25 ++++++++++ .../Web3Inbox/WebView/WebViewRequest.swift | 14 ++++++ .../WebView/WebViewRequestSubscriber.swift | 26 ++++++++++ 9 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 Sources/Web3Inbox/ChatClient/ChatClientProxy.swift create mode 100644 Sources/Web3Inbox/ChatClient/ChatClientRequest.swift create mode 100644 Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift create mode 100644 Sources/Web3Inbox/Web3Inbox.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewProxy.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewRequest.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift diff --git a/Package.swift b/Package.swift index 7110440bc..c633746d6 100644 --- a/Package.swift +++ b/Package.swift @@ -39,7 +39,10 @@ let package = Package( targets: ["WalletConnectNetworking"]), .library( name: "WalletConnectVerify", - targets: ["WalletConnectVerify"]) + targets: ["WalletConnectVerify"]), + .library( + name: "Web3Inbox", + targets: ["Web3Inbox"]), ], dependencies: [], targets: [ @@ -79,6 +82,9 @@ let package = Package( .target( name: "WalletConnectPairing", dependencies: ["WalletConnectNetworking"]), + .target( + name: "Web3Inbox", + dependencies: ["WalletConnectChat"]), .target( name: "WalletConnectUtils", dependencies: ["JSONRPC"]), diff --git a/Sources/Commons/AnyCodable.swift b/Sources/Commons/AnyCodable.swift index 52552e344..895c7cfc2 100644 --- a/Sources/Commons/AnyCodable.swift +++ b/Sources/Commons/AnyCodable.swift @@ -40,7 +40,16 @@ public struct AnyCodable { genericEncoding = { encoder in try codable.encode(to: encoder) } + } + + /** + Creates a type-erased codable value that wraps the given instance. + - parameters: + - any: Any value which supposed to be codable + */ + public init(any value: Any) { + self.init(AnyCodable(value)) } /** diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift new file mode 100644 index 000000000..2a47f4d4c --- /dev/null +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -0,0 +1,21 @@ +import Foundation +import WalletConnectChat + +final class ChatClientProxy { + + private let client: ChatClient + + var onResponse: ((WebViewResponse) -> Void)? + + init(client: ChatClient) { + self.client = client + } + + func execute(request: WebViewRequest) { + switch request { + case .getInvites(let account): + let invites = client.getInvites(account: Account(account)!) + onResponse?(.getInvites(invites: invites)) + } + } +} diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift new file mode 100644 index 000000000..147b39154 --- /dev/null +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectChat + +enum ChatClientRequestMethod: String { + case chatInvites +} + +enum ChatClientRequest: Codable { + case chatInvite(invite: Invite) +} diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift new file mode 100644 index 000000000..43f7b0fbd --- /dev/null +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift @@ -0,0 +1,25 @@ +import Foundation +import Combine +import WalletConnectChat + +final class ChatClientRequestSubscriper { + + private var publishers: Set = [] + + private let chatClient: ChatClient + + var onRequest: ((ChatClientRequest) -> Void)? + + init(chatClient: ChatClient) { + self.chatClient = chatClient + + setupSubscriptions() + } + + func setupSubscriptions() { + chatClient.invitePublisher + .sink { [unowned self] invite in + onRequest?(.chatInvite(invite: invite)) + }.store(in: &publishers) + } +} diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift new file mode 100644 index 000000000..da50ca2dd --- /dev/null +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -0,0 +1,48 @@ +import Foundation +import WebKit +import WalletConnectChat + +final class Web3InboxClient { + + private let host: String + private let clientProxy: ChatClientProxy + private let clientSubscriber: ChatClientRequestSubscriper + + private let webviewProxy: WebViewProxy + private let webviewSubscriber: WebViewRequestSubscriber + + private lazy var webView: WKWebView = { + let configuration = WKWebViewConfiguration() + configuration.userContentController.add(webviewSubscriber, name: WebViewRequestSubscriber.name) + let webview = WKWebView(frame: .zero, configuration: configuration) + let request = URLRequest(url: URL(string: host)!) + webview.load(request) + return webview + }() + + init( + host: String, + clientProxy: ChatClientProxy, + clientSubscriber: ChatClientRequestSubscriper, + webviewProxy: WebViewProxy, + webviewSubscriber: WebViewRequestSubscriber + ) { + self.host = host + self.clientProxy = clientProxy + self.clientSubscriber = clientSubscriber + self.webviewProxy = webviewProxy + self.webviewSubscriber = webviewSubscriber + } + + private func setupSubscriptions() { + webviewSubscriber.onRequest = { [unowned self] request in + clientProxy.execute(request: request) + } + clientProxy.onResponse = { [unowned self] response in + webviewProxy.execute(response: response) + } + clientSubscriber.onRequest = { [unowned self] request in + webviewProxy.execute(request: request) + } + } +} diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift new file mode 100644 index 000000000..de046dc66 --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -0,0 +1,25 @@ +import Foundation +import WebKit + +final class WebViewProxy { + + private let webView: WKWebView + + init(webView: WKWebView) { + self.webView = webView + } + + func execute(request: ChatClientRequest) { + switch request { + case .chatInvite(let invite): + break // TODO: Implement me + } + } + + func execute(response: WebViewResponse) { + switch response { + case .getInvites(let invites): + break // TODO: Implement me + } + } +} diff --git a/Sources/Web3Inbox/WebView/WebViewRequest.swift b/Sources/Web3Inbox/WebView/WebViewRequest.swift new file mode 100644 index 000000000..18bef96c0 --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewRequest.swift @@ -0,0 +1,14 @@ +import Foundation +import WalletConnectChat + +enum WebViewRequestMethod: String { + case getInvites +} + +enum WebViewRequest: Codable { + case getInvites(account: String) +} + +enum WebViewResponse: Codable { + case getInvites(invites: [Invite]) +} diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift new file mode 100644 index 000000000..3c40d1616 --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -0,0 +1,26 @@ +import Foundation +import WebKit +import JSONRPC + +final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { + + static let name = "web3InboxHandler" + + var onRequest: ((WebViewRequest) -> Void)? + + func userContentController( + _ userContentController: WKUserContentController, + didReceive message: WKScriptMessage + ) { + guard message.name == WebViewRequestSubscriber.name else { return } + + guard + let dict = message.body as? [String: Any], + let data = try? JSONSerialization.data(withJSONObject: dict), + let request = try? JSONDecoder().decode(RPCRequest.self, from: data), + let event = try? request.params?.get(WebViewRequest.self) + else { return } + + onRequest?(event) + } +} From ec5b0b2bf1076d8c9233323f6de33abb39e5f21c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 3 Jan 2023 17:22:21 +0500 Subject: [PATCH 02/74] Web3Inbox factory --- .../ChatClient/ChatClientRequest.swift | 13 +++-- Sources/Web3Inbox/Web3Inbox.swift | 46 ++--------------- Sources/Web3Inbox/Web3InboxClient.swift | 49 +++++++++++++++++++ .../Web3Inbox/Web3InboxClientFactory.swift | 23 +++++++++ .../Web3Inbox/WebView/WebViewFactory.swift | 25 ++++++++++ Sources/Web3Inbox/WebView/WebViewProxy.swift | 14 +----- .../Web3Inbox/WebView/WebViewRequest.swift | 13 +++-- Sources/Web3Inbox/WebView/WebViewScript.swift | 19 +++++++ 8 files changed, 138 insertions(+), 64 deletions(-) create mode 100644 Sources/Web3Inbox/Web3InboxClient.swift create mode 100644 Sources/Web3Inbox/Web3InboxClientFactory.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewFactory.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewScript.swift diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index 147b39154..931d31c2f 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -1,10 +1,13 @@ import Foundation import WalletConnectChat -enum ChatClientRequestMethod: String { - case chatInvites -} - -enum ChatClientRequest: Codable { +enum ChatClientRequest: Codable, WebViewScript { case chatInvite(invite: Invite) + + var command: String { + switch self { + case .chatInvite: + return "chatInvite" + } + } } diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index da50ca2dd..5210254b9 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -1,48 +1,10 @@ import Foundation -import WebKit import WalletConnectChat -final class Web3InboxClient { +public final class Web3Inbox { - private let host: String - private let clientProxy: ChatClientProxy - private let clientSubscriber: ChatClientRequestSubscriper - - private let webviewProxy: WebViewProxy - private let webviewSubscriber: WebViewRequestSubscriber - - private lazy var webView: WKWebView = { - let configuration = WKWebViewConfiguration() - configuration.userContentController.add(webviewSubscriber, name: WebViewRequestSubscriber.name) - let webview = WKWebView(frame: .zero, configuration: configuration) - let request = URLRequest(url: URL(string: host)!) - webview.load(request) - return webview + /// Web3Inbox client instance + public static var instance: Web3InboxClient = { + return Web3InboxClientFactory.create(chatClient: Chat.instance) }() - - init( - host: String, - clientProxy: ChatClientProxy, - clientSubscriber: ChatClientRequestSubscriper, - webviewProxy: WebViewProxy, - webviewSubscriber: WebViewRequestSubscriber - ) { - self.host = host - self.clientProxy = clientProxy - self.clientSubscriber = clientSubscriber - self.webviewProxy = webviewProxy - self.webviewSubscriber = webviewSubscriber - } - - private func setupSubscriptions() { - webviewSubscriber.onRequest = { [unowned self] request in - clientProxy.execute(request: request) - } - clientProxy.onResponse = { [unowned self] response in - webviewProxy.execute(response: response) - } - clientSubscriber.onRequest = { [unowned self] request in - webviewProxy.execute(request: request) - } - } } diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift new file mode 100644 index 000000000..006a6c607 --- /dev/null +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -0,0 +1,49 @@ +import Foundation +import WebKit +import WalletConnectChat + +public final class Web3InboxClient { + + private let webView: WKWebView + + private let clientProxy: ChatClientProxy + private let clientSubscriber: ChatClientRequestSubscriper + + private let webviewProxy: WebViewProxy + private let webviewSubscriber: WebViewRequestSubscriber + + init( + webView: WKWebView, + clientProxy: ChatClientProxy, + clientSubscriber: ChatClientRequestSubscriper, + webviewProxy: WebViewProxy, + webviewSubscriber: WebViewRequestSubscriber + ) { + self.webView = webView + self.clientProxy = clientProxy + self.clientSubscriber = clientSubscriber + self.webviewProxy = webviewProxy + self.webviewSubscriber = webviewSubscriber + } + + public func getWebView() -> WKWebView { + return webView + } +} + +// MARK: - Privates + +private extension Web3InboxClient { + + func setupSubscriptions() { + webviewSubscriber.onRequest = { [unowned self] request in + clientProxy.execute(request: request) + } + clientProxy.onResponse = { [unowned self] response in + webviewProxy.execute(script: response) + } + clientSubscriber.onRequest = { [unowned self] request in + webviewProxy.execute(script: request) + } + } +} diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift new file mode 100644 index 000000000..a1e5fc247 --- /dev/null +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -0,0 +1,23 @@ +import Foundation +import WebKit +import WalletConnectChat + +final class Web3InboxClientFactory { + + static func create(chatClient: ChatClient) -> Web3InboxClient { + let host = "https://web3inbox-dev-hidden-git-feat-add-w3i-proxy-walletconnect1.vercel.app/?noClientMode=true" + let webviewSubscriber = WebViewRequestSubscriber() + let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() + let webViewProxy = WebViewProxy(webView: webView) + let clientProxy = ChatClientProxy(client: chatClient) + let clientSubscriber = ChatClientRequestSubscriper(chatClient: chatClient) + + return Web3InboxClient( + webView: webView, + clientProxy: clientProxy, + clientSubscriber: clientSubscriber, + webviewProxy: webViewProxy, + webviewSubscriber: webviewSubscriber + ) + } +} diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift new file mode 100644 index 000000000..39fc26212 --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -0,0 +1,25 @@ +import Foundation +import WebKit + +final class WebViewFactory { + + private let host: String + private let webviewSubscriber: WebViewRequestSubscriber + + init(host: String, webviewSubscriber: WebViewRequestSubscriber) { + self.host = host + self.webviewSubscriber = webviewSubscriber + } + + func create() -> WKWebView { + let configuration = WKWebViewConfiguration() + configuration.userContentController.add( + webviewSubscriber, + name: WebViewRequestSubscriber.name + ) + let webview = WKWebView(frame: .zero, configuration: configuration) + let request = URLRequest(url: URL(string: host)!) + webview.load(request) + return webview + } +} diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index de046dc66..ce1aa4289 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -9,17 +9,7 @@ final class WebViewProxy { self.webView = webView } - func execute(request: ChatClientRequest) { - switch request { - case .chatInvite(let invite): - break // TODO: Implement me - } - } - - func execute(response: WebViewResponse) { - switch response { - case .getInvites(let invites): - break // TODO: Implement me - } + func execute(script: WebViewScript) { + webView.evaluateJavaScript(script.build()) } } diff --git a/Sources/Web3Inbox/WebView/WebViewRequest.swift b/Sources/Web3Inbox/WebView/WebViewRequest.swift index 18bef96c0..d4e8b9fbc 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequest.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequest.swift @@ -1,14 +1,17 @@ import Foundation import WalletConnectChat -enum WebViewRequestMethod: String { - case getInvites -} - enum WebViewRequest: Codable { case getInvites(account: String) } -enum WebViewResponse: Codable { +enum WebViewResponse: Codable, WebViewScript { case getInvites(invites: [Invite]) + + var command: String { + switch self { + case .getInvites: + return "getInvites" + } + } } diff --git a/Sources/Web3Inbox/WebView/WebViewScript.swift b/Sources/Web3Inbox/WebView/WebViewScript.swift new file mode 100644 index 000000000..5d1c243dd --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewScript.swift @@ -0,0 +1,19 @@ +import Foundation + +protocol WebViewScript { + var command: String { get } + var params: [String: String]? { get } +} + +extension WebViewScript { + + var params: [String: String]? { + return nil + } + + func build() -> String { + let data = try! JSONEncoder().encode(params) + let json = String(data: data, encoding: .utf8)! + return "window.actions.\(command)(\(json))" + } +} From 9b2c1e834cf5b98625de7871ace03117b7091b11 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 4 Jan 2023 15:54:57 +0500 Subject: [PATCH 03/74] WebView events --- Example/ExampleApp.xcodeproj/project.pbxproj | 27 ++++++ .../xcshareddata/swiftpm/Package.resolved | 88 ------------------- .../DomainLayer/Chat/ChatService.swift | 4 +- .../Chat/ChatList/ChatListInteractor.swift | 4 +- .../Chat/ChatList/ChatListPresenter.swift | 2 +- .../Chat/Main/MainPresenter.swift | 1 + .../Chat/Main/MainRouter.swift | 4 + .../Chat/Main/Model/TabPage.swift | 5 ++ .../Web3Inbox/Web3InboxModule.swift | 13 +++ .../Web3Inbox/Web3InboxRouter.swift | 12 +++ .../Web3Inbox/Web3InboxViewController.swift | 11 +++ Sources/Chat/ChatClient.swift | 4 +- .../ChatClient/ChatClientProxy.swift | 75 ++++++++++++++-- .../ChatClient/ChatClientRequest.swift | 12 +-- .../ChatClientRequestSubscriber.swift | 36 ++++++++ .../ChatClientRequestSubscriper.swift | 25 ------ Sources/Web3Inbox/Web3Inbox.swift | 1 - Sources/Web3Inbox/Web3InboxClient.swift | 16 ++-- .../Web3Inbox/Web3InboxClientFactory.swift | 9 +- Sources/Web3Inbox/Web3InboxImports.swift | 3 + Sources/Web3Inbox/WebView/WebViewEvent.swift | 9 ++ Sources/Web3Inbox/WebView/WebViewProxy.swift | 21 ++++- .../Web3Inbox/WebView/WebViewRequest.swift | 17 ---- .../WebView/WebViewRequestSubscriber.swift | 22 +++-- Sources/Web3Inbox/WebView/WebViewScript.swift | 19 ---- 25 files changed, 250 insertions(+), 190 deletions(-) delete mode 100644 Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift create mode 100644 Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift create mode 100644 Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift create mode 100644 Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift delete mode 100644 Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift create mode 100644 Sources/Web3Inbox/Web3InboxImports.swift create mode 100644 Sources/Web3Inbox/WebView/WebViewEvent.swift delete mode 100644 Sources/Web3Inbox/WebView/WebViewRequest.swift delete mode 100644 Sources/Web3Inbox/WebView/WebViewScript.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 90aa0e0a4..290fc786e 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -60,6 +60,9 @@ 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; + A518A98729683FB60035247E /* Web3InboxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98429683FB60035247E /* Web3InboxViewController.swift */; }; + A518A98829683FB60035247E /* Web3InboxModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98529683FB60035247E /* Web3InboxModule.swift */; }; + A518A98929683FB60035247E /* Web3InboxRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98629683FB60035247E /* Web3InboxRouter.swift */; }; A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518B31328E33A6500A2CE93 /* InputConfig.swift */; }; A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0D828E436A3001BACF9 /* InputConfig.swift */; }; A51AC0DD28E43727001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DB28E436E6001BACF9 /* InputConfig.swift */; }; @@ -90,6 +93,7 @@ A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AE728772A0100094373 /* InviteViewModel.swift */; }; A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AE92877F2D600094373 /* WalletConnectChat */; }; A5629AF22877F75100094373 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AF12877F75100094373 /* Starscream */; }; + A574B3582964560C00C2BB91 /* Web3Inbox in Frameworks */ = {isa = PBXBuildFile; productRef = A574B3572964560C00C2BB91 /* Web3Inbox */; }; A578FA322873036400AA7720 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA312873036400AA7720 /* InputView.swift */; }; A578FA35287304A300AA7720 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA34287304A300AA7720 /* Color.swift */; }; A578FA372873D8EE00AA7720 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA362873D8EE00AA7720 /* UIColor.swift */; }; @@ -319,6 +323,9 @@ 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; + A518A98429683FB60035247E /* Web3InboxViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxViewController.swift; sourceTree = ""; }; + A518A98529683FB60035247E /* Web3InboxModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxModule.swift; sourceTree = ""; }; + A518A98629683FB60035247E /* Web3InboxRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxRouter.swift; sourceTree = ""; }; A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DB28E436E6001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; @@ -521,6 +528,7 @@ A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */, A59FAEC928B7B93A002BB66F /* Web3 in Frameworks */, A5629AF22877F75100094373 /* Starscream in Frameworks */, + A574B3582964560C00C2BB91 /* Web3Inbox in Frameworks */, A59F877628B5462900A9CD80 /* WalletConnectAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -862,6 +870,16 @@ path = Chat; sourceTree = ""; }; + A574B3592964570000C2BB91 /* Web3Inbox */ = { + isa = PBXGroup; + children = ( + A518A98529683FB60035247E /* Web3InboxModule.swift */, + A518A98629683FB60035247E /* Web3InboxRouter.swift */, + A518A98429683FB60035247E /* Web3InboxViewController.swift */, + ); + path = Web3Inbox; + sourceTree = ""; + }; A578FA332873049400AA7720 /* Style */ = { isa = PBXGroup; children = ( @@ -961,6 +979,7 @@ A58E7D062872A4390082D443 /* PresentationLayer */ = { isa = PBXGroup; children = ( + A574B3592964570000C2BB91 /* Web3Inbox */, A59F876828B53E6400A9CD80 /* Chat */, ); path = PresentationLayer; @@ -1545,6 +1564,7 @@ A5629AF12877F75100094373 /* Starscream */, A59F877528B5462900A9CD80 /* WalletConnectAuth */, A59FAEC828B7B93A002BB66F /* Web3 */, + A574B3572964560C00C2BB91 /* Web3Inbox */, ); productName = Showcase; productReference = A58E7CE828729F550082D443 /* Showcase.app */; @@ -1829,14 +1849,17 @@ A58E7D0C2872A45B0082D443 /* MainModule.swift in Sources */, A5C2021C287E1FD8007E3188 /* ImportInteractor.swift in Sources */, A58E7D0D2872A45B0082D443 /* MainPresenter.swift in Sources */, + A518A98829683FB60035247E /* Web3InboxModule.swift in Sources */, A5C20219287E1FD8007E3188 /* ImportModule.swift in Sources */, A5629AD62876CC5700094373 /* InviteInteractor.swift in Sources */, A5629AE12876CC6E00094373 /* InviteListInteractor.swift in Sources */, A58E7CED28729F550082D443 /* SceneDelegate.swift in Sources */, A5C2020F287D9DEE007E3188 /* WelcomeView.swift in Sources */, + A518A98929683FB60035247E /* Web3InboxRouter.swift in Sources */, A5C20226287EB099007E3188 /* AccountNameResolver.swift in Sources */, A5C2020D287D9DEE007E3188 /* WelcomeRouter.swift in Sources */, A578FA372873D8EE00AA7720 /* UIColor.swift in Sources */, + A518A98729683FB60035247E /* Web3InboxViewController.swift in Sources */, A5C2021D287E1FD8007E3188 /* ImportView.swift in Sources */, A5C2021A287E1FD8007E3188 /* ImportPresenter.swift in Sources */, A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */, @@ -2600,6 +2623,10 @@ package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */; productName = Starscream; }; + A574B3572964560C00C2BB91 /* Web3Inbox */ = { + isa = XCSwiftPackageProductDependency; + productName = Web3Inbox; + }; A59F877528B5462900A9CD80 /* WalletConnectAuth */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectAuth; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 4dc474e71..000000000 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,88 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "BigInt", - "repositoryURL": "https://github.com/attaswift/BigInt.git", - "state": { - "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" - } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", - "state": { - "branch": null, - "revision": "19b3c3ceed117c5cc883517c4e658548315ba70b", - "version": "1.6.0" - } - }, - { - "package": "PromiseKit", - "repositoryURL": "https://github.com/mxcl/PromiseKit.git", - "state": { - "branch": null, - "revision": "43772616c46a44a9977e41924ae01d0e55f2f9ca", - "version": "6.18.1" - } - }, - { - "package": "secp256k1", - "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift.git", - "state": { - "branch": null, - "revision": "cd187c632fb812fd93711a9f7e644adb7e5f97f0", - "version": "0.1.7" - } - }, - { - "package": "SolanaSwift", - "repositoryURL": "https://github.com/flypaper0/solana-swift", - "state": { - "branch": "feature/available-13", - "revision": "a98811518e0a90c2dfc60c30cfd3ec85c33b6790", - "version": null - } - }, - { - "package": "Starscream", - "repositoryURL": "https://github.com/daltoniam/Starscream", - "state": { - "branch": null, - "revision": "a063fda2b8145a231953c20e7a646be254365396", - "version": "3.1.2" - } - }, - { - "package": "Task_retrying", - "repositoryURL": "https://github.com/bigearsenal/task-retrying-swift.git", - "state": { - "branch": null, - "revision": "645eaaf207a6f39ab4b469558d916ae23df199b5", - "version": "1.0.3" - } - }, - { - "package": "TweetNacl", - "repositoryURL": "https://github.com/bitmark-inc/tweetnacl-swiftwrap.git", - "state": { - "branch": null, - "revision": "f8fd111642bf2336b11ef9ea828510693106e954", - "version": "1.1.0" - } - }, - { - "package": "Web3", - "repositoryURL": "https://github.com/WalletConnect/Web3.swift", - "state": { - "branch": null, - "revision": "569255adcfff0b37e4cb8004aea29d0e2d6266df", - "version": "1.0.2" - } - } - ] - }, - "version": 1 -} diff --git a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift index 991c06c66..76264ff76 100644 --- a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift +++ b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift @@ -33,8 +33,8 @@ final class ChatService { await client.getMessages(topic: thread.topic) } - func getThreads() async -> [WalletConnectChat.Thread] { - await client.getThreads() + func getThreads(account: Account) async -> [WalletConnectChat.Thread] { + await client.getThreads(account: account) } func getInvites(account: Account) async -> [WalletConnectChat.Invite] { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift index 87e40a7af..a5a28b513 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift @@ -10,8 +10,8 @@ final class ChatListInteractor { self.accountStorage = accountStorage } - func getThreads() async -> [WalletConnectChat.Thread] { - return await chatService.getThreads() + func getThreads(account: Account) async -> [WalletConnectChat.Thread] { + return await chatService.getThreads(account: account) } func threadsSubscription() -> Stream { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift index a839d8dd7..204afb1d0 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift @@ -97,7 +97,7 @@ private extension ChatListPresenter { @MainActor func loadThreads() async { - let threads = await interactor.getThreads() + let threads = await interactor.getThreads(account: account) self.threads = threads .filter { $0.selfAccount == account } .sorted(by: { $0.topic < $1.topic }) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift index fde2e49fc..62f301e35 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift @@ -12,6 +12,7 @@ final class MainPresenter { var viewControllers: [UIViewController] { return [ router.chatViewController + router.web3InboxViewController, ] } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift index fdd32686d..b6bb372b1 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift @@ -10,6 +10,10 @@ final class MainRouter { return WelcomeModule.create(app: app) } + var web3InboxViewController: UIViewController { + return Web3InboxModule.create(app: app) + } + init(app: Application) { self.app = app } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift index 2613f6568..ca861bae4 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift @@ -3,6 +3,7 @@ import UIKit enum TabPage: CaseIterable { case chat case wallet + case web3Inbox var title: String { switch self { @@ -10,6 +11,8 @@ enum TabPage: CaseIterable { return "Chat" case .wallet: return "Wallet" + case .web3Inbox: + return "Web3Inbox" } } @@ -19,6 +22,8 @@ enum TabPage: CaseIterable { return UIImage(systemName: "message.fill")! case .wallet: return UIImage(systemName: "signature")! + case .web3Inbox: + return UIImage(systemName: "safari.fill")! } } diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift new file mode 100644 index 000000000..ad0633441 --- /dev/null +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift @@ -0,0 +1,13 @@ +import SwiftUI + +final class Web3InboxModule { + + @discardableResult + static func create(app: Application) -> UIViewController { + let router = Web3InboxRouter(app: app) + let viewController = Web3InboxViewController() + router.viewController = viewController + return viewController + } + +} diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift new file mode 100644 index 000000000..3631c35be --- /dev/null +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxRouter.swift @@ -0,0 +1,12 @@ +import UIKit + +final class Web3InboxRouter { + + weak var viewController: UIViewController! + + private let app: Application + + init(app: Application) { + self.app = app + } +} diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift new file mode 100644 index 000000000..e905ebba0 --- /dev/null +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -0,0 +1,11 @@ +import UIKit +import Web3Inbox +import WebKit + +final class Web3InboxViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + self.view = Web3Inbox.instance.getWebView() + } +} diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index b4c529076..373106ea2 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -117,10 +117,12 @@ public class ChatClient { } public func getInvites(account: Account) -> [Invite] { + // TODO: Account based storage return invitePayloadStore.getAll().map { $0.request } } - public func getThreads() async -> [Thread] { + public func getThreads(account: Account) async -> [Thread] { + // TODO: Account based storage await threadStore.getAll() } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index 2a47f4d4c..fb5595a89 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -1,21 +1,82 @@ import Foundation -import WalletConnectChat final class ChatClientProxy { private let client: ChatClient - var onResponse: ((WebViewResponse) -> Void)? + var onResponse: ((RPCResponse) async throws -> Void)? init(client: ChatClient) { self.client = client } - func execute(request: WebViewRequest) { - switch request { - case .getInvites(let account): - let invites = client.getInvites(account: Account(account)!) - onResponse?(.getInvites(invites: invites)) + func request(_ request: RPCRequest) async throws { + guard let event = WebViewEvent(rawValue: request.method) + else { throw Errors.unregisteredMethod } + + switch event { + case .getInvites: + let params = try parse(GetInvitesRequest.self, params: request.params) + let invites = client.getInvites(account: params.account) + try await respond(with: invites, request: request) + + case .getThreads: + let params = try parse(GetThreadsRequest.self, params: request.params) + let threads = await client.getThreads(account: params.account) + try await respond(with: threads, request: request) + + case .register: + let params = try parse(RegisterRequest.self, params: request.params) + try await client.register(account: params.account) + + case .getMessages: + let params = try parse(GetMessagesRequest.self, params: request.params) + let messages = await client.getMessages(topic: params.topic) + try await respond(with: messages, request: request) + + case .message: + let params = try parse(MessageRequest.self, params: request.params) + try await client.message(topic: params.topic, message: params.payload.message) + try await respond(with: params.payload, request: request) } } } + +private extension ChatClientProxy { + enum Errors: Error { + case unregisteredMethod + case unregisteredParams + } + + struct GetInvitesRequest: Codable { + let account: Account + } + + struct GetThreadsRequest: Codable { + let account: Account + } + + struct RegisterRequest: Codable { + let account: Account + } + + struct GetMessagesRequest: Codable { + let topic: String + } + + struct MessageRequest: Codable { + let topic: String + let payload: Message + } + + func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { + guard let params = try? params?.get([Request].self).first + else { throw Errors.unregisteredParams } + return params + } + + func respond(with object: Object, request: RPCRequest) async throws { + let response = RPCResponse(matchingRequest: request, result: object) + try await onResponse?(response) + } +} diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index 931d31c2f..e837af67d 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -1,13 +1,9 @@ import Foundation -import WalletConnectChat -enum ChatClientRequest: Codable, WebViewScript { - case chatInvite(invite: Invite) +enum ChatClientRequest: String { + case chatInvite - var command: String { - switch self { - case .chatInvite: - return "chatInvite" - } + var method: String { + return rawValue } } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift new file mode 100644 index 000000000..fed16ce27 --- /dev/null +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift @@ -0,0 +1,36 @@ +import Foundation +import Combine + +final class ChatClientRequestSubscriber { + + private var publishers: Set = [] + + private let chatClient: ChatClient + private let logger: ConsoleLogging + + var onRequest: ((RPCRequest) async throws -> Void)? + + init(chatClient: ChatClient, logger: ConsoleLogging) { + self.chatClient = chatClient + self.logger = logger + + setupSubscriptions() + } + + func setupSubscriptions() { + chatClient.invitePublisher + .sink { [unowned self] invite in + Task { @MainActor in + do { + let request = RPCRequest( + method: ChatClientRequest.chatInvite.method, + params: invite + ) + try await onRequest?(request) + } catch { + logger.error("Client Request error: \(error.localizedDescription)") + } + } + }.store(in: &publishers) + } +} diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift deleted file mode 100644 index 43f7b0fbd..000000000 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriper.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation -import Combine -import WalletConnectChat - -final class ChatClientRequestSubscriper { - - private var publishers: Set = [] - - private let chatClient: ChatClient - - var onRequest: ((ChatClientRequest) -> Void)? - - init(chatClient: ChatClient) { - self.chatClient = chatClient - - setupSubscriptions() - } - - func setupSubscriptions() { - chatClient.invitePublisher - .sink { [unowned self] invite in - onRequest?(.chatInvite(invite: invite)) - }.store(in: &publishers) - } -} diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index 5210254b9..b85728545 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectChat public final class Web3Inbox { diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 006a6c607..1bdfc81af 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -1,29 +1,33 @@ import Foundation import WebKit -import WalletConnectChat public final class Web3InboxClient { private let webView: WKWebView + private let logger: ConsoleLogging private let clientProxy: ChatClientProxy - private let clientSubscriber: ChatClientRequestSubscriper + private let clientSubscriber: ChatClientRequestSubscriber private let webviewProxy: WebViewProxy private let webviewSubscriber: WebViewRequestSubscriber init( webView: WKWebView, + logger: ConsoleLogging, clientProxy: ChatClientProxy, - clientSubscriber: ChatClientRequestSubscriper, + clientSubscriber: ChatClientRequestSubscriber, webviewProxy: WebViewProxy, webviewSubscriber: WebViewRequestSubscriber ) { self.webView = webView + self.logger = logger self.clientProxy = clientProxy self.clientSubscriber = clientSubscriber self.webviewProxy = webviewProxy self.webviewSubscriber = webviewSubscriber + + setupSubscriptions() } public func getWebView() -> WKWebView { @@ -37,13 +41,13 @@ private extension Web3InboxClient { func setupSubscriptions() { webviewSubscriber.onRequest = { [unowned self] request in - clientProxy.execute(request: request) + try await self.clientProxy.request(request) } clientProxy.onResponse = { [unowned self] response in - webviewProxy.execute(script: response) + try await self.webviewProxy.respond(response) } clientSubscriber.onRequest = { [unowned self] request in - webviewProxy.execute(script: request) + try await self.webviewProxy.request(request) } } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index a1e5fc247..4d6b29178 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -1,19 +1,20 @@ import Foundation import WebKit -import WalletConnectChat final class Web3InboxClientFactory { static func create(chatClient: ChatClient) -> Web3InboxClient { - let host = "https://web3inbox-dev-hidden-git-feat-add-w3i-proxy-walletconnect1.vercel.app/?noClientMode=true" - let webviewSubscriber = WebViewRequestSubscriber() + let host = "https://web3inbox-dev-hidden-git-feat-add-w3i-proxy-walletconnect1.vercel.app/?chatProvider=ios" + let logger = ConsoleLogger(suffix: "📬") + let webviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() let webViewProxy = WebViewProxy(webView: webView) let clientProxy = ChatClientProxy(client: chatClient) - let clientSubscriber = ChatClientRequestSubscriper(chatClient: chatClient) + let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger) return Web3InboxClient( webView: webView, + logger: ConsoleLogger(), clientProxy: clientProxy, clientSubscriber: clientSubscriber, webviewProxy: webViewProxy, diff --git a/Sources/Web3Inbox/Web3InboxImports.swift b/Sources/Web3Inbox/Web3InboxImports.swift new file mode 100644 index 000000000..54b421e88 --- /dev/null +++ b/Sources/Web3Inbox/Web3InboxImports.swift @@ -0,0 +1,3 @@ +#if !CocoaPods +@_exported import WalletConnectChat +#endif diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift new file mode 100644 index 000000000..0f6e7e11e --- /dev/null +++ b/Sources/Web3Inbox/WebView/WebViewEvent.swift @@ -0,0 +1,9 @@ +import Foundation + +enum WebViewEvent: String { + case getInvites + case getThreads + case register + case getMessages + case message +} diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index ce1aa4289..8977bfd8b 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -1,7 +1,7 @@ import Foundation import WebKit -final class WebViewProxy { +actor WebViewProxy { private let webView: WKWebView @@ -9,7 +9,22 @@ final class WebViewProxy { self.webView = webView } - func execute(script: WebViewScript) { - webView.evaluateJavaScript(script.build()) + func respond(_ response: RPCResponse) async throws { + let body = try response.json() + let script = formatScript(body: body) + await webView.evaluateJavaScript(script, completionHandler: nil) + } + + func request(_ request: RPCRequest) async throws { + let body = try request.json() + let script = formatScript(body: body) + await webView.evaluateJavaScript(script, completionHandler: nil) + } +} + +private extension WebViewProxy { + + func formatScript(body: String) -> String { + return "window.\(WebViewRequestSubscriber.name).chat.postMessage(\(body))" } } diff --git a/Sources/Web3Inbox/WebView/WebViewRequest.swift b/Sources/Web3Inbox/WebView/WebViewRequest.swift deleted file mode 100644 index d4e8b9fbc..000000000 --- a/Sources/Web3Inbox/WebView/WebViewRequest.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation -import WalletConnectChat - -enum WebViewRequest: Codable { - case getInvites(account: String) -} - -enum WebViewResponse: Codable, WebViewScript { - case getInvites(invites: [Invite]) - - var command: String { - switch self { - case .getInvites: - return "getInvites" - } - } -} diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index 3c40d1616..f42751b97 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -1,12 +1,17 @@ import Foundation import WebKit -import JSONRPC final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { - static let name = "web3InboxHandler" + static let name = "web3inbox" - var onRequest: ((WebViewRequest) -> Void)? + var onRequest: ((RPCRequest) async throws -> Void)? + + private let logger: ConsoleLogging + + init(logger: ConsoleLogging) { + self.logger = logger + } func userContentController( _ userContentController: WKUserContentController, @@ -17,10 +22,15 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { guard let dict = message.body as? [String: Any], let data = try? JSONSerialization.data(withJSONObject: dict), - let request = try? JSONDecoder().decode(RPCRequest.self, from: data), - let event = try? request.params?.get(WebViewRequest.self) + let request = try? JSONDecoder().decode(RPCRequest.self, from: data) else { return } - onRequest?(event) + Task { @MainActor in + do { + try await onRequest?(request) + } catch { + logger.error("WebView Request error: \(error.localizedDescription)") + } + } } } diff --git a/Sources/Web3Inbox/WebView/WebViewScript.swift b/Sources/Web3Inbox/WebView/WebViewScript.swift deleted file mode 100644 index 5d1c243dd..000000000 --- a/Sources/Web3Inbox/WebView/WebViewScript.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -protocol WebViewScript { - var command: String { get } - var params: [String: String]? { get } -} - -extension WebViewScript { - - var params: [String: String]? { - return nil - } - - func build() -> String { - let data = try! JSONEncoder().encode(params) - let json = String(data: data, encoding: .utf8)! - return "window.actions.\(command)(\(json))" - } -} From b12dcca020a065c0d1b8e92b5cfbbef0b57f2497 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Jan 2023 16:24:48 +0500 Subject: [PATCH 04/74] After merge fixes --- .../Configurator/ApplicationConfigurator.swift | 2 +- .../PresentationLayer/Chat/Main/MainPresenter.swift | 2 +- .../PresentationLayer/Chat/Main/Model/TabPage.swift | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift index 810260eef..a50e9c6e6 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift @@ -11,6 +11,6 @@ struct ApplicationConfigurator: Configurator { } func configure() { - WelcomeModule.create(app: app).present() + MainModule.create(app: app).present() } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift index 62f301e35..fe2f68269 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift @@ -11,7 +11,7 @@ final class MainPresenter { var viewControllers: [UIViewController] { return [ - router.chatViewController + router.chatViewController, router.web3InboxViewController, ] } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift index ca861bae4..e22d69308 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift @@ -2,15 +2,12 @@ import UIKit enum TabPage: CaseIterable { case chat - case wallet case web3Inbox var title: String { switch self { case .chat: return "Chat" - case .wallet: - return "Wallet" case .web3Inbox: return "Web3Inbox" } @@ -20,18 +17,16 @@ enum TabPage: CaseIterable { switch self { case .chat: return UIImage(systemName: "message.fill")! - case .wallet: - return UIImage(systemName: "signature")! case .web3Inbox: return UIImage(systemName: "safari.fill")! } } static var selectedIndex: Int { - return 0 + return 1 } static var enabledTabs: [TabPage] { - return [.chat, .wallet] + return [.chat, .web3Inbox] } } From ef04fec94fd2deabae20715dcc0e726618d57f10 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 10 Jan 2023 17:54:11 +0500 Subject: [PATCH 05/74] Accounts management --- .../Chat/Welcome/WelcomePresenter.swift | 4 ++-- .../Chat/Welcome/WelcomeView.swift | 4 +--- .../Web3Inbox/Web3InboxViewController.swift | 1 + .../ChatClient/ChatClientProxy.swift | 8 ++++++++ .../ChatClient/ChatClientRequest.swift | 3 ++- .../ChatClientRequestSubscriber.swift | 2 +- Sources/Web3Inbox/Web3Inbox.swift | 16 ++++++++++++++- Sources/Web3Inbox/Web3InboxClient.swift | 20 +++++++++++++++++++ .../Web3Inbox/Web3InboxClientFactory.swift | 3 ++- Sources/Web3Inbox/WebView/WebViewEvent.swift | 1 + .../Web3Inbox/WebView/WebViewFactory.swift | 1 + Sources/Web3Inbox/WebView/WebViewProxy.swift | 10 ++++++---- .../WebView/WebViewRequestSubscriber.swift | 16 ++++++++++++++- 13 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift index 0ad6296ee..08565843e 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift @@ -26,11 +26,11 @@ final class WelcomePresenter: ObservableObject { return interactor.isAuthorized() ? "Start Messaging" : "Connect wallet" } - func didPressImport() async { + func didPressImport() { if let account = interactor.account { router.presentChats(account: account) } else { - await authWithWallet() + router.presentImport() } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift index b56ef64e0..2d9ba4129 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeView.swift @@ -32,9 +32,7 @@ struct WelcomeView: View { .multilineTextAlignment(.center) BrandButton(title: presenter.buttonTitle, action: { - Task { - await presenter.didPressImport() - } + presenter.didPressImport() }) Text("By connecting your wallet you agree with our\nTerms of Service") diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index e905ebba0..0cc93ff37 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -6,6 +6,7 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + Web3Inbox.configure(account: Account("eip155:1:0x2F871A068a16BF4a970663dF5e417951aB79Bfd3")!) self.view = Web3Inbox.instance.getWebView() } } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index fb5595a89..e9c8003d2 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -38,6 +38,10 @@ final class ChatClientProxy { let params = try parse(MessageRequest.self, params: request.params) try await client.message(topic: params.topic, message: params.payload.message) try await respond(with: params.payload, request: request) + + case .accept: + let params = try parse(AcceptRequest.self, params: request.params) + try await client.accept(inviteId: params.id.description) } } } @@ -69,6 +73,10 @@ private extension ChatClientProxy { let payload: Message } + struct AcceptRequest: Codable { + let id: Int + } + func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { guard let params = try? params?.get([Request].self).first else { throw Errors.unregisteredParams } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index e837af67d..6c038b981 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -1,7 +1,8 @@ import Foundation enum ChatClientRequest: String { - case chatInvite + case chatInvite = "chat_invite" + case setAccount = "setAccount" var method: String { return rawValue diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift index fed16ce27..ac0a3f91f 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift @@ -20,7 +20,7 @@ final class ChatClientRequestSubscriber { func setupSubscriptions() { chatClient.invitePublisher .sink { [unowned self] invite in - Task { @MainActor in + Task { do { let request = RPCRequest( method: ChatClientRequest.chatInvite.method, diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index b85728545..a482f2b21 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -4,6 +4,20 @@ public final class Web3Inbox { /// Web3Inbox client instance public static var instance: Web3InboxClient = { - return Web3InboxClientFactory.create(chatClient: Chat.instance) + guard let account = account else { + fatalError("Error - you must call Web3Inbox.configure(_:) before accessing the shared instance.") + } + return Web3InboxClientFactory.create(chatClient: Chat.instance, account: account) }() + + private static var account: Account? + + private init() { } + + /// Sign instance config method + /// - Parameters: + /// - metadata: App metadata + static public func configure(account: Account) { + Web3Inbox.account = account + } } diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 1bdfc81af..a02bb6d08 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -4,6 +4,7 @@ import WebKit public final class Web3InboxClient { private let webView: WKWebView + private var account: Account private let logger: ConsoleLogging private let clientProxy: ChatClientProxy @@ -14,6 +15,7 @@ public final class Web3InboxClient { init( webView: WKWebView, + account: Account, logger: ConsoleLogging, clientProxy: ChatClientProxy, clientSubscriber: ChatClientRequestSubscriber, @@ -21,6 +23,7 @@ public final class Web3InboxClient { webviewSubscriber: WebViewRequestSubscriber ) { self.webView = webView + self.account = account self.logger = logger self.clientProxy = clientProxy self.clientSubscriber = clientSubscriber @@ -33,6 +36,10 @@ public final class Web3InboxClient { public func getWebView() -> WKWebView { return webView } + + public func setAccount(_ account: Account) async throws { + try await authorize(account: account) + } } // MARK: - Privates @@ -40,6 +47,9 @@ public final class Web3InboxClient { private extension Web3InboxClient { func setupSubscriptions() { + webviewSubscriber.onLogin = { [unowned self] in + try await self.authorize(account: self.account) + } webviewSubscriber.onRequest = { [unowned self] request in try await self.clientProxy.request(request) } @@ -50,4 +60,14 @@ private extension Web3InboxClient { try await self.webviewProxy.request(request) } } + + func authorize(account: Account) async throws { + self.account = account + + let request = RPCRequest( + method: ChatClientRequest.setAccount.method, + params: ["account": account.address] + ) + try await webviewProxy.request(request) + } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 4d6b29178..384b6f935 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -3,7 +3,7 @@ import WebKit final class Web3InboxClientFactory { - static func create(chatClient: ChatClient) -> Web3InboxClient { + static func create(chatClient: ChatClient, account: Account) -> Web3InboxClient { let host = "https://web3inbox-dev-hidden-git-feat-add-w3i-proxy-walletconnect1.vercel.app/?chatProvider=ios" let logger = ConsoleLogger(suffix: "📬") let webviewSubscriber = WebViewRequestSubscriber(logger: logger) @@ -14,6 +14,7 @@ final class Web3InboxClientFactory { return Web3InboxClient( webView: webView, + account: account, logger: ConsoleLogger(), clientProxy: clientProxy, clientSubscriber: clientSubscriber, diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift index 0f6e7e11e..d64642b7f 100644 --- a/Sources/Web3Inbox/WebView/WebViewEvent.swift +++ b/Sources/Web3Inbox/WebView/WebViewEvent.swift @@ -6,4 +6,5 @@ enum WebViewEvent: String { case register case getMessages case message + case accept } diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index 39fc26212..8b1cf0256 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -18,6 +18,7 @@ final class WebViewFactory { name: WebViewRequestSubscriber.name ) let webview = WKWebView(frame: .zero, configuration: configuration) + webview.navigationDelegate = webviewSubscriber let request = URLRequest(url: URL(string: host)!) webview.load(request) return webview diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index 8977bfd8b..73130fede 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -9,16 +9,18 @@ actor WebViewProxy { self.webView = webView } + @MainActor func respond(_ response: RPCResponse) async throws { let body = try response.json() - let script = formatScript(body: body) - await webView.evaluateJavaScript(script, completionHandler: nil) + let script = await formatScript(body: body) + webView.evaluateJavaScript(script, completionHandler: nil) } + @MainActor func request(_ request: RPCRequest) async throws { let body = try request.json() - let script = formatScript(body: body) - await webView.evaluateJavaScript(script, completionHandler: nil) + let script = await formatScript(body: body) + webView.evaluateJavaScript(script, completionHandler: nil) } } diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index f42751b97..fea0dc95b 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -6,6 +6,7 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { static let name = "web3inbox" var onRequest: ((RPCRequest) async throws -> Void)? + var onLogin: (() async throws -> Void)? private let logger: ConsoleLogging @@ -25,7 +26,7 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { let request = try? JSONDecoder().decode(RPCRequest.self, from: data) else { return } - Task { @MainActor in + Task { do { try await onRequest?(request) } catch { @@ -34,3 +35,16 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { } } } + +// MARK: - WKNavigationDelegate + +extension WebViewRequestSubscriber: WKNavigationDelegate { + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + guard webView.url?.path == "/login" else { return } + + Task { + try await onLogin?() + } + } +} From 078bf2672dfc2809872754208907e45a07a9b6e7 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 20:24:40 +0500 Subject: [PATCH 06/74] ChatStorage --- Sources/Chat/ChatClient.swift | 12 ++-- Sources/Chat/ChatClientFactory.swift | 6 +- Sources/Chat/ChatStorage.swift | 28 ++++++++ .../Invitee/InvitationHandlingService.swift | 70 +++++++++++++------ .../Inviter/InviteService.swift | 2 +- Sources/Chat/Types/Invite.swift | 29 ++++---- Sources/JSONRPC/RPCID.swift | 18 +++++ .../RPCHistory/RPCHistory.swift | 20 +++++- 8 files changed, 137 insertions(+), 48 deletions(-) create mode 100644 Sources/Chat/ChatStorage.swift diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index 373106ea2..0236501b9 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -13,7 +13,7 @@ public class ChatClient { private let kms: KeyManagementService private let threadStore: Database private let messagesStore: Database - private let invitePayloadStore: CodableStore> + private let chatStorage: ChatStorage public let socketConnectionStatusPublisher: AnyPublisher @@ -44,7 +44,7 @@ public class ChatClient { kms: KeyManagementService, threadStore: Database, messagesStore: Database, - invitePayloadStore: CodableStore>, + chatStorage: ChatStorage, socketConnectionStatusPublisher: AnyPublisher ) { self.registry = registry @@ -57,7 +57,7 @@ public class ChatClient { self.kms = kms self.threadStore = threadStore self.messagesStore = messagesStore - self.invitePayloadStore = invitePayloadStore + self.chatStorage = chatStorage self.socketConnectionStatusPublisher = socketConnectionStatusPublisher setUpEnginesCallbacks() @@ -90,11 +90,11 @@ public class ChatClient { try await inviteService.invite(peerAccount: peerAccount, openingMessage: openingMessage, account: account) } - public func accept(inviteId: String) async throws { + public func accept(inviteId: Int64) async throws { try await invitationHandlingService.accept(inviteId: inviteId) } - public func reject(inviteId: String) async throws { + public func reject(inviteId: Int64) async throws { try await invitationHandlingService.reject(inviteId: inviteId) } @@ -118,7 +118,7 @@ public class ChatClient { public func getInvites(account: Account) -> [Invite] { // TODO: Account based storage - return invitePayloadStore.getAll().map { $0.request } + return chatStorage.getInvites() } public func getThreads(account: Account) async -> [Thread] { diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 84b79bc4c..1688d8366 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -26,11 +26,11 @@ public struct ChatClientFactory { let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) - let invitePayloadStore = CodableStore>(defaults: keyValueStorage, identifier: ChatStorageIdentifiers.invite.rawValue) + let chatStorage = ChatStorage(history: rpcHistory) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let threadStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, threadStore: threadStore, logger: logger) - let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, invitePayloadStore: invitePayloadStore, threadsStore: threadStore) + let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage, threadsStore: threadStore) let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, threadStore: threadStore, rpcHistory: rpcHistory, logger: logger, registry: registry) let leaveService = LeaveService() let messagesStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue) @@ -47,7 +47,7 @@ public struct ChatClientFactory { kms: kms, threadStore: threadStore, messagesStore: messagesStore, - invitePayloadStore: invitePayloadStore, + chatStorage: chatStorage, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher ) diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift new file mode 100644 index 000000000..4227ea50e --- /dev/null +++ b/Sources/Chat/ChatStorage.swift @@ -0,0 +1,28 @@ +import Foundation + +struct ChatStorage { + + let history: RPCHistory + + func getInvite(id: Int64) -> Invite? { + guard + let record = history.get(recordId: RPCID(id)), + let payload = try? record.request.params?.get(InvitePayload.self) + else { return nil } + + return Invite(id: record.id.integer, payload: payload) + } + + func getInviteTopic(id: Int64) -> String? { + return history.get(recordId: RPCID(id))?.topic + } + + func getInvites() -> [Invite] { + return history.getAllWithIDs(of: InvitePayload.self) + .map { Invite(id: $0.id.integer, payload: $0.value) } + } + + func delete(invite: Invite) { + history.delete(id: RPCID(invite.id)) + } +} diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index db0c13599..4195569f9 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -8,7 +8,7 @@ class InvitationHandlingService { var onInvite: ((Invite) -> Void)? var onNewThread: ((Thread) -> Void)? private let networkingInteractor: NetworkInteracting - private let invitePayloadStore: CodableStore> + private let chatStorage: ChatStorage private let topicToRegistryRecordStore: CodableStore private let registry: Registry private let logger: ConsoleLogging @@ -21,32 +21,42 @@ class InvitationHandlingService { kms: KeyManagementService, logger: ConsoleLogging, topicToRegistryRecordStore: CodableStore, - invitePayloadStore: CodableStore>, + chatStorage: ChatStorage, threadsStore: Database) { self.registry = registry self.kms = kms self.networkingInteractor = networkingInteractor self.logger = logger self.topicToRegistryRecordStore = topicToRegistryRecordStore - self.invitePayloadStore = invitePayloadStore + self.chatStorage = chatStorage self.threadsStore = threadsStore setUpRequestHandling() } - func accept(inviteId: String) async throws { - let protocolMethod = ChatInviteProtocolMethod() - - guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound } + func accept(inviteId: Int64) async throws { + guard + let invite = chatStorage.getInvite(id: inviteId), + let inviteTopic = chatStorage.getInviteTopic(id: inviteId) + else { throw Error.inviteForIdNotFound } let selfThreadPubKey = try kms.createX25519KeyPair() let inviteResponse = InviteResponse(publicKey: selfThreadPubKey.hexRepresentation) - let response = RPCResponse(id: payload.id, result: inviteResponse) - let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) - try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: protocolMethod) - - let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: payload.request.publicKey) + let responseTopic = try getInviteResponseTopic( + requestTopic: inviteTopic, + invite: invite + ) + try await networkingInteractor.respond( + topic: responseTopic, + response: RPCResponse(id: inviteId, result: inviteResponse), + protocolMethod: ChatInviteProtocolMethod() + ) + + let threadAgreementKeys = try kms.performKeyAgreement( + selfPublicKey: selfThreadPubKey, + peerPublicKey: invite.publicKey + ) let threadTopic = threadAgreementKeys.derivedTopic() try kms.setSymmetricKey(threadAgreementKeys.sharedKey, for: threadTopic) try await networkingInteractor.subscribe(topic: threadTopic) @@ -54,31 +64,45 @@ class InvitationHandlingService { logger.debug("Accepting an invite on topic: \(threadTopic)") // TODO - derive account - let selfAccount = try! topicToRegistryRecordStore.get(key: payload.topic)!.account - let thread = Thread(topic: threadTopic, selfAccount: selfAccount, peerAccount: payload.request.account) + let selfAccount = try! topicToRegistryRecordStore.get(key: inviteTopic)!.account + let thread = Thread( + topic: threadTopic, + selfAccount: selfAccount, + peerAccount: invite.account + ) await threadsStore.add(thread) - invitePayloadStore.delete(forKey: inviteId) + chatStorage.delete(invite: invite) onNewThread?(thread) } - func reject(inviteId: String) async throws { - guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound } + func reject(inviteId: Int64) async throws { + guard + let invite = chatStorage.getInvite(id: inviteId), + let inviteTopic = chatStorage.getInviteTopic(id: inviteId) + else { throw Error.inviteForIdNotFound } - let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) + let responseTopic = try getInviteResponseTopic(requestTopic: inviteTopic, invite: invite) - try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, protocolMethod: ChatInviteProtocolMethod(), reason: ChatError.userRejected) + try await networkingInteractor.respondError( + topic: responseTopic, + requestId: RPCID(inviteId), + protocolMethod: ChatInviteProtocolMethod(), + reason: ChatError.userRejected + ) - invitePayloadStore.delete(forKey: inviteId) + chatStorage.delete(invite: invite) } private func setUpRequestHandling() { networkingInteractor.requestSubscription(on: ChatInviteProtocolMethod()) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in + .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("did receive an invite") - invitePayloadStore.set(payload, forKey: payload.request.publicKey) - onInvite?(payload.request) + onInvite?(Invite( + id: payload.id.integer, + payload: payload.request + )) }.store(in: &publishers) } diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index f8b2339c3..851a61f8a 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -37,7 +37,7 @@ class InviteService { let protocolMethod = ChatInviteProtocolMethod() self.peerAccount = peerAccount let selfPubKeyY = try kms.createX25519KeyPair() - let invite = Invite(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation) + let invite = InvitePayload(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation) let peerPubKey = try await registry.resolve(account: peerAccount) let symKeyI = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: peerPubKey) let inviteTopic = try AgreementPublicKey(hex: peerPubKey).rawRepresentation.sha256().toHexString() diff --git a/Sources/Chat/Types/Invite.swift b/Sources/Chat/Types/Invite.swift index dc3dcb490..5ca3bd37d 100644 --- a/Sources/Chat/Types/Invite.swift +++ b/Sources/Chat/Types/Invite.swift @@ -1,22 +1,25 @@ import Foundation -struct InviteResponse: Codable { - let publicKey: String -} - -public struct Invite: Codable, Equatable { - public var id: String { - return publicKey - } +public struct Invite: Codable { + public let id: Int64 public let message: String public let account: Account public let publicKey: String - static var tag: Int { - return 2000 + init(id: Int64, payload: InvitePayload) { + self.id = id + self.message = payload.message + self.account = payload.account + self.publicKey = payload.publicKey } +} - static var method: String { - return "wc_chatInvite" - } +struct InviteResponse: Codable { + let publicKey: String +} + +struct InvitePayload: Codable { + let message: String + let account: Account + let publicKey: String } diff --git a/Sources/JSONRPC/RPCID.swift b/Sources/JSONRPC/RPCID.swift index 4bf915fbc..c067b67ae 100644 --- a/Sources/JSONRPC/RPCID.swift +++ b/Sources/JSONRPC/RPCID.swift @@ -17,6 +17,24 @@ struct IntIdentifierGenerator: IdentifierGenerator { extension RPCID { + public var string: String { + switch self { + case .right(let int): + return int.description + case .left(let string): + return string + } + } + + public var integer: Int64 { + switch self { + case .right(let int): + return int + case .left(let string): + return Int64(string) ?? 0 + } + } + public var timestamp: Date { guard let id = self.right else { return .distantPast } let interval = TimeInterval(id / 1000 / 1000) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 8e130c106..8c1b65098 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -27,7 +27,7 @@ public final class RPCHistory { } public func get(recordId: RPCID) -> Record? { - try? storage.get(key: "\(recordId)") + try? storage.get(key: recordId.string) } public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws { @@ -65,11 +65,27 @@ public final class RPCHistory { } } + public func getAll(of type: Object.Type) -> [Object] { + return getAllWithIDs(of: type).map { $0.value } + } + + public func getAllWithIDs(of type: Object.Type) -> [(id: RPCID, value: Object)] { + return storage.getAll().compactMap { record in + guard let object = try? record.request.params?.get(Object.self) + else { return nil } + return (record.id, object) + } + } + + public func delete(id: RPCID) { + storage.delete(forKey: id.string) + } + public func deleteAll(forTopic topic: String) { deleteAll(forTopics: [topic]) } public func getPending() -> [Record] { - storage.getAll().filter {$0.response == nil} + storage.getAll().filter { $0.response == nil } } } From ffd94e4fa608e89811b15df3c30cf74b38371392 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 20:34:51 +0500 Subject: [PATCH 07/74] IntegrationTests fix --- Sources/Chat/ProtocolServices/Inviter/InviteService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index 851a61f8a..ae273017a 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -60,7 +60,7 @@ class InviteService { private func setUpResponseHandling() { networkingInteractor.responseSubscription(on: ChatInviteProtocolMethod()) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Invite has been accepted") Task(priority: .background) { From 253e372d8ebaca73d9995f5996914fcfae765fb7 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 20:49:16 +0500 Subject: [PATCH 08/74] Accept repaired --- Sources/Web3Inbox/ChatClient/ChatClientProxy.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index e9c8003d2..cfe869a2f 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -41,7 +41,7 @@ final class ChatClientProxy { case .accept: let params = try parse(AcceptRequest.self, params: request.params) - try await client.accept(inviteId: params.id.description) + try await client.accept(inviteId: params.id) } } } @@ -74,7 +74,7 @@ private extension ChatClientProxy { } struct AcceptRequest: Codable { - let id: Int + let id: Int64 } func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { From 164fe4ebc6d552eac2ae11ed13783af226205043 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 21:10:08 +0500 Subject: [PATCH 09/74] Messages and Thread storages removed --- Sources/Chat/ChatClient.swift | 14 ++----- Sources/Chat/ChatClientFactory.swift | 12 ++---- Sources/Chat/ChatStorage.swift | 16 ++++++++ Sources/Chat/ChatStorageIdentifiers.swift | 3 -- .../Common/MessagingService.swift | 38 ++++++++++--------- .../Common/ResubscriptionService.swift | 13 ++++--- .../Invitee/InvitationHandlingService.swift | 17 +++------ .../Inviter/InviteService.swift | 21 +++++----- .../NetworkInteracting.swift | 1 + .../NetworkingInteractor.swift | 4 ++ Sources/WalletConnectRelay/RelayClient.swift | 10 +++++ 11 files changed, 84 insertions(+), 65 deletions(-) diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index 0236501b9..97ea14e4a 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -11,8 +11,6 @@ public class ChatClient { private let leaveService: LeaveService private let resubscriptionService: ResubscriptionService private let kms: KeyManagementService - private let threadStore: Database - private let messagesStore: Database private let chatStorage: ChatStorage public let socketConnectionStatusPublisher: AnyPublisher @@ -42,8 +40,6 @@ public class ChatClient { leaveService: LeaveService, resubscriptionService: ResubscriptionService, kms: KeyManagementService, - threadStore: Database, - messagesStore: Database, chatStorage: ChatStorage, socketConnectionStatusPublisher: AnyPublisher ) { @@ -55,8 +51,6 @@ public class ChatClient { self.leaveService = leaveService self.resubscriptionService = resubscriptionService self.kms = kms - self.threadStore = threadStore - self.messagesStore = messagesStore self.chatStorage = chatStorage self.socketConnectionStatusPublisher = socketConnectionStatusPublisher @@ -121,13 +115,13 @@ public class ChatClient { return chatStorage.getInvites() } - public func getThreads(account: Account) async -> [Thread] { + public func getThreads(account: Account) -> [Thread] { // TODO: Account based storage - await threadStore.getAll() + return chatStorage.getThreads() } - public func getMessages(topic: String) async -> [Message] { - await messagesStore.filter {$0.topic == topic} ?? [] + public func getMessages(topic: String) -> [Message] { + return chatStorage.getMessages(topic: topic) } private func setUpEnginesCallbacks() { diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 1688d8366..22f456ab1 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -28,13 +28,11 @@ public struct ChatClientFactory { let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) let chatStorage = ChatStorage(history: rpcHistory) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) - let threadStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) - let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, threadStore: threadStore, logger: logger) - let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage, threadsStore: threadStore) - let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, threadStore: threadStore, rpcHistory: rpcHistory, logger: logger, registry: registry) + let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) + let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) + let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry) let leaveService = LeaveService() - let messagesStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue) - let messagingService = MessagingService(networkingInteractor: networkingInteractor, messagesStore: messagesStore, threadStore: threadStore, logger: logger) + let messagingService = MessagingService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) let client = ChatClient( registry: registry, @@ -45,8 +43,6 @@ public struct ChatClientFactory { leaveService: leaveService, resubscriptionService: resubscriptionService, kms: kms, - threadStore: threadStore, - messagesStore: messagesStore, chatStorage: chatStorage, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher ) diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift index 4227ea50e..b79c170fa 100644 --- a/Sources/Chat/ChatStorage.swift +++ b/Sources/Chat/ChatStorage.swift @@ -4,6 +4,8 @@ struct ChatStorage { let history: RPCHistory + // MARK: - Invites + func getInvite(id: Int64) -> Invite? { guard let record = history.get(recordId: RPCID(id)), @@ -25,4 +27,18 @@ struct ChatStorage { func delete(invite: Invite) { history.delete(id: RPCID(invite.id)) } + + // MARK: - Threads + + func getThreads() -> [Thread] { + return history.getAll(of: Thread.self) + } + + func getThread(topic: String) -> Thread? { + return getThreads().first(where: { $0.topic == topic }) + } + + func getMessages(topic: String) -> [Message] { + return history.getAll(of: Message.self).filter { $0.topic == topic } + } } diff --git a/Sources/Chat/ChatStorageIdentifiers.swift b/Sources/Chat/ChatStorageIdentifiers.swift index bcf560ed3..1177beffe 100644 --- a/Sources/Chat/ChatStorageIdentifiers.swift +++ b/Sources/Chat/ChatStorageIdentifiers.swift @@ -2,8 +2,5 @@ import Foundation enum ChatStorageIdentifiers: String { case topicToInvitationPubKey = "com.walletconnect.chat.topicToInvitationPubKey" - case invite = "com.walletconnect.chat.invite" case jsonRpcHistory = "com.walletconnect.chat.jsonRpcHistory" - case threads = "com.walletconnect.chat.threads" - case messages = "com.walletconnect.chat.messages" } diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index b9de3db59..ef5e06dce 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -5,38 +5,39 @@ class MessagingService { enum Errors: Error { case threadDoNotExist } + + var onMessage: ((Message) -> Void)? + let networkingInteractor: NetworkInteracting - var messagesStore: Database + let chatStorage: ChatStorage let logger: ConsoleLogging - var onMessage: ((Message) -> Void)? - var threadStore: Database + private var publishers = [AnyCancellable]() init(networkingInteractor: NetworkInteracting, - messagesStore: Database, - threadStore: Database, + chatStorage: ChatStorage, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor - self.messagesStore = messagesStore + self.chatStorage = chatStorage self.logger = logger - self.threadStore = threadStore setUpResponseHandling() setUpRequestHandling() } func send(topic: String, messageString: String) async throws { // TODO - manage author account - let protocolMethod = ChatMessageProtocolMethod() - let thread = await threadStore.first {$0.topic == topic} - guard let authorAccount = thread?.selfAccount else { throw Errors.threadDoNotExist} + + guard let authorAccount = chatStorage.getThread(topic: topic)?.selfAccount + else { throw Errors.threadDoNotExist} + let timestamp = Int64(Date().timeIntervalSince1970 * 1000) let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp) + + let protocolMethod = ChatMessageProtocolMethod() let request = RPCRequest(method: protocolMethod.method, params: message) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) - Task(priority: .background) { - await messagesStore.add(message) - onMessage?(message) - } + + onMessage?(message) } private func setUpResponseHandling() { @@ -56,9 +57,12 @@ class MessagingService { } private func handleMessage(_ message: Message, topic: String, requestId: RPCID) { - Task(priority: .background) { - try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, protocolMethod: ChatMessageProtocolMethod()) - await messagesStore.add(message) + Task(priority: .high) { + try await networkingInteractor.respondSuccess( + topic: topic, + requestId: requestId, + protocolMethod: ChatMessageProtocolMethod() + ) logger.debug("Received message") onMessage?(message) } diff --git a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift index b01b901ae..0cb40299f 100644 --- a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift +++ b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift @@ -4,15 +4,15 @@ import Combine class ResubscriptionService { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging - private var threadStore: Database + private var chatStorage: ChatStorage private var publishers = [AnyCancellable]() init(networkingInteractor: NetworkInteracting, - threadStore: Database, + chatStorage: ChatStorage, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor self.logger = logger - self.threadStore = threadStore + self.chatStorage = chatStorage setUpResubscription() } @@ -20,9 +20,10 @@ class ResubscriptionService { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in guard status == .connected else { return } - Task(priority: .background) { - let topics = await threadStore.getAll().map {$0.topic} - topics.forEach { topic in Task(priority: .background) { try? await networkingInteractor.subscribe(topic: topic) } } + + Task(priority: .high) { + let topics = chatStorage.getThreads().map { $0.topic } + try await networkingInteractor.batchSubscribe(topics: topics) } }.store(in: &publishers) } diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index 4195569f9..84ee52520 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -13,7 +13,6 @@ class InvitationHandlingService { private let registry: Registry private let logger: ConsoleLogging private let kms: KeyManagementService - private let threadsStore: Database private var publishers = [AnyCancellable]() init(registry: Registry, @@ -21,15 +20,13 @@ class InvitationHandlingService { kms: KeyManagementService, logger: ConsoleLogging, topicToRegistryRecordStore: CodableStore, - chatStorage: ChatStorage, - threadsStore: Database) { + chatStorage: ChatStorage) { self.registry = registry self.kms = kms self.networkingInteractor = networkingInteractor self.logger = logger self.topicToRegistryRecordStore = topicToRegistryRecordStore self.chatStorage = chatStorage - self.threadsStore = threadsStore setUpRequestHandling() } @@ -65,16 +62,14 @@ class InvitationHandlingService { // TODO - derive account let selfAccount = try! topicToRegistryRecordStore.get(key: inviteTopic)!.account - let thread = Thread( - topic: threadTopic, - selfAccount: selfAccount, - peerAccount: invite.account - ) - await threadsStore.add(thread) chatStorage.delete(invite: invite) - onNewThread?(thread) + onNewThread?(Thread( + topic: threadTopic, + selfAccount: selfAccount, + peerAccount: invite.account + )) } func reject(inviteId: Int64) async throws { diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index ae273017a..7f2110173 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -6,8 +6,7 @@ class InviteService { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging private let kms: KeyManagementService - private let threadStore: Database - private let rpcHistory: RPCHistory + private let chatStorage: ChatStorage private let registry: Registry var onNewThread: ((Thread) -> Void)? @@ -16,16 +15,14 @@ class InviteService { init( networkingInteractor: NetworkInteracting, kms: KeyManagementService, - threadStore: Database, - rpcHistory: RPCHistory, + chatStorage: ChatStorage, logger: ConsoleLogging, registry: Registry ) { self.kms = kms self.networkingInteractor = networkingInteractor self.logger = logger - self.threadStore = threadStore - self.rpcHistory = rpcHistory + self.chatStorage = chatStorage self.registry = registry setUpResponseHandling() } @@ -63,7 +60,7 @@ class InviteService { .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Invite has been accepted") - Task(priority: .background) { + Task(priority: .high) { try await createThread( selfPubKeyHex: payload.request.publicKey, peerPubKey: payload.response.publicKey, @@ -79,10 +76,14 @@ class InviteService { let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) let threadTopic = agreementKeys.derivedTopic() try kms.setSymmetricKey(agreementKeys.sharedKey, for: threadTopic) + try await networkingInteractor.subscribe(topic: threadTopic) - let thread = Thread(topic: threadTopic, selfAccount: account, peerAccount: peerAccount) - await threadStore.add(thread) - onNewThread?(thread) + + onNewThread?(Thread( + topic: threadTopic, + selfAccount: account, + peerAccount: peerAccount) + ) // TODO - remove symKeyI } } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index ea759adfe..109415ad6 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -6,6 +6,7 @@ public protocol NetworkInteracting { var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) + func batchSubscribe(topics: [String]) async throws func batchUnsubscribe(topics: [String]) async throws func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 9e72ba110..16f551f82 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -56,6 +56,10 @@ public class NetworkingInteractor: NetworkInteracting { } } + public func batchSubscribe(topics: [String]) async throws { + try await relayClient.batchSubscribe(topics: topics) + } + public func batchUnsubscribe(topics: [String]) async throws { try await relayClient.batchUnsubscribe(topics: topics) rpcHistory.deleteAll(forTopics: topics) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 1a618bda3..5d51bbf0a 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -213,6 +213,16 @@ public final class RelayClient { } } + public func batchSubscribe(topics: [String]) async throws { + await withThrowingTaskGroup(of: Void.self) { group in + for topic in topics { + group.addTask { + try await self.subscribe(topic: topic) + } + } + } + } + public func batchUnsubscribe(topics: [String]) async throws { await withThrowingTaskGroup(of: Void.self) { group in for topic in topics { From 297247fd5d3f73ab3ef2563ec3ce372e2b117bce Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 21:38:49 +0500 Subject: [PATCH 10/74] Thread test repaired --- Sources/Chat/ChatClientFactory.swift | 3 ++- Sources/Chat/ChatStorage.swift | 16 ++++++++++++++-- Sources/Chat/ChatStorageIdentifiers.swift | 1 + Sources/Chat/Database.swift | 18 +++++++++--------- .../Common/MessagingService.swift | 6 +++--- .../Invitee/InvitationHandlingService.swift | 11 +++++++---- .../Inviter/InviteService.swift | 8 ++++++-- .../NetworkingInteractorMock.swift | 6 ++++++ 8 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 22f456ab1..513316e26 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -26,7 +26,8 @@ public struct ChatClientFactory { let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) - let chatStorage = ChatStorage(history: rpcHistory) + let threadStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) + let chatStorage = ChatStorage(history: rpcHistory, threadStore: threadStore) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift index b79c170fa..0d7c0f00e 100644 --- a/Sources/Chat/ChatStorage.swift +++ b/Sources/Chat/ChatStorage.swift @@ -2,7 +2,13 @@ import Foundation struct ChatStorage { - let history: RPCHistory + private let history: RPCHistory + private let threadStore: Database + + init(history: RPCHistory, threadStore: Database) { + self.history = history + self.threadStore = threadStore + } // MARK: - Invites @@ -31,13 +37,19 @@ struct ChatStorage { // MARK: - Threads func getThreads() -> [Thread] { - return history.getAll(of: Thread.self) + return threadStore.getAll() } func getThread(topic: String) -> Thread? { return getThreads().first(where: { $0.topic == topic }) } + func add(thread: Thread) { + threadStore.add(thread) + } + + // MARK: - Messages + func getMessages(topic: String) -> [Message] { return history.getAll(of: Message.self).filter { $0.topic == topic } } diff --git a/Sources/Chat/ChatStorageIdentifiers.swift b/Sources/Chat/ChatStorageIdentifiers.swift index 1177beffe..6da01afe4 100644 --- a/Sources/Chat/ChatStorageIdentifiers.swift +++ b/Sources/Chat/ChatStorageIdentifiers.swift @@ -2,5 +2,6 @@ import Foundation enum ChatStorageIdentifiers: String { case topicToInvitationPubKey = "com.walletconnect.chat.topicToInvitationPubKey" + case threads = "com.walletconnect.chat.threads" case jsonRpcHistory = "com.walletconnect.chat.jsonRpcHistory" } diff --git a/Sources/Chat/Database.swift b/Sources/Chat/Database.swift index 08e6f1360..6d8aa098b 100644 --- a/Sources/Chat/Database.swift +++ b/Sources/Chat/Database.swift @@ -6,32 +6,32 @@ class Database where Element: Codable { private let keyValueStorage: KeyValueStorage private let identifier: String - init(keyValueStorage: KeyValueStorage, - identifier: String) { + init(keyValueStorage: KeyValueStorage, identifier: String) { self.keyValueStorage = keyValueStorage self.identifier = identifier + if let data = keyValueStorage.object(forKey: identifier) as? Data, let decoded = try? JSONDecoder().decode([Element].self, from: data) { array = decoded } } - func filter(_ isIncluded: (Element) -> Bool) async -> [Element]? { - return Array(self.array.filter(isIncluded)) + func filter(_ isIncluded: (Element) -> Bool) -> [Element]? { + return Array(array.filter(isIncluded)) } - func getAll() async -> [Element] { + func getAll() -> [Element] { array } - func add(_ element: Element) async { - self.array.append(element) + func add(_ element: Element) { + array.append(element) if let encoded = try? JSONEncoder().encode(array) { keyValueStorage.set(encoded, forKey: identifier) } } - func first(where predicate: (Element) -> Bool) async -> Element? { - self.array.first(where: predicate) + func first(where predicate: (Element) -> Bool) -> Element? { + array.first(where: predicate) } } diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index ef5e06dce..acd5ff051 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -8,9 +8,9 @@ class MessagingService { var onMessage: ((Message) -> Void)? - let networkingInteractor: NetworkInteracting - let chatStorage: ChatStorage - let logger: ConsoleLogging + private let networkingInteractor: NetworkInteracting + private let chatStorage: ChatStorage + private let logger: ConsoleLogging private var publishers = [AnyCancellable]() diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index 84ee52520..fd7039aba 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -63,13 +63,16 @@ class InvitationHandlingService { // TODO - derive account let selfAccount = try! topicToRegistryRecordStore.get(key: inviteTopic)!.account - chatStorage.delete(invite: invite) - - onNewThread?(Thread( + let thread = Thread( topic: threadTopic, selfAccount: selfAccount, peerAccount: invite.account - )) + ) + + chatStorage.add(thread: thread) + chatStorage.delete(invite: invite) + + onNewThread?(thread) } func reject(inviteId: Int64) async throws { diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index 7f2110173..c030ede0c 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -79,11 +79,15 @@ class InviteService { try await networkingInteractor.subscribe(topic: threadTopic) - onNewThread?(Thread( + let thread = Thread( topic: threadTopic, selfAccount: account, - peerAccount: peerAccount) + peerAccount: peerAccount ) + + chatStorage.add(thread: thread) + + onNewThread?(thread) // TODO - remove symKeyI } } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 323c52279..86f7cee52 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -105,6 +105,12 @@ public class NetworkingInteractorMock: NetworkInteracting { } } + public func batchSubscribe(topics: [String]) async throws { + for topic in topics { + try await subscribe(topic: topic) + } + } + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { requestCallCount += 1 requests.append((topic, request)) From 2fc419a6c175f09aa763238768149695859dad6a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Jan 2023 22:09:00 +0500 Subject: [PATCH 11/74] newThreadPublisher subscriber --- .../ChatClient/ChatClientProxy.swift | 4 +-- .../ChatClient/ChatClientRequest.swift | 1 + .../ChatClientRequestSubscriber.swift | 35 +++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index cfe869a2f..3ba7dd8d3 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -22,7 +22,7 @@ final class ChatClientProxy { case .getThreads: let params = try parse(GetThreadsRequest.self, params: request.params) - let threads = await client.getThreads(account: params.account) + let threads = client.getThreads(account: params.account) try await respond(with: threads, request: request) case .register: @@ -31,7 +31,7 @@ final class ChatClientProxy { case .getMessages: let params = try parse(GetMessagesRequest.self, params: request.params) - let messages = await client.getMessages(topic: params.topic) + let messages = client.getMessages(topic: params.topic) try await respond(with: messages, request: request) case .message: diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index 6c038b981..0e1bd63d2 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -2,6 +2,7 @@ import Foundation enum ChatClientRequest: String { case chatInvite = "chat_invite" + case chatThread = "chat_thread" case setAccount = "setAccount" var method: String { diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift index ac0a3f91f..28d4e82a7 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift @@ -20,17 +20,30 @@ final class ChatClientRequestSubscriber { func setupSubscriptions() { chatClient.invitePublisher .sink { [unowned self] invite in - Task { - do { - let request = RPCRequest( - method: ChatClientRequest.chatInvite.method, - params: invite - ) - try await onRequest?(request) - } catch { - logger.error("Client Request error: \(error.localizedDescription)") - } - } + handle(event: .chatInvite, params: invite) }.store(in: &publishers) + + // TODO: Should we listen it? + chatClient.newThreadPublisher + .sink { [unowned self] thread in + handle(event: .chatThread, params: thread) + }.store(in: &publishers) + } +} + +private extension ChatClientRequestSubscriber { + + func handle(event: ChatClientRequest, params: Codable) { + Task { + do { + let request = RPCRequest( + method: event.method, + params: params + ) + try await onRequest?(request) + } catch { + logger.error("Client Request error: \(error.localizedDescription)") + } + } } } From 8917cbddc5a4862c7b5dead03eb22b147a92eb37 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Jan 2023 17:39:18 +0500 Subject: [PATCH 12/74] Responses ack + invites fix --- Sources/Web3Inbox/ChatClient/ChatClientProxy.swift | 9 +++++++-- Sources/Web3Inbox/ChatClient/ChatClientRequest.swift | 1 + .../ChatClient/ChatClientRequestSubscriber.swift | 6 +++++- Sources/Web3Inbox/Web3InboxClientFactory.swift | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index 3ba7dd8d3..24379b9c9 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -28,6 +28,7 @@ final class ChatClientProxy { case .register: let params = try parse(RegisterRequest.self, params: request.params) try await client.register(account: params.account) + try await respond(request: request) case .getMessages: let params = try parse(GetMessagesRequest.self, params: request.params) @@ -42,11 +43,15 @@ final class ChatClientProxy { case .accept: let params = try parse(AcceptRequest.self, params: request.params) try await client.accept(inviteId: params.id) + try await respond(request: request) } } } private extension ChatClientProxy { + + private typealias Blob = Dictionary + enum Errors: Error { case unregisteredMethod case unregisteredParams @@ -78,12 +83,12 @@ private extension ChatClientProxy { } func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { - guard let params = try? params?.get([Request].self).first + guard let params = try params?.get([Request].self).first else { throw Errors.unregisteredParams } return params } - func respond(with object: Object, request: RPCRequest) async throws { + func respond(with object: Object = Blob(), request: RPCRequest) async throws { let response = RPCResponse(matchingRequest: request, result: object) try await onResponse?(response) } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index 0e1bd63d2..b99372d6f 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -3,6 +3,7 @@ import Foundation enum ChatClientRequest: String { case chatInvite = "chat_invite" case chatThread = "chat_thread" + case chatMessage = "chat_message" case setAccount = "setAccount" var method: String { diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift index 28d4e82a7..12d3c24b1 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequestSubscriber.swift @@ -23,11 +23,15 @@ final class ChatClientRequestSubscriber { handle(event: .chatInvite, params: invite) }.store(in: &publishers) - // TODO: Should we listen it? chatClient.newThreadPublisher .sink { [unowned self] thread in handle(event: .chatThread, params: thread) }.store(in: &publishers) + + chatClient.messagePublisher + .sink { [unowned self] message in + handle(event: .chatMessage, params: message) + }.store(in: &publishers) } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 384b6f935..b24e1993b 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -4,7 +4,7 @@ import WebKit final class Web3InboxClientFactory { static func create(chatClient: ChatClient, account: Account) -> Web3InboxClient { - let host = "https://web3inbox-dev-hidden-git-feat-add-w3i-proxy-walletconnect1.vercel.app/?chatProvider=ios" + let host = "https://web3inbox-dev-hidden-git-feat-figma-matching-walletconnect1.vercel.app/?chatProvider=ios" let logger = ConsoleLogger(suffix: "📬") let webviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() From 0041b8e70d6a9df116d14204312920f2bda74096 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Jan 2023 16:58:01 +0500 Subject: [PATCH 13/74] Expiry validation on request received --- .../Engine/Common/SessionEngine.swift | 11 +++++-- Sources/WalletConnectSign/Request.swift | 33 ++++++++++++++++--- .../WalletConnectSign/Sign/SignClient.swift | 4 +-- .../Types/Session/SessionType.swift | 1 + .../Types/SignReasonCode.swift | 7 ++++ Tests/WalletConnectSignTests/Stub/Stubs.swift | 2 +- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 7f552ca32..98de13b0a 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -54,7 +54,7 @@ final class SessionEngine { guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { throw WalletConnectError.invalidPermissions } - let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) + let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params, expiry: request.expiry) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) let protocolMethod = SessionRequestProtocolMethod() let rpcRequest = RPCRequest(method: protocolMethod.method, params: sessionRequestParams) @@ -191,7 +191,9 @@ private extension SessionEngine { topic: payload.topic, method: payload.request.request.method, params: payload.request.request.params, - chainId: payload.request.chainId) + chainId: payload.request.chainId, + expiry: payload.request.request.expiry + ) guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) @@ -202,6 +204,11 @@ private extension SessionEngine { guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { return respondError(payload: payload, reason: .unauthorizedMethod(request.method), protocolMethod: protocolMethod) } + + guard !request.isExpired() else { + return respondError(payload: payload, reason: .sessionRequestExpired, protocolMethod: protocolMethod) + } + onSessionRequest?(request) } diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index 4820b8fc0..e41ea77de 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -6,20 +6,43 @@ public struct Request: Codable, Equatable { public let method: String public let params: AnyCodable public let chainId: Blockchain + public let expiry: UInt64? - internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain) { + internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain, expiry: UInt64?) { self.id = id self.topic = topic self.method = method self.params = params self.chainId = chainId + self.expiry = expiry } - public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain) { - self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId) + public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain, expiry: UInt64? = nil) { + self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId, expiry: expiry) } - internal init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable { - self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId) + init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain, expiry: UInt64?) where C: Codable { + self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId, expiry: expiry) + } + + func isExpired() -> Bool { + guard let expiry = expiry else { return false } + + let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) + + guard + Date().distance(to: expiryDate) > Constants.maxExpiry, + Date().distance(to: expiryDate) < Constants.minExpiry + else { return true } + + return expiryDate < Date() + } +} + +private extension Request { + + struct Constants { + static let minExpiry: TimeInterval = 300 // 5 minutes + static let maxExpiry: TimeInterval = 604800 // 7 days } } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 6d015414e..b2f539272 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -326,7 +326,7 @@ public final class SignClient: SignClientProtocol { let pendingRequests: [Request] = history.getPending() .compactMap { guard let request = try? $0.request.params?.get(SessionType.RequestParams.self) else { return nil } - return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId) + return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId, expiry: request.request.expiry) } if let topic = topic { return pendingRequests.filter {$0.topic == topic} @@ -343,7 +343,7 @@ public final class SignClient: SignClientProtocol { let request = try? record.request.params?.get(SessionType.RequestParams.self) else { return nil } - return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId) + return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId, expiry: request.request.expiry) } /// Delete all stored data such as: pairings, sessions, keys diff --git a/Sources/WalletConnectSign/Types/Session/SessionType.swift b/Sources/WalletConnectSign/Types/Session/SessionType.swift index d6c7c87d5..2731e448c 100644 --- a/Sources/WalletConnectSign/Types/Session/SessionType.swift +++ b/Sources/WalletConnectSign/Types/Session/SessionType.swift @@ -42,6 +42,7 @@ internal enum SessionType { struct Request: Codable, Equatable { let method: String let params: AnyCodable + let expiry: UInt64? } } diff --git a/Sources/WalletConnectSign/Types/SignReasonCode.swift b/Sources/WalletConnectSign/Types/SignReasonCode.swift index 522cbfb4f..e1435dab1 100644 --- a/Sources/WalletConnectSign/Types/SignReasonCode.swift +++ b/Sources/WalletConnectSign/Types/SignReasonCode.swift @@ -37,6 +37,9 @@ enum SignReasonCode: Reason, Codable, Equatable { // 6000 case userDisconnected + // 8000 + case sessionRequestExpired + var code: Int { switch self { case .invalidMethod: return 1001 @@ -66,6 +69,8 @@ enum SignReasonCode: Reason, Codable, Equatable { case .userDisconnected: return 6000 case .noSessionForTopic: return 7001 + + case .sessionRequestExpired: return 8000 } } @@ -117,6 +122,8 @@ enum SignReasonCode: Reason, Codable, Equatable { return "Unsupported namespace key" case .userDisconnected: return "User discconnected" + case .sessionRequestExpired: + return "Session request expired or expiry param validation failed (MIN_INTERVAL: 300, MAX_INTERVAL: 604800)" } } } diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index 3789cdb3e..1f22303f7 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -70,7 +70,7 @@ extension RPCRequest { static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest { let params = SessionType.RequestParams( - request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable())), + request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable()), expiry: nil), chainId: chainId) return RPCRequest(method: SessionRequestProtocolMethod().method, params: params) } From bf33ff1082aed31012d151eb5b82080f70c08563 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Jan 2023 19:05:10 +0500 Subject: [PATCH 14/74] Reject and invite methods --- .../Chat/Main/Model/TabPage.swift | 2 +- .../ChatClient/ChatClientProxy.swift | 23 +++++++++++++++++++ .../ChatClient/ChatClientRequest.swift | 2 +- Sources/Web3Inbox/WebView/WebViewEvent.swift | 2 ++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift index e22d69308..2e6e3b43b 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/Model/TabPage.swift @@ -23,7 +23,7 @@ enum TabPage: CaseIterable { } static var selectedIndex: Int { - return 1 + return 0 } static var enabledTabs: [TabPage] { diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index 24379b9c9..ed9ce3101 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -44,6 +44,16 @@ final class ChatClientProxy { let params = try parse(AcceptRequest.self, params: request.params) try await client.accept(inviteId: params.id) try await respond(request: request) + + case .reject: + let params = try parse(RejectRequest.self, params: request.params) + try await client.reject(inviteId: params.id) + try await respond(request: request) + + case .invite: + let params = try parse(InviteRequest.self, params: request.params) + try await client.invite(peerAccount: params.invite.account, openingMessage: params.invite.message, account: params.account) + try await respond(request: request) } } } @@ -82,6 +92,19 @@ private extension ChatClientProxy { let id: Int64 } + struct RejectRequest: Codable { + let id: Int64 + } + + struct InviteRequest: Codable { + struct Invite: Codable { + let account: Account + let message: String + } + let account: Account + let invite: Invite + } + func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { guard let params = try params?.get([Request].self).first else { throw Errors.unregisteredParams } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift index b99372d6f..c5dcb307d 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientRequest.swift @@ -2,7 +2,7 @@ import Foundation enum ChatClientRequest: String { case chatInvite = "chat_invite" - case chatThread = "chat_thread" + case chatThread = "chat_joined" case chatMessage = "chat_message" case setAccount = "setAccount" diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift index d64642b7f..b83a3249b 100644 --- a/Sources/Web3Inbox/WebView/WebViewEvent.swift +++ b/Sources/Web3Inbox/WebView/WebViewEvent.swift @@ -7,4 +7,6 @@ enum WebViewEvent: String { case getMessages case message case accept + case reject + case invite } From f43e75864136ba530c29a63eb822c41aeb532127 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Jan 2023 16:53:47 +0500 Subject: [PATCH 15/74] Configure WebView in viewWillAppear --- .../Web3Inbox/Web3InboxViewController.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index 0cc93ff37..df78b24cb 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -4,8 +4,9 @@ import WebKit final class Web3InboxViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + Web3Inbox.configure(account: Account("eip155:1:0x2F871A068a16BF4a970663dF5e417951aB79Bfd3")!) self.view = Web3Inbox.instance.getWebView() } From c7cc04da9ca4ec03aa8b888093303d171f13d1de Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 18 Jan 2023 19:44:30 +0100 Subject: [PATCH 16/74] remove unused error case --- Sources/WalletConnectPush/Push.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/WalletConnectPush/Push.swift b/Sources/WalletConnectPush/Push.swift index e7e7d6c9e..eea9a582c 100644 --- a/Sources/WalletConnectPush/Push.swift +++ b/Sources/WalletConnectPush/Push.swift @@ -4,9 +4,6 @@ import WalletConnectPairing import WalletConnectEcho public class Push { - enum Errors: Error { - case failedToGetClientId - } public static var dapp: DappPushClient = { return DappPushClientFactory.create( From 2614df4c5ac2688233a2ee637be9e23027bc6fe5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 19 Jan 2023 10:10:55 +0100 Subject: [PATCH 17/74] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 156 ++++++++++++++++++ Example/PNDecryptionService/Info.plist | 13 ++ .../NotificationService.swift | 41 +++++ .../NotificationService.swift | 1 - .../ApplicationLayer/AppDelegate.swift | 11 ++ .../Configurator/ThirdPartyConfigurator.swift | 5 + .../ApplicationLayer/SceneDelegate.swift | 3 + 7 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 Example/PNDecryptionService/Info.plist create mode 100644 Example/PNDecryptionService/NotificationService.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index d5d883e65..67717b510 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -58,6 +58,9 @@ 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; }; 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; + 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B84929787A8000428BAF /* NotificationService.swift */; }; + 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B85329787AAE00428BAF /* WalletConnectPush */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -269,6 +272,13 @@ remoteGlobalIDString = 84B767752954554A00E92316; remoteInfo = PushDecryptionService; }; + 84E6B84C29787A8000428BAF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84E6B84629787A8000428BAF; + remoteInfo = PNDecryptionService; + }; A5A4FC7D2840C5D400BBEC1E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; @@ -297,6 +307,17 @@ name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + 84E6B85229787A8000428BAF /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -353,6 +374,9 @@ 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; 84CEC64528D89D6B00D081A8 /* PairingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTests.swift; sourceTree = ""; }; 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; + 84E6B84729787A8000428BAF /* PNDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PNDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; @@ -564,6 +588,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84E6B84429787A8000428BAF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A58E7CE528729F550082D443 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -604,6 +635,7 @@ C5133A78294125CC00A8314C /* Web3 in Frameworks */, C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */, C55D349929630D440004314A /* Web3Wallet in Frameworks */, + 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */, C56EE255293F569A004840D1 /* Starscream in Frameworks */, C56EE27B293F56F8004840D1 /* WalletConnectAuth in Frameworks */, ); @@ -662,6 +694,7 @@ A58E7CE928729F550082D443 /* Showcase */, 84B767772954554A00E92316 /* PushDecryptionService */, C56EE21C293F55ED004840D1 /* WalletApp */, + 84E6B84829787A8000428BAF /* PNDecryptionService */, 764E1D3D26F8D3FC00A1FB15 /* Products */, 764E1D5326F8DAC800A1FB15 /* Frameworks */, 764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */, @@ -678,6 +711,7 @@ A58E7CE828729F550082D443 /* Showcase.app */, 84B767762954554A00E92316 /* PushDecryptionService.appex */, C56EE21B293F55ED004840D1 /* WalletApp.app */, + 84E6B84729787A8000428BAF /* PNDecryptionService.appex */, ); name = Products; sourceTree = ""; @@ -825,6 +859,15 @@ path = Auth; sourceTree = ""; }; + 84E6B84829787A8000428BAF /* PNDecryptionService */ = { + isa = PBXGroup; + children = ( + 84E6B84929787A8000428BAF /* NotificationService.swift */, + 84E6B84B29787A8000428BAF /* Info.plist */, + ); + path = PNDecryptionService; + sourceTree = ""; + }; A50F3944288005A700064555 /* Types */ = { isa = PBXGroup; children = ( @@ -1616,6 +1659,23 @@ productReference = 84CE641C27981DED00142511 /* DApp.app */; productType = "com.apple.product-type.application"; }; + 84E6B84629787A8000428BAF /* PNDecryptionService */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84E6B84F29787A8000428BAF /* Build configuration list for PBXNativeTarget "PNDecryptionService" */; + buildPhases = ( + 84E6B84329787A8000428BAF /* Sources */, + 84E6B84429787A8000428BAF /* Frameworks */, + 84E6B84529787A8000428BAF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PNDecryptionService; + productName = PNDecryptionService; + productReference = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; + productType = "com.apple.product-type.app-extension"; + }; A58E7CE728729F550082D443 /* Showcase */ = { isa = PBXNativeTarget; buildConfigurationList = A58E7CFB28729F550082D443 /* Build configuration list for PBXNativeTarget "Showcase" */; @@ -1691,10 +1751,12 @@ C56EE217293F55ED004840D1 /* Sources */, C56EE218293F55ED004840D1 /* Frameworks */, C56EE219293F55ED004840D1 /* Resources */, + 84E6B85229787A8000428BAF /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 84E6B84D29787A8000428BAF /* PBXTargetDependency */, ); name = WalletApp; packageProductDependencies = ( @@ -1704,6 +1766,7 @@ C5133A77294125CC00A8314C /* Web3 */, C55D349829630D440004314A /* Web3Wallet */, C5B2F7042970573D000DBA0E /* SolanaSwift */, + 84E6B85329787AAE00428BAF /* WalletConnectPush */, ); productName = ChatWallet; productReference = C56EE21B293F55ED004840D1 /* WalletApp.app */; @@ -1727,6 +1790,9 @@ 84CE641B27981DED00142511 = { CreatedOnToolsVersion = 13.2; }; + 84E6B84629787A8000428BAF = { + CreatedOnToolsVersion = 14.2; + }; A58E7CE728729F550082D443 = { CreatedOnToolsVersion = 13.3; }; @@ -1766,6 +1832,7 @@ A58E7CE728729F550082D443 /* Showcase */, 84B767752954554A00E92316 /* PushDecryptionService */, C56EE21A293F55ED004840D1 /* WalletApp */, + 84E6B84629787A8000428BAF /* PNDecryptionService */, ); }; /* End PBXProject section */ @@ -1796,6 +1863,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84E6B84529787A8000428BAF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A58E7CE628729F550082D443 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1900,6 +1974,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84E6B84329787A8000428BAF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A58E7CE428729F550082D443 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2101,6 +2183,11 @@ target = 84B767752954554A00E92316 /* PushDecryptionService */; targetProxy = 84B7677B2954554A00E92316 /* PBXContainerItemProxy */; }; + 84E6B84D29787A8000428BAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 84E6B84629787A8000428BAF /* PNDecryptionService */; + targetProxy = 84E6B84C29787A8000428BAF /* PBXContainerItemProxy */; + }; A5A4FC7E2840C5D400BBEC1E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 84CE641B27981DED00142511 /* DApp */; @@ -2438,6 +2525,60 @@ }; name = Release; }; + 84E6B85029787A8000428BAF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = RQ4NX29V75; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PNDecryptionService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 84E6B85129787A8000428BAF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = RQ4NX29V75; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = PNDecryptionService/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; A58E7CF928729F550082D443 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2580,6 +2721,7 @@ C56EE226293F55EE004840D1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -2610,6 +2752,7 @@ C56EE227293F55EE004840D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -2676,6 +2819,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84E6B84F29787A8000428BAF /* Build configuration list for PBXNativeTarget "PNDecryptionService" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84E6B85029787A8000428BAF /* Debug */, + 84E6B85129787A8000428BAF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A58E7CFB28729F550082D443 /* Build configuration list for PBXNativeTarget "Showcase" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2774,6 +2926,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectAuth; }; + 84E6B85329787AAE00428BAF /* WalletConnectPush */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectPush; + }; A54195A42934E83F0035AD19 /* Web3 */ = { isa = XCSwiftPackageProductDependency; package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; diff --git a/Example/PNDecryptionService/Info.plist b/Example/PNDecryptionService/Info.plist new file mode 100644 index 000000000..57421ebf9 --- /dev/null +++ b/Example/PNDecryptionService/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/Example/PNDecryptionService/NotificationService.swift b/Example/PNDecryptionService/NotificationService.swift new file mode 100644 index 000000000..3adee82cc --- /dev/null +++ b/Example/PNDecryptionService/NotificationService.swift @@ -0,0 +1,41 @@ + +import UserNotifications +import WalletConnectPush + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + if let bestAttemptContent = bestAttemptContent { + let topic = bestAttemptContent.userInfo["topic"] as! String + let ciphertext = bestAttemptContent.userInfo["ciphertext"] as! String + do { + let service = PushDecryptionService() + let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext) + bestAttemptContent.title = pushMessage.title + bestAttemptContent.body = pushMessage.body + contentHandler(bestAttemptContent) + return + } + catch { + print(error) + } + bestAttemptContent.title = "" + bestAttemptContent.body = "content not set" + contentHandler(bestAttemptContent) + } + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + +} diff --git a/Example/PushDecryptionService/NotificationService.swift b/Example/PushDecryptionService/NotificationService.swift index 737026677..689a5255c 100644 --- a/Example/PushDecryptionService/NotificationService.swift +++ b/Example/PushDecryptionService/NotificationService.swift @@ -1,4 +1,3 @@ -// import UserNotifications import WalletConnectPush diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift index 821a8f719..e5a5e4bdd 100644 --- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift @@ -1,9 +1,20 @@ import UIKit +import WalletConnectPush +import Combine @main final class AppDelegate: UIResponder, UIApplicationDelegate { + private var publishers = [AnyCancellable]() + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. + + Push.wallet.requestPublisher.sink { (id: RPCID, account: Account, metadata: AppMetadata) in + Task(priority: .high) { try! await Push.wallet.approve(id: id) } + }.store(in: &publishers) + Push.wallet.pushMessagePublisher.sink { pm in + print(pm) + }.store(in: &publishers) return true } diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index 25279daaa..611e57b18 100644 --- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -1,7 +1,9 @@ import WalletConnectNetworking import Web3Wallet +import WalletConnectPush struct ThirdPartyConfigurator: Configurator { + func configure() { Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory()) @@ -13,5 +15,8 @@ struct ThirdPartyConfigurator: Configurator { ) Web3Wallet.configure(metadata: metadata, signerFactory: DefaultSignerFactory()) + Push.configure() + } + } diff --git a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift index 1fa5738e2..1c5b083e6 100644 --- a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift @@ -2,9 +2,11 @@ import UIKit import Auth import WalletConnectPairing + final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + private let app = Application() private var configurators: [Configurator] { @@ -40,5 +42,6 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { Task { try await Pair.instance.pair(uri: WalletConnectURI(string: uri)!) } + } } From 29d4b8fb1ab8cb0b3b1b169d7a4752a972311253 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 19 Jan 2023 11:44:23 +0100 Subject: [PATCH 18/74] remove exchange key pair --- .../Client/Dapp/ProposalResponseSubscriber.swift | 6 ++++++ .../Client/Wallet/PushRequestResponder.swift | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index b1a97d158..6bdcb3380 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -48,6 +48,7 @@ class ProposalResponseSubscriber { let pushSubscription = PushSubscription(topic: topic, relay: relay, metadata: metadata) subscriptionsStore.set(pushSubscription, forKey: topic) + removeExchangeKeyPair(for: selfpublicKeyHex) try await networkingInteractor.subscribe(topic: topic) return pushSubscription } @@ -60,10 +61,15 @@ class ProposalResponseSubscriber { return (topic: topic, keys: keys) } + private func removeExchangeKeyPair(for pubKey: String) { + kms.deletePrivateKey(for: pubKey) + } + private func subscribeForProposalErrors() { let protocolMethod = PushRequestProtocolMethod() networkingInteractor.responseErrorSubscription(on: protocolMethod) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + removeExchangeKeyPair(for: payload.request.publicKey) guard let error = PushError(code: payload.error.code) else { return } onResponse?(payload.id, .failure(error)) }.store(in: &publishers) diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 766f323c8..340934851 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -55,6 +55,8 @@ class PushRequestResponder { subscriptionsStore.set(pushSubscription, forKey: pushTopic) try await networkingInteractor.respond(topic: pairingTopic, response: response, protocolMethod: PushRequestProtocolMethod()) + + removeExchangeKeyPair(for: keys.publicKey.hexRepresentation) } func respondError(requestId: RPCID) async throws { @@ -65,6 +67,10 @@ class PushRequestResponder { try await networkingInteractor.respondError(topic: pairingTopic, requestId: requestId, protocolMethod: PushRequestProtocolMethod(), reason: PushError.rejected) } + private func removeExchangeKeyPair(for pubKey: String) { + kms.deletePrivateKey(for: pubKey) + } + private func getRecord(requestId: RPCID) throws -> RPCHistory.Record { guard let record = rpcHistory.get(recordId: requestId) else { throw Errors.recordForIdNotFound } From 559732bac3e7885209a42da228526200a7d01c1b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 19 Jan 2023 13:24:53 +0100 Subject: [PATCH 19/74] update push client factory add account to push subscription --- .../Client/Dapp/ProposalResponseSubscriber.swift | 2 +- .../Client/Wallet/PushMessagesProvider.swift | 15 +++++++++++++++ .../Client/Wallet/PushRequestResponder.swift | 2 +- .../Client/Wallet/WalletPushClient.swift | 7 +++++++ .../Client/Wallet/WalletPushClientFactory.swift | 2 ++ .../Types/PushSubscription.swift | 1 + .../RPCHistory/RPCHistory.swift | 10 ++++++++++ 7 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 4afa24002..5e4b85ebe 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -46,7 +46,7 @@ class ProposalResponseSubscriber { let selfpublicKeyHex = payload.request.publicKey let (topic, _) = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) - let pushSubscription = PushSubscription(topic: topic, relay: relay, metadata: metadata) + let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) subscriptionsStore.set(pushSubscription, forKey: topic) kms.deletePrivateKey(for: selfpublicKeyHex) try await networkingInteractor.subscribe(topic: topic) diff --git a/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift b/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift new file mode 100644 index 000000000..236fb0879 --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/PushMessagesProvider.swift @@ -0,0 +1,15 @@ + +import Foundation +import WalletConnectUtils + +class PushMessagesProvider { + private let history: RPCHistory + + init(history: RPCHistory) { + self.history = history + } + + public func getMessageHistory(topic: String) -> [PushMessage] { + history.getAll(of: PushMessage.self, topic: topic) + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 840e85cfa..c674d63a0 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -51,7 +51,7 @@ class PushRequestResponder { let response = RPCResponse(id: requestId, result: responseParams) let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) - let pushSubscription = PushSubscription(topic: pushTopic, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) + let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) subscriptionsStore.set(pushSubscription, forKey: pushTopic) try await networkingInteractor.respond(topic: pairingTopic, response: response, protocolMethod: PushRequestProtocolMethod()) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 18c7c30b3..b38596db5 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -36,6 +36,7 @@ public class WalletPushClient { private let proposeResponder: PushRequestResponder private let pushMessageSubscriber: PushMessageSubscriber private let subscriptionsProvider: SubscriptionsProvider + private let pushMessagesProvider: PushMessagesProvider private let resubscribeService: PushResubscribeService init(logger: ConsoleLogging, @@ -45,6 +46,7 @@ public class WalletPushClient { proposeResponder: PushRequestResponder, pushMessageSubscriber: PushMessageSubscriber, subscriptionsProvider: SubscriptionsProvider, + pushMessagesProvider: PushMessagesProvider, deletePushSubscriptionService: DeletePushSubscriptionService, deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber, resubscribeService: PushResubscribeService) { @@ -54,6 +56,7 @@ public class WalletPushClient { self.echoClient = echoClient self.pushMessageSubscriber = pushMessageSubscriber self.subscriptionsProvider = subscriptionsProvider + self.pushMessagesProvider = pushMessagesProvider self.deletePushSubscriptionService = deletePushSubscriptionService self.deletePushSubscriptionSubscriber = deletePushSubscriptionSubscriber self.resubscribeService = resubscribeService @@ -72,6 +75,10 @@ public class WalletPushClient { subscriptionsProvider.getActiveSubscriptions() } + public func getMessageHistory(topic: String) -> [PushMessage] { + pushMessagesProvider.getMessageHistory(topic: topic) + } + public func delete(topic: String) async throws { try await deletePushSubscriptionService.delete(topic: topic) } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 7d11f4ade..2ed976c16 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -36,6 +36,7 @@ public struct WalletPushClientFactory { let deletePushSubscriptionService = DeletePushSubscriptionService(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore) let deletePushSubscriptionSubscriber = DeletePushSubscriptionSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore) let resubscribeService = PushResubscribeService(networkInteractor: networkInteractor, subscriptionsStorage: subscriptionStore) + let pushMessagesProvider = PushMessagesProvider(history: history) return WalletPushClient( logger: logger, kms: kms, @@ -44,6 +45,7 @@ public struct WalletPushClientFactory { proposeResponder: proposeResponder, pushMessageSubscriber: pushMessageSubscriber, subscriptionsProvider: subscriptionProvider, + pushMessagesProvider: pushMessagesProvider, deletePushSubscriptionService: deletePushSubscriptionService, deletePushSubscriptionSubscriber: deletePushSubscriptionSubscriber, resubscribeService: resubscribeService diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 0dc61526b..74e7b5528 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -4,6 +4,7 @@ import WalletConnectPairing public struct PushSubscription: Codable, Equatable { let topic: String + let account: Account let relay: RelayProtocolOptions let metadata: AppMetadata } diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 8e130c106..48eebeb7e 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -65,6 +65,16 @@ public final class RPCHistory { } } + public func getAll(of type: Object.Type, topic: String) -> [Object] { + return storage.getAll() + .filter{$0.topic == topic} + .compactMap { record in + guard let object = try? record.request.params?.get(Object.self) + else { return nil } + return object + } + } + public func deleteAll(forTopic topic: String) { deleteAll(forTopics: [topic]) } From 75543642c0dc8319fc2d8c6c5d22f2e2fa188564 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 19 Jan 2023 13:29:21 +0100 Subject: [PATCH 20/74] add history test --- Example/IntegrationTests/Push/PushTests.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 123549b51..1bbc1e234 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -147,6 +147,7 @@ final class PushTests: XCTestCase { func testDappSendsPushMessage() async { let expectation = expectation(description: "expects wallet to receive push message") let pushMessage = PushMessage.stub() + var pushSubscription: PushSubscription! let uri = try! await dappPairingClient.create() try! await walletPairingClient.pair(uri: uri) try! await dappPushClient.request(account: Account.stub(), topic: uri.topic) @@ -160,14 +161,18 @@ final class PushTests: XCTestCase { XCTFail() return } + pushSubscription = subscription Task(priority: .userInitiated) { try! await dappPushClient.notify(topic: subscription.topic, message: pushMessage) } }.store(in: &publishers) - walletPushClient.pushMessagePublisher.sink { receivedPushMessage in + walletPushClient.pushMessagePublisher.sink { [unowned self] receivedPushMessage in + let messageHistory = walletPushClient.getMessageHistory(topic: pushSubscription.topic) XCTAssertEqual(pushMessage, receivedPushMessage) + XCTAssertTrue(messageHistory.contains(receivedPushMessage)) expectation.fulfill() }.store(in: &publishers) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } From b018f6e83d8ccbf9a21cac01f0b9601e2428256a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 20 Jan 2023 11:05:54 +0100 Subject: [PATCH 21/74] savepoint --- Example/DApp/Sign/Accounts/AccountsViewController.swift | 8 ++++++++ Example/ExampleApp.xcodeproj/project.pbxproj | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index 8a7d256fa..55cc6caa2 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -1,5 +1,6 @@ import UIKit import WalletConnectSign +import WalletConnectPush struct AccountDetails { let chain: String @@ -49,6 +50,13 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT } } + private func proposePushSubscription() { + let account = session.namespaces.values.first!.accounts.first! + // temp solution + let pairingTopic = Pair.instance.getPairings().last!.topic + Task(priority: .high){ try! await Push.dapp.request(account: account, topic: pairingTopic)} + } + @objc private func disconnect() { Task { diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index d5d883e65..1d7136361 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; }; 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; + 84E6B85629797CDE00428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B85529797CDE00428BAF /* WalletConnectPush */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -559,6 +560,7 @@ files = ( 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, A54195A52934E83F0035AD19 /* Web3 in Frameworks */, + 84E6B85629797CDE00428BAF /* WalletConnectPush in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -1611,6 +1613,7 @@ A5D85227286333E300DAF5C3 /* Starscream */, A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, A54195A42934E83F0035AD19 /* Web3 */, + 84E6B85529797CDE00428BAF /* WalletConnectPush */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -2774,6 +2777,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectAuth; }; + 84E6B85529797CDE00428BAF /* WalletConnectPush */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectPush; + }; A54195A42934E83F0035AD19 /* Web3 */ = { isa = XCSwiftPackageProductDependency; package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; From 798547a9db58707226b522cdde351e646ce62463 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 20 Jan 2023 11:11:17 +0100 Subject: [PATCH 22/74] update apns payload structure --- Example/PushDecryptionService/NotificationService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/PushDecryptionService/NotificationService.swift b/Example/PushDecryptionService/NotificationService.swift index 737026677..02db7aaf1 100644 --- a/Example/PushDecryptionService/NotificationService.swift +++ b/Example/PushDecryptionService/NotificationService.swift @@ -13,7 +13,7 @@ class NotificationService: UNNotificationServiceExtension { bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { let topic = bestAttemptContent.userInfo["topic"] as! String - let ciphertext = bestAttemptContent.userInfo["ciphertext"] as! String + let ciphertext = bestAttemptContent.userInfo["blob"] as! String do { let service = PushDecryptionService() let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext) From 1532a6a9d778f25f89dc776b4d6b226c4a737c78 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 20 Jan 2023 11:27:57 +0100 Subject: [PATCH 23/74] savepoint --- .../Accounts/AccountsViewController.swift | 30 ++++++++++++++++++- Example/DApp/Sign/SignCoordinator.swift | 8 +++-- .../Types/PushSubscription.swift | 6 ++-- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index 55cc6caa2..2510aca40 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -1,6 +1,7 @@ import UIKit import WalletConnectSign import WalletConnectPush +import Combine struct AccountDetails { let chain: String @@ -13,7 +14,9 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT let session: Session var accountsDetails: [AccountDetails] = [] var onDisconnect: (() -> Void)? + var pushSubscription: PushSubscription? + private var publishers = [AnyCancellable]() private let accountsView: AccountsView = { AccountsView() }() @@ -35,12 +38,21 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT super.viewDidLoad() navigationItem.title = "Accounts" + navigationItem.rightBarButtonItem = UIBarButtonItem( title: "Disconnect", style: .plain, target: self, action: #selector(disconnect) ) + + navigationItem.leftBarButtonItem = UIBarButtonItem( + title: "Push Test", + style: .plain, + target: self, + action: #selector(pushTest) + ) + accountsView.tableView.dataSource = self accountsView.tableView.delegate = self session.namespaces.values.forEach { namespace in @@ -48,13 +60,29 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT accountsDetails.append(AccountDetails(chain: account.blockchainIdentifier, methods: Array(namespace.methods), account: account.address)) // TODO: Rethink how this info is displayed on example } } + proposePushSubscription() } - private func proposePushSubscription() { + func proposePushSubscription() { let account = session.namespaces.values.first!.accounts.first! // temp solution let pairingTopic = Pair.instance.getPairings().last!.topic Task(priority: .high){ try! await Push.dapp.request(account: account, topic: pairingTopic)} + Push.dapp.responsePublisher.sink { (id: RPCID, result: Result) in + switch result { + case .success(let subscription): + self.pushSubscription = subscription + case .failure(let error): + print(error) + } + }.store(in: &publishers) + } + + @objc + private func pushTest() { + guard let pushTopic = pushSubscription?.topic else {return} + let message = PushMessage(title: "Push Message", body: "Hey this is a message from swift client", icon: "", url: "") + Task(priority: .userInitiated) { try! await Push.dapp.notify(topic: pushTopic, message: message) } } @objc diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index 51e70078e..55344a902 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -48,11 +48,12 @@ final class SignCoordinator { Sign.instance.sessionSettlePublisher .receive(on: DispatchQueue.main) .sink { [unowned self] session in - showAccountsScreen(session) + let vc = showAccountsScreen(session) + vc.proposePushSubscription() }.store(in: &publishers) if let session = Sign.instance.getSessions().first { - showAccountsScreen(session) + _ = showAccountsScreen(session) } else { showSelectChainScreen() } @@ -63,13 +64,14 @@ final class SignCoordinator { navigationController.viewControllers = [controller] } - private func showAccountsScreen(_ session: Session) { + private func showAccountsScreen(_ session: Session) -> AccountsViewController { let controller = AccountsViewController(session: session) controller.onDisconnect = { [unowned self] in showSelectChainScreen() } navigationController.presentedViewController?.dismiss(animated: false) navigationController.viewControllers = [controller] + return controller } private func presentResponse(for response: Response) { diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 0dc61526b..ac8a2f22f 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -3,7 +3,7 @@ import WalletConnectUtils import WalletConnectPairing public struct PushSubscription: Codable, Equatable { - let topic: String - let relay: RelayProtocolOptions - let metadata: AppMetadata + public let topic: String + public let relay: RelayProtocolOptions + public let metadata: AppMetadata } From 4c89b95e1a4e9c77b71d88285e2c6b0afd3c7889 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 20 Jan 2023 11:41:08 +0100 Subject: [PATCH 24/74] update push message --- Example/DApp/Sign/Accounts/AccountsViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index 2510aca40..c4df4145b 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -81,7 +81,7 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT @objc private func pushTest() { guard let pushTopic = pushSubscription?.topic else {return} - let message = PushMessage(title: "Push Message", body: "Hey this is a message from swift client", icon: "", url: "") + let message = PushMessage(title: "Push Message", body: "He,y this is a message from the swift client", icon: "", url: "") Task(priority: .userInitiated) { try! await Push.dapp.notify(topic: pushTopic, message: message) } } From 87027ef74d6cf9301da8d40e9523e9a922a4e849 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 20 Jan 2023 16:35:47 +0500 Subject: [PATCH 25/74] HistoryService + Validate on respond --- .../Engine/Common/SessionEngine.swift | 31 +++++++++++++- .../Services/HistoryService.swift | 42 +++++++++++++++++++ .../WalletConnectSign/Sign/SignClient.swift | 22 +++------- .../Sign/SignClientFactory.swift | 5 ++- 4 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 Sources/WalletConnectSign/Services/HistoryService.swift diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 98de13b0a..f06fd7179 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -4,6 +4,7 @@ import Combine final class SessionEngine { enum Errors: Error { case sessionNotFound(topic: String) + case sessionRequestExpired } var onSessionsUpdate: (([Session]) -> Void)? @@ -15,17 +16,20 @@ final class SessionEngine { private let sessionStore: WCSessionStorage private let networkingInteractor: NetworkInteracting + private let historyService: HistoryService private let kms: KeyManagementServiceProtocol private var publishers = [AnyCancellable]() private let logger: ConsoleLogging init( networkingInteractor: NetworkInteracting, + historyService: HistoryService, kms: KeyManagementServiceProtocol, sessionStore: WCSessionStorage, logger: ConsoleLogging ) { self.networkingInteractor = networkingInteractor + self.historyService = historyService self.kms = kms self.sessionStore = sessionStore self.logger = logger @@ -65,8 +69,24 @@ final class SessionEngine { guard sessionStore.hasSession(forTopic: topic) else { throw Errors.sessionNotFound(topic: topic) } - let response = RPCResponse(id: requestId, outcome: response) - try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: SessionRequestProtocolMethod()) + + let protocolMethod = SessionRequestProtocolMethod() + + guard sessionRequestNotExpired(requestId: requestId) else { + try await networkingInteractor.respondError( + topic: topic, + requestId: requestId, + protocolMethod: protocolMethod, + reason: SignReasonCode.sessionRequestExpired + ) + throw Errors.sessionRequestExpired + } + + try await networkingInteractor.respond( + topic: topic, + response: RPCResponse(id: requestId, outcome: response), + protocolMethod: protocolMethod + ) } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -159,6 +179,13 @@ private extension SessionEngine { } } + func sessionRequestNotExpired(requestId: RPCID) -> Bool { + guard let request = historyService.getSessionRequest(id: requestId) + else { return false } + + return !request.isExpired() + } + func respondError(payload: SubscriptionPayload, reason: SignReasonCode, protocolMethod: ProtocolMethod) { Task(priority: .high) { do { diff --git a/Sources/WalletConnectSign/Services/HistoryService.swift b/Sources/WalletConnectSign/Services/HistoryService.swift new file mode 100644 index 000000000..394ff0c61 --- /dev/null +++ b/Sources/WalletConnectSign/Services/HistoryService.swift @@ -0,0 +1,42 @@ +import Foundation + +final class HistoryService { + + private let history: RPCHistory + + init(history: RPCHistory) { + self.history = history + } + + func getPendingRequests() -> [Request] { + return history.getPending() + .compactMap { mapRequestRecord($0) } + .filter { !$0.isExpired() } + } + + func getPendingRequests(topic: String) -> [Request] { + return getPendingRequests().filter { $0.topic == topic } + } + + public func getSessionRequest(id: RPCID) -> Request? { + guard let record = history.get(recordId: id) else { return nil } + return mapRequestRecord(record) + } +} + +private extension HistoryService { + + func mapRequestRecord(_ record: RPCHistory.Record) -> Request? { + guard let request = try? record.request.params?.get(SessionType.RequestParams.self) + else { return nil } + + return Request( + id: record.id, + topic: record.topic, + method: request.request.method, + params: request.request.params, + chainId: request.chainId, + expiry: request.request.expiry + ) + } +} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index b2f539272..dd0614324 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -110,7 +110,7 @@ public final class SignClient: SignClientProtocol { private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine private let appProposeService: AppProposeService - private let history: RPCHistory + private let historyService: HistoryService private let cleanupService: SignCleanupService private let sessionProposalPublisherSubject = PassthroughSubject() @@ -140,7 +140,7 @@ public final class SignClient: SignClientProtocol { controllerSessionStateMachine: ControllerSessionStateMachine, appProposeService: AppProposeService, disconnectService: DisconnectService, - history: RPCHistory, + historyService: HistoryService, cleanupService: SignCleanupService, pairingClient: PairingClient ) { @@ -153,7 +153,7 @@ public final class SignClient: SignClientProtocol { self.nonControllerSessionStateMachine = nonControllerSessionStateMachine self.controllerSessionStateMachine = controllerSessionStateMachine self.appProposeService = appProposeService - self.history = history + self.historyService = historyService self.cleanupService = cleanupService self.disconnectService = disconnectService self.pairingClient = pairingClient @@ -323,27 +323,17 @@ public final class SignClient: SignClientProtocol { /// - Returns: Pending requests received from peer with `wc_sessionRequest` protocol method /// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions. public func getPendingRequests(topic: String? = nil) -> [Request] { - let pendingRequests: [Request] = history.getPending() - .compactMap { - guard let request = try? $0.request.params?.get(SessionType.RequestParams.self) else { return nil } - return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId, expiry: request.request.expiry) - } if let topic = topic { - return pendingRequests.filter {$0.topic == topic} + return historyService.getPendingRequests(topic: topic) } else { - return pendingRequests + return historyService.getPendingRequests() } } /// - Parameter id: id of a wc_sessionRequest jsonrpc request /// - Returns: json rpc record object for given id or nil if record for give id does not exits public func getSessionRequestRecord(id: RPCID) -> Request? { - guard - let record = history.get(recordId: id), - let request = try? record.request.params?.get(SessionType.RequestParams.self) - else { return nil } - - return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId, expiry: request.request.expiry) + return historyService.getSessionRequest(id: id) } /// Delete all stored data such as: pairings, sessions, keys diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index ce27c1e4c..29b0a6987 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -25,7 +25,8 @@ public struct SignClientFactory { let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionToPairingTopic.rawValue) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.proposals.rawValue) - let sessionEngine = SessionEngine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) + let historyService = HistoryService(history: rpcHistory) + let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) @@ -47,7 +48,7 @@ public struct SignClientFactory { controllerSessionStateMachine: controllerSessionStateMachine, appProposeService: appProposerService, disconnectService: disconnectService, - history: rpcHistory, + historyService: historyService, cleanupService: cleanupService, pairingClient: pairingClient ) From 8771a95bb2ded121ce0440ccaa7a783e29a8c887 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 20 Jan 2023 20:02:42 +0500 Subject: [PATCH 26/74] TTL extension --- .../Engine/Common/SessionEngine.swift | 2 +- Sources/WalletConnectSign/Request.swift | 9 +++++++++ .../SessionRequestProtocolMethod.swift | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index f06fd7179..562726920 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -60,7 +60,7 @@ final class SessionEngine { } let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params, expiry: request.expiry) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - let protocolMethod = SessionRequestProtocolMethod() + let protocolMethod = SessionRequestProtocolMethod(ttl: request.calculateTtl()) let rpcRequest = RPCRequest(method: protocolMethod.method, params: sessionRequestParams) try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SessionRequestProtocolMethod()) } diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index e41ea77de..38d5a69da 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -37,6 +37,15 @@ public struct Request: Codable, Equatable { return expiryDate < Date() } + + func calculateTtl() -> Int { + guard let expiry = expiry else { return SessionRequestProtocolMethod.defaultTtl } + + let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) + let diff = expiryDate - Date().timeIntervalSince1970 + + return Int(diff.timeIntervalSince1970) + } } private extension Request { diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift index 13d77a32e..28ee6d446 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift @@ -1,9 +1,22 @@ import Foundation struct SessionRequestProtocolMethod: ProtocolMethod { + + static let defaultTtl: Int = 300 + let method: String = "wc_sessionRequest" - let requestConfig = RelayConfig(tag: 1108, prompt: true, ttl: 300) + private let ttl: Int + + var requestConfig: RelayConfig { + RelayConfig(tag: 1108, prompt: true, ttl: ttl) + } + + var responseConfig: RelayConfig { + RelayConfig(tag: 1109, prompt: false, ttl: ttl) + } - let responseConfig = RelayConfig(tag: 1109, prompt: false, ttl: 300) + init(ttl: Int = Self.defaultTtl) { + self.ttl = ttl + } } From 89df241b4bbc8975f91062614370f152b6beefa4 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 20 Jan 2023 20:38:02 +0500 Subject: [PATCH 27/74] calculateTtl tests --- .../xcschemes/WalletConnectSignTests.xcscheme | 52 +++++++++++++++++++ Sources/WalletConnectSign/Request.swift | 13 +++-- .../SessionRequestTests.swift | 48 +++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme create mode 100644 Tests/WalletConnectSignTests/SessionRequestTests.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme new file mode 100644 index 000000000..2b307dc3b --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index 38d5a69da..8999cba52 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -31,18 +31,23 @@ public struct Request: Codable, Equatable { let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) guard - Date().distance(to: expiryDate) > Constants.maxExpiry, - Date().distance(to: expiryDate) < Constants.minExpiry + Date().distance(to: expiryDate) < Constants.maxExpiry, + Date().distance(to: expiryDate) > Constants.minExpiry else { return true } return expiryDate < Date() } - func calculateTtl() -> Int { + func calculateTtl(currentDate: Date = Date()) -> Int { guard let expiry = expiry else { return SessionRequestProtocolMethod.defaultTtl } let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) - let diff = expiryDate - Date().timeIntervalSince1970 + let diff = expiryDate - currentDate.timeIntervalSince1970 + + guard + diff.timeIntervalSince1970 < Constants.maxExpiry, + diff.timeIntervalSince1970 > Constants.minExpiry + else { return SessionRequestProtocolMethod.defaultTtl } return Int(diff.timeIntervalSince1970) } diff --git a/Tests/WalletConnectSignTests/SessionRequestTests.swift b/Tests/WalletConnectSignTests/SessionRequestTests.swift new file mode 100644 index 000000000..99970f4f0 --- /dev/null +++ b/Tests/WalletConnectSignTests/SessionRequestTests.swift @@ -0,0 +1,48 @@ +import XCTest +@testable import WalletConnectSign + +final class SessionRequestTests: XCTestCase { + + func testRequestTtlDefault() { + let request = Request.stub() + + XCTAssertEqual(request.calculateTtl(), SessionRequestProtocolMethod.defaultTtl) + } + + func testRequestTtlExtended() { + let currentDate = Date(timeIntervalSince1970: 0) + let expiry = currentDate.advanced(by: 500) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + + XCTAssertEqual(request.calculateTtl(currentDate: currentDate), 500) + } + + func testRequestTtlNotExtendedMinValidation() { + let currentDate = Date(timeIntervalSince1970: 0) + let expiry = currentDate.advanced(by: 200) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + + XCTAssertEqual(request.calculateTtl(currentDate: currentDate), SessionRequestProtocolMethod.defaultTtl) + } + + func testRequestTtlNotExtendedMaxValidation() { + let currentDate = Date(timeIntervalSince1970: 0) + let expiry = currentDate.advanced(by: 700000) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + + XCTAssertEqual(request.calculateTtl(currentDate: currentDate), SessionRequestProtocolMethod.defaultTtl) + } +} + +private extension Request { + + static func stub(expiry: UInt64? = nil) -> Request { + return Request( + topic: "topic", + method: "method", + params: AnyCodable("params"), + chainId: Blockchain("eip155:1")!, + expiry: expiry + ) + } +} From 25b522b73789e1d28ca4b582ee9fc50822f131eb Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 20 Jan 2023 20:50:31 +0500 Subject: [PATCH 28/74] isExpired tests --- Sources/WalletConnectSign/Request.swift | 8 ++--- .../SessionRequestTests.swift | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index 8999cba52..f1a9b38b5 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -25,17 +25,17 @@ public struct Request: Codable, Equatable { self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId, expiry: expiry) } - func isExpired() -> Bool { + func isExpired(currentDate: Date = Date()) -> Bool { guard let expiry = expiry else { return false } let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) guard - Date().distance(to: expiryDate) < Constants.maxExpiry, - Date().distance(to: expiryDate) > Constants.minExpiry + currentDate.distance(to: expiryDate) < Constants.maxExpiry, + currentDate.distance(to: expiryDate) > Constants.minExpiry else { return true } - return expiryDate < Date() + return expiryDate < currentDate } func calculateTtl(currentDate: Date = Date()) -> Int { diff --git a/Tests/WalletConnectSignTests/SessionRequestTests.swift b/Tests/WalletConnectSignTests/SessionRequestTests.swift index 99970f4f0..e89ff347d 100644 --- a/Tests/WalletConnectSignTests/SessionRequestTests.swift +++ b/Tests/WalletConnectSignTests/SessionRequestTests.swift @@ -32,6 +32,41 @@ final class SessionRequestTests: XCTestCase { XCTAssertEqual(request.calculateTtl(currentDate: currentDate), SessionRequestProtocolMethod.defaultTtl) } + + func testIsExpiredDefault() { + let request = Request.stub() + + XCTAssertFalse(request.isExpired()) + } + + func testIsExpiredTrue() { + let currentDate = Date(timeIntervalSince1970: 500) + let expiry = Date(timeIntervalSince1970: 0) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + XCTAssertTrue(request.isExpired(currentDate: currentDate)) + } + + func testIsExpiredTrueMinValidation() { + let currentDate = Date(timeIntervalSince1970: 500) + let expiry = Date(timeIntervalSince1970: 600) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + XCTAssertTrue(request.isExpired(currentDate: currentDate)) + } + + func testIsExpiredTrueMaxValidation() { + let currentDate = Date(timeIntervalSince1970: 500) + let expiry = Date(timeIntervalSince1970: 700000) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + XCTAssertTrue(request.isExpired(currentDate: currentDate)) + } + + func testIsExpiredFalse() { + let currentDate = Date(timeIntervalSince1970: 0) + let expiry = Date(timeIntervalSince1970: 500) + let request = Request.stub(expiry: UInt64(expiry.timeIntervalSince1970)) + + XCTAssertFalse(request.isExpired(currentDate: currentDate)) + } } private extension Request { From d788d1657b4ac35608891e148a5395f804ed0a41 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 20 Jan 2023 21:35:24 +0500 Subject: [PATCH 29/74] testErrorOnRequestExpiry test --- .../Sign/SignClientTests.swift | 4 +- Sources/WalletConnectSign/Request.swift | 4 +- .../NetworkingInteractorMock.swift | 2 + .../SessionEngineTests.swift | 54 +++++++++++++++++++ Tests/WalletConnectSignTests/Stub/Stubs.swift | 4 +- 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 Tests/WalletConnectSignTests/SessionEngineTests.swift diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index a429d0884..c5a7fcab1 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -184,7 +184,7 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain, expiry: nil) try await dapp.client.request(params: request) } } @@ -230,7 +230,7 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain, expiry: nil) try await dapp.client.request(params: request) } } diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index f1a9b38b5..1cae4e0cd 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -31,8 +31,8 @@ public struct Request: Codable, Equatable { let expiryDate = Date(timeIntervalSince1970: TimeInterval(expiry)) guard - currentDate.distance(to: expiryDate) < Constants.maxExpiry, - currentDate.distance(to: expiryDate) > Constants.minExpiry + abs(currentDate.distance(to: expiryDate)) < Constants.maxExpiry, + abs(currentDate.distance(to: expiryDate)) > Constants.minExpiry else { return true } return expiryDate < currentDate diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 323c52279..4e34798ec 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -23,6 +23,7 @@ public class NetworkingInteractorMock: NetworkInteracting { var didCallRequest: Bool { requestCallCount > 0 } var onSubscribeCalled: (() -> Void)? + var onRespondError: ((Int) -> Void)? public let socketConnectionStatusPublisherSubject = PassthroughSubject() public var socketConnectionStatusPublisher: AnyPublisher { @@ -121,6 +122,7 @@ public class NetworkingInteractorMock: NetworkInteracting { public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { lastErrorCode = reason.code didRespondError = true + onRespondError?(reason.code) } public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { diff --git a/Tests/WalletConnectSignTests/SessionEngineTests.swift b/Tests/WalletConnectSignTests/SessionEngineTests.swift new file mode 100644 index 000000000..051329ffe --- /dev/null +++ b/Tests/WalletConnectSignTests/SessionEngineTests.swift @@ -0,0 +1,54 @@ +import XCTest +@testable import WalletConnectSign +@testable import WalletConnectUtils +@testable import TestingUtils + +final class SessionEngineTests: XCTestCase { + + var networkingInteractor: NetworkingInteractorMock! + var sessionStorage: WCSessionStorageMock! + var engine: SessionEngine! + + override func setUp() { + networkingInteractor = NetworkingInteractorMock() + sessionStorage = WCSessionStorageMock() + engine = SessionEngine( + networkingInteractor: networkingInteractor, + historyService: HistoryService( + history: RPCHistory( + keyValueStore: .init( + defaults: RuntimeKeyValueStorage(), + identifier: "" + ) + ) + ), + kms: KeyManagementServiceMock(), + sessionStore: sessionStorage, + logger: ConsoleLoggerMock() + ) + } + + func testErrorOnRequestExpiry() { + let expectation = expectation(description: "TestErrorOnRequestExpiry") + + sessionStorage.setSession(WCSession.stub( + topic: "topic", + namespaces: SessionNamespace.stubDictionary() + )) + + networkingInteractor.onRespondError = { code in + XCTAssertEqual(code, 8000) + expectation.fulfill() + } + + let request = RPCRequest.stubRequest( + method: "method", + chainId: Blockchain("eip155:1")!, + expiry: UInt64(Date().timeIntervalSince1970) + ) + + networkingInteractor.requestPublisherSubject.send(("topic", request)) + + wait(for: [expectation], timeout: 0.5) + } +} diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index 1f22303f7..cbd649f4c 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -68,9 +68,9 @@ extension RPCRequest { return RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) } - static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest { + static func stubRequest(method: String, chainId: Blockchain, expiry: UInt64? = nil) -> RPCRequest { let params = SessionType.RequestParams( - request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable()), expiry: nil), + request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable()), expiry: expiry), chainId: chainId) return RPCRequest(method: SessionRequestProtocolMethod().method, params: params) } From 4b6e966e6d9282f330935cc01fe4c737149975ce Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Mon, 23 Jan 2023 09:33:51 +0100 Subject: [PATCH 30/74] Remove namespace extensions --- .../Sign/Connect/ConnectViewController.swift | 4 +- .../SelectChainViewController.swift | 4 +- .../SessionDetailViewModel.swift | 3 +- .../Wallet/WalletViewController.swift | 6 +- .../SessionProposalInteractor.swift | 6 +- Sources/WalletConnectSign/Namespace.swift | 57 +---- .../Types/Session/WCSession.swift | 28 -- .../NamespaceValidationTests.swift | 240 +++--------------- Tests/WalletConnectSignTests/Stub/Stubs.swift | 6 +- .../WCSessionTests.swift | 187 +------------- 10 files changed, 61 insertions(+), 480 deletions(-) diff --git a/Example/DApp/Sign/Connect/ConnectViewController.swift b/Example/DApp/Sign/Connect/ConnectViewController.swift index 3e3da5422..c7fc8bcbe 100644 --- a/Example/DApp/Sign/Connect/ConnectViewController.swift +++ b/Example/DApp/Sign/Connect/ConnectViewController.swift @@ -91,7 +91,7 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie "eth_sendTransaction", "personal_sign", "eth_signTypedData" - ], events: [], extensions: nil + ], events: [] ), "solana": ProposalNamespace( chains: [ @@ -100,7 +100,7 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie methods: [ "solana_signMessage", "solana_signTransaction" - ], events: [], extensions: nil + ], events: [] ) ] Task { diff --git a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift index be310e2f9..83a73f0a8 100644 --- a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift @@ -46,7 +46,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { "eth_sendTransaction", "personal_sign", "eth_signTypedData" - ], events: [], extensions: nil + ], events: [] ), "solana": ProposalNamespace( chains: [ @@ -55,7 +55,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { methods: [ "solana_signMessage", "solana_signTransaction" - ], events: [], extensions: nil + ], events: [] ) ] Task { diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index d43a5ce3d..6e047d692 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -175,8 +175,7 @@ private extension SessionNamespace { self.init( accounts: accounts ?? namespace.accounts, methods: methods ?? namespace.methods, - events: events ?? namespace.events, - extensions: namespace.extensions + events: events ?? namespace.events ) } } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index da1d92cad..0eaef4cac 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -223,11 +223,7 @@ extension WalletViewController: ProposalViewControllerDelegate { let proposalNamespace = $0.value let accounts = Set(proposalNamespace.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") }) - let extensions: [SessionNamespace.Extension]? = proposalNamespace.extensions?.map { element in - let accounts = Set(element.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") }) - return SessionNamespace.Extension(accounts: accounts, methods: element.methods, events: element.events) - } - let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events, extensions: extensions) + let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events) sessionNamespaces[caip2Namespace] = sessionNamespace } approve(proposalId: proposal.id, namespaces: sessionNamespaces) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift index c88d43aef..fcbd47201 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift @@ -15,11 +15,7 @@ final class SessionProposalInteractor { let proposalNamespace = $0.value let accounts = Set(proposalNamespace.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") }) - let extensions: [SessionNamespace.Extension]? = proposalNamespace.extensions?.map { element in - let accounts = Set(element.chains.compactMap { Account($0.absoluteString + ":\(self.accounts[$0.namespace]!)") }) - return SessionNamespace.Extension(accounts: accounts, methods: element.methods, events: element.events) - } - let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events, extensions: extensions) + let sessionNamespace = SessionNamespace(accounts: accounts, methods: proposalNamespace.methods, events: proposalNamespace.events) sessionNamespaces[caip2Namespace] = sessionNamespace } diff --git a/Sources/WalletConnectSign/Namespace.swift b/Sources/WalletConnectSign/Namespace.swift index 931ae2d4b..f9d2f8e16 100644 --- a/Sources/WalletConnectSign/Namespace.swift +++ b/Sources/WalletConnectSign/Namespace.swift @@ -3,25 +3,11 @@ public struct ProposalNamespace: Equatable, Codable { public let chains: Set public let methods: Set public let events: Set - public let extensions: [Extension]? - public struct Extension: Equatable, Codable { - public let chains: Set - public let methods: Set - public let events: Set - - public init(chains: Set, methods: Set, events: Set) { - self.chains = chains - self.methods = methods - self.events = events - } - } - - public init(chains: Set, methods: Set, events: Set, extensions: [ProposalNamespace.Extension]? = nil) { + public init(chains: Set, methods: Set, events: Set) { self.chains = chains self.methods = methods self.events = events - self.extensions = extensions } } @@ -30,36 +16,11 @@ public struct SessionNamespace: Equatable, Codable { public let accounts: Set public let methods: Set public let events: Set - public let extensions: [Extension]? - - public struct Extension: Equatable, Codable { - public let accounts: Set - public let methods: Set - public let events: Set - - public init(accounts: Set, methods: Set, events: Set) { - self.accounts = accounts - self.methods = methods - self.events = events - } - - func isCompliant(to required: ProposalNamespace.Extension) -> Bool { - guard - SessionNamespace.accountsAreCompliant(accounts, toChains: required.chains), - methods.isSuperset(of: required.methods), - events.isSuperset(of: required.events) - else { - return false - } - return true - } - } - public init(accounts: Set, methods: Set, events: Set, extensions: [SessionNamespace.Extension]? = nil) { + public init(accounts: Set, methods: Set, events: Set) { self.accounts = accounts self.methods = methods self.events = events - self.extensions = extensions } static func accountsAreCompliant(_ accounts: Set, toChains chains: Set) -> Bool { @@ -84,13 +45,6 @@ enum Namespace { throw WalletConnectError.unsupportedNamespace(.unsupportedChains) } } - if let extensions = namespace.extensions { - for ext in extensions { - if ext.chains.isEmpty { - throw WalletConnectError.unsupportedNamespace(.unsupportedChains) - } - } - } } } @@ -104,13 +58,6 @@ enum Namespace { throw WalletConnectError.unsupportedNamespace(.unsupportedAccounts) } } - if let extensions = namespace.extensions { - for ext in extensions { - if ext.accounts.isEmpty { - throw WalletConnectError.unsupportedNamespace(.unsupportedAccounts) - } - } - } } } diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift index e4e4d2b14..642d848c6 100644 --- a/Sources/WalletConnectSign/Types/Session/WCSession.swift +++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift @@ -95,15 +95,6 @@ struct WCSession: SequenceObject, Equatable { if namespace.methods.contains(method) { return true } - if let extensions = namespace.extensions { - for extended in extensions { - if extended.accounts.contains(where: { $0.blockchain == chain }) { - if extended.methods.contains(method) { - return true - } - } - } - } } } return false @@ -115,15 +106,6 @@ struct WCSession: SequenceObject, Equatable { if namespace.events.contains(event) { return true } - if let extensions = namespace.extensions { - for extended in extensions { - if extended.accounts.contains(where: { $0.blockchain == chain }) { - if extended.events.contains(event) { - return true - } - } - } - } } } return false @@ -139,16 +121,6 @@ struct WCSession: SequenceObject, Equatable { else { throw Error.unsatisfiedUpdateNamespaceRequirement } - if let extensions = item.value.extensions { - guard let compliantExtensions = compliantNamespace.extensions else { - throw Error.unsatisfiedUpdateNamespaceRequirement - } - for existingExtension in extensions { - guard compliantExtensions.contains(where: { $0.isCompliant(to: existingExtension) }) else { - throw Error.unsatisfiedUpdateNamespaceRequirement - } - } - } } self.namespaces = namespaces self.timestamp = timestamp diff --git a/Tests/WalletConnectSignTests/NamespaceValidationTests.swift b/Tests/WalletConnectSignTests/NamespaceValidationTests.swift index 599b40c70..6fea012f9 100644 --- a/Tests/WalletConnectSignTests/NamespaceValidationTests.swift +++ b/Tests/WalletConnectSignTests/NamespaceValidationTests.swift @@ -18,16 +18,12 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["method"], - events: ["event"], - extensions: [ - ProposalNamespace.Extension(chains: [Blockchain("eip155:137")!], methods: ["otherMethod"], events: ["otherEvent"]) - ] + events: ["event"] ), "cosmos": ProposalNamespace( chains: [cosmosChain], methods: ["someMethod"], - events: ["someEvent"], - extensions: nil + events: ["someEvent"] ) ] XCTAssertNoThrow(try Namespace.validate(namespace)) @@ -38,8 +34,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertThrowsError(try Namespace.validate(namespace)) } @@ -49,8 +44,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: [], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertNoThrow(try Namespace.validate(namespace)) } @@ -60,8 +54,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["method"], - events: [], - extensions: nil) + events: []) ] XCTAssertNoThrow(try Namespace.validate(namespace)) } @@ -71,43 +64,26 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain, Blockchain("eip155:137")!, Blockchain("eip155:10")!], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] let invalidNamespace = [ "eip155": ProposalNamespace( chains: [ethChain, Blockchain("cosmos:cosmoshub-4")!], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertNoThrow(try Namespace.validate(validNamespace)) XCTAssertThrowsError(try Namespace.validate(invalidNamespace)) } - func testExtensionChainsMustNotBeEmpty() { - let namespace = [ - "eip155": ProposalNamespace( - chains: [ethChain], - methods: ["method"], - events: ["event"], - extensions: [ - ProposalNamespace.Extension(chains: [], methods: ["otherMethod"], events: ["otherEvent"]) - ] - ) - ] - XCTAssertThrowsError(try Namespace.validate(namespace)) - } - func testValidateAllProposalNamespaces() { let namespace = [ "eip155": ProposalNamespace( chains: [ethChain], methods: ["method"], - events: ["event"], - extensions: nil), + events: ["event"]), "cosmos": ProposalNamespace( - chains: [], methods: [], events: [], extensions: nil) + chains: [], methods: [], events: []) ] XCTAssertThrowsError(try Namespace.validate(namespace)) } @@ -119,10 +95,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method"], - events: ["event"], - extensions: [ - SessionNamespace.Extension(accounts: [polyAccount], methods: ["otherMethod"], events: ["otherEvent"]) - ] + events: ["event"] ) ] XCTAssertNoThrow(try Namespace.validate(namespace)) @@ -133,8 +106,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": SessionNamespace( accounts: [], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertThrowsError(try Namespace.validate(namespace)) } @@ -144,8 +116,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertNoThrow(try Namespace.validate(namespace)) } @@ -155,8 +126,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method"], - events: [], - extensions: nil) + events: []) ] XCTAssertNoThrow(try Namespace.validate(namespace)) } @@ -166,43 +136,26 @@ final class NamespaceValidationTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] let invalidNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount, cosmosAccount], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] XCTAssertNoThrow(try Namespace.validate(validNamespace)) XCTAssertThrowsError(try Namespace.validate(invalidNamespace)) } - func testExtensionAccountsMustNotBeEmpty() { - let namespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount], - methods: ["method"], - events: ["event"], - extensions: [ - SessionNamespace.Extension(accounts: [], methods: ["otherMethod"], events: ["otherEvent"]) - ] - ) - ] - XCTAssertThrowsError(try Namespace.validate(namespace)) - } - func testValidateAllSessionNamespaces() { let namespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method"], - events: ["event"], - extensions: nil), + events: ["event"]), "cosmos": SessionNamespace( - accounts: [], methods: [], events: [], extensions: nil) + accounts: [], methods: [], events: []) ] XCTAssertThrowsError(try Namespace.validate(namespace)) } @@ -214,22 +167,19 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["eth_sign"], - events: [], - extensions: nil) + events: []) ] let validSessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["eth_sign"], - events: [], - extensions: nil) + events: []) ] let invalidSessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: [], - extensions: nil) + events: []) ] XCTAssertNoThrow(try Namespace.validateApproved(validSessionNamespace, against: proposalNamespace)) XCTAssertThrowsError(try Namespace.validateApproved(invalidSessionNamespace, against: proposalNamespace)) @@ -240,22 +190,19 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain, polyChain], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] let validSessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] let invalidSessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] XCTAssertNoThrow(try Namespace.validateApproved(validSessionNamespace, against: proposalNamespace)) XCTAssertThrowsError(try Namespace.validateApproved(invalidSessionNamespace, against: proposalNamespace)) @@ -266,8 +213,7 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] let sessionNamespace = [ "eip155": SessionNamespace( @@ -277,8 +223,7 @@ final class NamespaceValidationTests: XCTestCase { Account("eip155:1:0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8")!, Account("eip155:1:0xEB2F31B0224222D774541BfF89A221e7eb15a17E")!], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) } @@ -288,15 +233,13 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] let sessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["eth_sign", "personalSign"], - events: ["accountsChanged", "someEvent"], - extensions: nil) + events: ["accountsChanged", "someEvent"]) ] XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) } @@ -306,15 +249,13 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] let sessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) + events: ["accountsChanged"]) ] XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) } @@ -324,154 +265,45 @@ final class NamespaceValidationTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethChain], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil), + events: ["accountsChanged"]), "cosmos": ProposalNamespace( chains: [cosmosChain], methods: ["cosmos_signDirect"], - events: ["someEvent"], - extensions: nil) + events: ["someEvent"]) ] let validNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil), + events: ["accountsChanged"]), "cosmos": SessionNamespace( accounts: [cosmosAccount], methods: ["cosmos_signDirect"], - events: ["someEvent"], - extensions: nil) + events: ["someEvent"]) ] let invalidNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: ["eth_sign", "cosmos_signDirect"], - events: ["accountsChanged", "someEvent"], - extensions: nil) + events: ["accountsChanged", "someEvent"]) ] XCTAssertNoThrow(try Namespace.validateApproved(validNamespace, against: proposalNamespace)) XCTAssertThrowsError(try Namespace.validateApproved(invalidNamespace, against: proposalNamespace)) } - func testExtensionsMayBeMerged() { - let proposalNamespace = [ - "eip155": ProposalNamespace( - chains: [ethChain, polyChain], - methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: [ - ProposalNamespace.Extension(chains: [polyChain], methods: ["personalSign"], events: []) - ] - ) - ] - let sessionNamespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["eth_sign"], - events: ["accountsChanged", "personalSign"], - extensions: nil) - ] - XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) - } - func testApprovalMustContainAllEvents() { let proposalNamespace = [ "eip155": ProposalNamespace( chains: [ethChain], methods: [], - events: ["chainChanged"], - extensions: nil) + events: ["chainChanged"]) ] let sessionNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: [], - extensions: nil) + events: []) ] XCTAssertThrowsError(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) } - - func testApprovalMayExtendoMethodsAndEventsInExtensions() { - let proposalNamespace = [ - "eip155": ProposalNamespace( - chains: [ethChain, polyChain], - methods: [], - events: ["chainChanged"], - extensions: [ - ProposalNamespace.Extension(chains: [polyChain], methods: ["eth_sign"], events: []) - ] - ) - ] - let sessionNamespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: [], - events: ["chainChanged"], - extensions: [ - SessionNamespace.Extension( - accounts: [polyAccount], - methods: ["eth_sign", "personalSign"], - events: ["accountsChanged"] - ) - ] - ) - ] - XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) - } - - func testApprovalExtensionsMayContainAccountsNotDefinedInProposal() { - let proposalNamespace = [ - "eip155": ProposalNamespace( - chains: [ethChain, polyChain], - methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: [ - ProposalNamespace.Extension(chains: [polyChain], methods: ["personalSign"], events: []) - ] - ) - ] - let sessionNamespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: [ - SessionNamespace.Extension( - accounts: [polyAccount, Account("eip155:42:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!], - methods: ["personalSign"], - events: [] - ) - ] - ) - ] - XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) - } - - func testApprovalMayAddExtensionsNotDefinedInProposal() { - let proposalNamespace = [ - "eip155": ProposalNamespace( - chains: [ethChain, polyChain], - methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: nil) - ] - let sessionNamespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["eth_sign"], - events: ["accountsChanged"], - extensions: [ - SessionNamespace.Extension( - accounts: [polyAccount], - methods: ["personalSign"], - events: ["accountsChanged"] - ) - ] - ) - ] - XCTAssertNoThrow(try Namespace.validateApproved(sessionNamespace, against: proposalNamespace)) - } } diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index c83298a20..d4027df25 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -24,8 +24,7 @@ extension ProposalNamespace { "eip155": ProposalNamespace( chains: [Blockchain("eip155:1")!], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] } } @@ -36,8 +35,7 @@ extension SessionNamespace { "eip155": SessionNamespace( accounts: [Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!], methods: ["method"], - events: ["event"], - extensions: nil) + events: ["event"]) ] } } diff --git a/Tests/WalletConnectSignTests/WCSessionTests.swift b/Tests/WalletConnectSignTests/WCSessionTests.swift index 3a0d567b4..097dd8883 100644 --- a/Tests/WalletConnectSignTests/WCSessionTests.swift +++ b/Tests/WalletConnectSignTests/WCSessionTests.swift @@ -14,25 +14,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method"], - events: [], - extensions: nil) - ] - var session = WCSession.stub() - XCTAssertNoThrow(try session.updateNamespaces(namespace)) - XCTAssertTrue(session.hasPermission(forMethod: "method", onChain: ethAccount.blockchain)) - } - - func testHasPermissionForMethodInExtension() { - let namespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount], - methods: [], - events: [], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount], - methods: ["method"], - events: [])]) + events: []) ] var session = WCSession.stub() XCTAssertNoThrow(try session.updateNamespaces(namespace)) @@ -44,30 +26,11 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: [], - extensions: nil), + events: []), "cosmos": SessionNamespace( accounts: [cosmosAccount], methods: ["method"], - events: [], - extensions: nil) - ] - var session = WCSession.stub() - XCTAssertNoThrow(try session.updateNamespaces(namespace)) - XCTAssertFalse(session.hasPermission(forMethod: "method", onChain: ethAccount.blockchain)) - } - - func testDenyPermissionForMethodInOtherChainExtension() { - let namespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: [], - events: [], - extensions: [ - SessionNamespace.Extension( - accounts: [polyAccount], - methods: ["method"], - events: [])]) + events: []) ] var session = WCSession.stub() XCTAssertNoThrow(try session.updateNamespaces(namespace)) @@ -79,25 +42,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: ["event"], - extensions: nil) - ] - var session = WCSession.stub() - XCTAssertNoThrow(try session.updateNamespaces(namespace)) - XCTAssertTrue(session.hasPermission(forEvent: "event", onChain: ethAccount.blockchain)) - } - - func testHasPermissionForEventInExtension() { - let namespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount], - methods: [], - events: [], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount], - methods: [], - events: ["event"])]) + events: ["event"]) ] var session = WCSession.stub() XCTAssertNoThrow(try session.updateNamespaces(namespace)) @@ -109,30 +54,11 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: [], - events: [], - extensions: nil), + events: []), "cosmos": SessionNamespace( accounts: [cosmosAccount], methods: [], - events: ["event"], - extensions: nil) - ] - var session = WCSession.stub() - XCTAssertNoThrow(try session.updateNamespaces(namespace)) - XCTAssertFalse(session.hasPermission(forEvent: "event", onChain: ethAccount.blockchain)) - } - - func testDenyPermissionForEventInOtherChainExtension() { - let namespace = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: [], - events: [], - extensions: [ - SessionNamespace.Extension( - accounts: [polyAccount], - methods: [], - events: ["event"])]) + events: ["event"]) ] var session = WCSession.stub() XCTAssertNoThrow(try session.updateNamespaces(namespace)) @@ -146,8 +72,7 @@ final class WCSessionTests: XCTestCase { "eip155": ProposalNamespace( chains: [ethAccount.blockchain, polyAccount.blockchain], methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] + events: ["event", "event-2"])] } private func stubCompliantNamespaces() -> [String: SessionNamespace] { @@ -155,21 +80,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] - } - - private func stubRequiredNamespacesWithExtension() -> [String: ProposalNamespace] { - return [ - "eip155": ProposalNamespace( - chains: [ethAccount.blockchain, polyAccount.blockchain], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - ProposalNamespace.Extension( - chains: [ethAccount.blockchain, polyAccount.blockchain], - methods: ["method-2", "newMethod-2"], - events: ["event-2", "newEvent-2"])])] + events: ["event", "event-2"])] } func testUpdateEqualNamespaces() { @@ -182,22 +93,12 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method"], - events: ["event"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount], - methods: ["method-2"], - events: ["event-2"])])] + events: ["event"])] let newNamespace = [ "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["method", "newMethod"], - events: ["event", "newEvent"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount, polyAccount], - methods: ["method-2", "newMethod-2"], - events: ["event-2", "newEvent-2"])])] + events: ["event", "newEvent"])] var session = WCSession.stub(namespaces: namespace) XCTAssertNoThrow(try session.updateNamespaces(newNamespace)) } @@ -213,8 +114,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [newEthAccount, polyAccount], methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] + events: ["event", "event-2"])] var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces()) XCTAssertNoThrow(try session.updateNamespaces(valid)) } @@ -224,8 +124,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount], methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] + events: ["event", "event-2"])] var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces()) XCTAssertThrowsError(try session.updateNamespaces(invalid)) } @@ -235,8 +134,7 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["method"], - events: ["event", "event-2"], - extensions: nil)] + events: ["event", "event-2"])] var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces()) XCTAssertThrowsError(try session.updateNamespaces(invalid)) } @@ -246,65 +144,8 @@ final class WCSessionTests: XCTestCase { "eip155": SessionNamespace( accounts: [ethAccount, polyAccount], methods: ["method", "method-2"], - events: ["event"], - extensions: nil)] + events: ["event"])] var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces()) XCTAssertThrowsError(try session.updateNamespaces(invalid)) } - - func testUpdateLessThanRequiredExtension() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionAccounts() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount], - methods: ["method-2", "newMethod-2"], - events: ["event-2", "newEvent-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionMethods() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount, polyAccount], - methods: ["method-2"], - events: ["event-2", "newEvent-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionEvents() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount, polyAccount], - methods: ["method-2", "newMethod-2"], - events: ["event-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } } From fecec947a7392d395705de544b17f0ba3322c43d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 23 Jan 2023 14:54:04 +0530 Subject: [PATCH 31/74] jsonRpcHistory key removed --- Sources/Chat/ChatStorageIdentifiers.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Chat/ChatStorageIdentifiers.swift b/Sources/Chat/ChatStorageIdentifiers.swift index 6da01afe4..dd8ba5b2d 100644 --- a/Sources/Chat/ChatStorageIdentifiers.swift +++ b/Sources/Chat/ChatStorageIdentifiers.swift @@ -3,5 +3,4 @@ import Foundation enum ChatStorageIdentifiers: String { case topicToInvitationPubKey = "com.walletconnect.chat.topicToInvitationPubKey" case threads = "com.walletconnect.chat.threads" - case jsonRpcHistory = "com.walletconnect.chat.jsonRpcHistory" } From 33e7153d781e05faaaf3f4def01de864d7658c0a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 23 Jan 2023 11:31:54 +0100 Subject: [PATCH 32/74] allow to configure echo host --- Sources/WalletConnectEcho/Echo.swift | 10 +++++++--- Sources/WalletConnectEcho/EchoClientFactory.swift | 4 ++-- Sources/WalletConnectEcho/EchoConfig.swift | 1 + Sources/WalletConnectPush/Push.swift | 6 +++--- Sources/WalletConnectPush/PushConfig.swift | 1 + 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Sources/WalletConnectEcho/Echo.swift b/Sources/WalletConnectEcho/Echo.swift index 3921ffebc..a9ade07f4 100644 --- a/Sources/WalletConnectEcho/Echo.swift +++ b/Sources/WalletConnectEcho/Echo.swift @@ -10,7 +10,8 @@ public class Echo { return EchoClientFactory.create( projectId: Networking.projectId, - clientId: config.clientId) + clientId: config.clientId, + echoHost: config.echoHost) }() private static var config: Config? @@ -19,7 +20,10 @@ public class Echo { /// Echo instance config method /// - Parameter clientId: https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/specs/clients/core/relay/relay-client-auth.md#overview - static public func configure(clientId: String) { - Echo.config = Echo.Config(clientId: clientId) + static public func configure( + clientId: String, + echoHost: String = "echo.walletconnect.com" + ) { + Echo.config = Echo.Config(clientId: clientId, echoHost: echoHost) } } diff --git a/Sources/WalletConnectEcho/EchoClientFactory.swift b/Sources/WalletConnectEcho/EchoClientFactory.swift index 859b0d37f..f1515d5a8 100644 --- a/Sources/WalletConnectEcho/EchoClientFactory.swift +++ b/Sources/WalletConnectEcho/EchoClientFactory.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking public struct EchoClientFactory { - public static func create(projectId: String, clientId: String) -> EchoClient { + public static func create(projectId: String, clientId: String, echoHost: String) -> EchoClient { - let httpClient = HTTPNetworkClient(host: "echo.walletconnect.com") + let httpClient = HTTPNetworkClient(host: echoHost) return EchoClientFactory.create( projectId: projectId, diff --git a/Sources/WalletConnectEcho/EchoConfig.swift b/Sources/WalletConnectEcho/EchoConfig.swift index acd6ade9f..d1eab366f 100644 --- a/Sources/WalletConnectEcho/EchoConfig.swift +++ b/Sources/WalletConnectEcho/EchoConfig.swift @@ -3,5 +3,6 @@ import Foundation extension Echo { struct Config { let clientId: String + let echoHost: String } } diff --git a/Sources/WalletConnectPush/Push.swift b/Sources/WalletConnectPush/Push.swift index eea9a582c..5b159bf58 100644 --- a/Sources/WalletConnectPush/Push.swift +++ b/Sources/WalletConnectPush/Push.swift @@ -16,7 +16,7 @@ public class Push { guard let config = Push.config else { fatalError("Error - you must call Push.configure(_:) before accessing the shared wallet instance.") } - Echo.configure(clientId: config.clientId) + Echo.configure(clientId: config.clientId, echoHost: config.echoHost) return WalletPushClientFactory.create( networkInteractor: Networking.interactor, pairingRegisterer: Pair.registerer, @@ -29,9 +29,9 @@ public class Push { private init() { } /// Wallet's configuration method - static public func configure() { + static public func configure(echoHost: String = "echo.walletconnect.com") { let clientId = try! Networking.interactor.getClientId() - Push.config = Push.Config(clientId: clientId) + Push.config = Push.Config(clientId: clientId, echoHost: echoHost) } } diff --git a/Sources/WalletConnectPush/PushConfig.swift b/Sources/WalletConnectPush/PushConfig.swift index bf0f083d1..8c7445dcc 100644 --- a/Sources/WalletConnectPush/PushConfig.swift +++ b/Sources/WalletConnectPush/PushConfig.swift @@ -3,5 +3,6 @@ import Foundation extension Push { struct Config { let clientId: String + let echoHost: String } } From 93218053d4e896359fa0c440901c19ea6cbc6aa2 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 23 Jan 2023 12:01:35 +0100 Subject: [PATCH 33/74] fix integration tests --- Example/IntegrationTests/Pairing/PairingTests.swift | 2 +- Example/IntegrationTests/Push/PushTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 8390e4e38..b0f51314a 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -69,7 +69,7 @@ final class PairingTests: XCTestCase { let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix) let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) walletPairingClient = pairingClient - let echoClient = EchoClientFactory.create(projectId: "", clientId: "") + let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com") walletPushClient = WalletPushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 123549b51..6cd781ec7 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -69,7 +69,7 @@ final class PushTests: XCTestCase { let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix) let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) walletPairingClient = pairingClient - let echoClient = EchoClientFactory.create(projectId: "", clientId: "") + let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com") walletPushClient = WalletPushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, From 269459964d6b526038b31a3d0fe90e83f3c2386d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 23 Jan 2023 12:46:33 +0100 Subject: [PATCH 34/74] savepoint --- Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift | 6 ++++-- Sources/WalletConnectSign/Session.swift | 1 + Sources/WalletConnectSign/Types/Session/WCSession.swift | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index a3624466a..7ea01e788 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -62,6 +62,7 @@ final class ApproveEngine { } let proposal = payload.request + let pairingTopic = payload.topic proposalPayloadsStore.delete(forKey: proposerPubKey) @@ -88,7 +89,7 @@ final class ApproveEngine { async let proposeResponse: () = networkingInteractor.respond(topic: payload.topic, response: response, protocolMethod: SessionProposeProtocolMethod()) - async let settleRequest: () = settle(topic: sessionTopic, proposal: proposal, namespaces: sessionNamespaces) + async let settleRequest: () = settle(topic: sessionTopic, proposal: proposal, namespaces: sessionNamespaces, pairingTopic: pairingTopic) _ = try await [proposeResponse, settleRequest] @@ -107,7 +108,7 @@ final class ApproveEngine { // TODO: Delete pairing if inactive } - func settle(topic: String, proposal: SessionProposal, namespaces: [String: SessionNamespace]) async throws { + func settle(topic: String, proposal: SessionProposal, namespaces: [String: SessionNamespace], pairingTopic: String) async throws { guard let agreementKeys = kms.getAgreementSecret(for: topic) else { throw Errors.agreementMissingOrInvalid } @@ -132,6 +133,7 @@ final class ApproveEngine { let session = WCSession( topic: topic, + pairingTopic: pairingTopic timestamp: Date(), selfParticipant: selfParticipant, peerParticipant: proposal.proposer, diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift index 99e981591..225c63432 100644 --- a/Sources/WalletConnectSign/Session.swift +++ b/Sources/WalletConnectSign/Session.swift @@ -5,6 +5,7 @@ import Foundation */ public struct Session { public let topic: String + public let pairingTopic: String public let peer: AppMetadata public let namespaces: [String: SessionNamespace] public let expiryDate: Date diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift index e4e4d2b14..756457aac 100644 --- a/Sources/WalletConnectSign/Types/Session/WCSession.swift +++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift @@ -7,6 +7,7 @@ struct WCSession: SequenceObject, Equatable { } let topic: String + let pairingTopic: String let relay: RelayProtocolOptions let selfParticipant: Participant let peerParticipant: Participant @@ -27,6 +28,7 @@ struct WCSession: SequenceObject, Equatable { } init(topic: String, + pairingTopic: String, timestamp: Date, selfParticipant: Participant, peerParticipant: Participant, @@ -34,6 +36,7 @@ struct WCSession: SequenceObject, Equatable { requiredNamespaces: [String: ProposalNamespace], acknowledged: Bool) { self.topic = topic + self.pairingTopic = pairingTopic self.timestamp = timestamp self.relay = settleParams.relay self.controller = AgreementPeer(publicKey: settleParams.controller.publicKey) @@ -179,6 +182,7 @@ struct WCSession: SequenceObject, Equatable { func publicRepresentation() -> Session { return Session( topic: topic, + pairingTopic: pairingTopic, peer: peerParticipant.metadata, namespaces: namespaces, expiryDate: expiryDate) From 879d4256dfd8bbfa14ff772a3be7a939520a31b9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 11:44:01 +0100 Subject: [PATCH 35/74] savepoint --- .../Engine/Common/ApproveEngine.swift | 14 ++++++++------ .../Types/Session/WCSession.swift | 7 ++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 7ea01e788..c4e7b1747 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -133,7 +133,7 @@ final class ApproveEngine { let session = WCSession( topic: topic, - pairingTopic: pairingTopic + pairingTopic: pairingTopic, timestamp: Date(), selfParticipant: selfParticipant, peerParticipant: proposal.proposer, @@ -317,15 +317,17 @@ private extension ApproveEngine { metadata: metadata ) - if let pairingTopic = try? sessionToPairingTopic.get(key: topic) { - pairingRegisterer.activate( - pairingTopic: pairingTopic, - peerMetadata: params.controller.metadata - ) + guard let pairingTopic = try? sessionToPairingTopic.get(key: topic) else { + return } + pairingRegisterer.activate( + pairingTopic: pairingTopic, + peerMetadata: params.controller.metadata + ) let session = WCSession( topic: topic, + pairingTopic: pairingTopic, timestamp: Date(), selfParticipant: selfParticipant, peerParticipant: params.controller, diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift index 756457aac..19d561d87 100644 --- a/Sources/WalletConnectSign/Types/Session/WCSession.swift +++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift @@ -51,6 +51,7 @@ struct WCSession: SequenceObject, Equatable { #if DEBUG internal init( topic: String, + pairingTopic: String, timestamp: Date, relay: RelayProtocolOptions, controller: AgreementPeer, @@ -64,6 +65,7 @@ struct WCSession: SequenceObject, Equatable { expiry: Int64 ) { self.topic = topic + self.pairingTopic = pairingTopic self.timestamp = timestamp self.relay = relay self.controller = controller @@ -194,7 +196,7 @@ struct WCSession: SequenceObject, Equatable { extension WCSession { enum CodingKeys: String, CodingKey { - case topic, relay, selfParticipant, peerParticipant, expiryDate, acknowledged, controller, namespaces, timestamp, requiredNamespaces + case topic, pairingTopic, relay, selfParticipant, peerParticipant, expiryDate, acknowledged, controller, namespaces, timestamp, requiredNamespaces } init(from decoder: Decoder) throws { @@ -211,6 +213,9 @@ extension WCSession { // Migration beta.102 self.timestamp = try container.decodeIfPresent(Date.self, forKey: .timestamp) ?? .distantPast self.requiredNamespaces = try container.decodeIfPresent([String: ProposalNamespace].self, forKey: .requiredNamespaces) ?? [:] + + // + self.pairingTopic = try container.decode(String.self, forKey: .pairingTopic) } func encode(to encoder: Encoder) throws { From 7ea3b20aba9bb2d52fb08a179f443530577aa29b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 12:13:21 +0100 Subject: [PATCH 36/74] savepoint --- Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift | 2 -- Sources/WalletConnectSign/Types/Session/WCSession.swift | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index c4e7b1747..accf087b2 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -78,7 +78,6 @@ final class ApproveEngine { let sessionTopic = agreementKey.derivedTopic() try kms.setAgreementSecret(agreementKey, topic: sessionTopic) - sessionToPairingTopic.set(payload.topic, forKey: sessionTopic) guard let relay = proposal.relays.first else { throw Errors.relayNotFound @@ -152,7 +151,6 @@ final class ApproveEngine { async let settleRequest: () = networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) _ = try await [settleRequest, subscription] - onSessionSettle?(session.publicRepresentation()) } } diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift index 19d561d87..21d9f7350 100644 --- a/Sources/WalletConnectSign/Types/Session/WCSession.swift +++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift @@ -221,6 +221,7 @@ extension WCSession { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(topic, forKey: .topic) + try container.encode(pairingTopic, forKey: .pairingTopic) try container.encode(relay, forKey: .relay) try container.encode(controller, forKey: .controller) try container.encode(selfParticipant, forKey: .selfParticipant) From a4aa80fa333504e0f591c9e4d77110824a9b41e6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 12:15:58 +0100 Subject: [PATCH 37/74] update WCSession --- Sources/WalletConnectSign/Types/Session/WCSession.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/WalletConnectSign/Types/Session/WCSession.swift b/Sources/WalletConnectSign/Types/Session/WCSession.swift index 21d9f7350..de6bc21ae 100644 --- a/Sources/WalletConnectSign/Types/Session/WCSession.swift +++ b/Sources/WalletConnectSign/Types/Session/WCSession.swift @@ -209,12 +209,8 @@ extension WCSession { self.namespaces = try container.decode([String: SessionNamespace].self, forKey: .namespaces) self.acknowledged = try container.decode(Bool.self, forKey: .acknowledged) self.expiryDate = try container.decode(Date.self, forKey: .expiryDate) - - // Migration beta.102 - self.timestamp = try container.decodeIfPresent(Date.self, forKey: .timestamp) ?? .distantPast - self.requiredNamespaces = try container.decodeIfPresent([String: ProposalNamespace].self, forKey: .requiredNamespaces) ?? [:] - - // + self.timestamp = try container.decode(Date.self, forKey: .timestamp) + self.requiredNamespaces = try container.decode([String: ProposalNamespace].self, forKey: .requiredNamespaces) self.pairingTopic = try container.decode(String.self, forKey: .pairingTopic) } From cd118387c9e28e2d908e96f4860695fc1099fa5d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 12:29:09 +0100 Subject: [PATCH 38/74] fix unit tests --- Tests/WalletConnectSignTests/ApproveEngineTests.swift | 8 +++++--- Tests/WalletConnectSignTests/Stub/Session+Stub.swift | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index dfbf0523e..45fc2c1ea 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -18,6 +18,7 @@ final class ApproveEngineTests: XCTestCase { var sessionStorageMock: WCSessionStorageMock! var pairingRegisterer: PairingRegistererMock! var proposalPayloadsStore: CodableStore>! + var sessionToPairingTopic: CodableStore! var publishers = Set() @@ -29,10 +30,11 @@ final class ApproveEngineTests: XCTestCase { sessionStorageMock = WCSessionStorageMock() pairingRegisterer = PairingRegistererMock() proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "") + sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") engine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, - sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), + sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingRegisterer, metadata: metadata, kms: cryptoMock, @@ -92,7 +94,7 @@ final class ApproveEngineTests: XCTestCase { let topicB = String.generateTopic() cryptoMock.setAgreementSecret(agreementKeys, topic: topicB) let proposal = SessionProposal.stub(proposerPubKey: AgreementPrivateKey().publicKey.hexRepresentation) - try await engine.settle(topic: topicB, proposal: proposal, namespaces: SessionNamespace.stubDictionary()) + try await engine.settle(topic: topicB, proposal: proposal, namespaces: SessionNamespace.stubDictionary(), pairingTopic: "") XCTAssertTrue(sessionStorageMock.hasSession(forTopic: topicB), "Responder must persist session on topic B") XCTAssert(networkingInteractor.didSubscribe(to: topicB), "Responder must subscribe for topic B") XCTAssertTrue(networkingInteractor.didCallRequest, "Responder must send session settle payload on topic B") @@ -105,7 +107,7 @@ final class ApproveEngineTests: XCTestCase { engine.onSessionSettle = { _ in didCallBackOnSessionApproved = true } - + sessionToPairingTopic.set("", forKey: sessionTopic) engine.settlingProposal = SessionProposal.stub() networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle())) diff --git a/Tests/WalletConnectSignTests/Stub/Session+Stub.swift b/Tests/WalletConnectSignTests/Stub/Session+Stub.swift index bbef34c9b..e60b778ec 100644 --- a/Tests/WalletConnectSignTests/Stub/Session+Stub.swift +++ b/Tests/WalletConnectSignTests/Stub/Session+Stub.swift @@ -18,6 +18,7 @@ extension WCSession { let controllerKey = isSelfController ? selfKey : peerKey return WCSession( topic: topic, + pairingTopic: "", timestamp: timestamp, relay: RelayProtocolOptions.stub(), controller: AgreementPeer(publicKey: controllerKey), From f65eab7ce3c3733f8e49a99548dbd4e71095b78e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 13:10:26 +0100 Subject: [PATCH 39/74] fix build --- Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index ad88efd30..1b6667b7b 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -73,8 +73,9 @@ public final class RPCHistory { else { return nil } return object } + } - public func getAll(of type: Object.Type) -> [Object] { + public func getAll(of type: Object.Type) -> [Object] { return getAllWithIDs(of: type).map { $0.value } } From ac7ac71f7c79ad922b186addb442e59b1cd337a7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 13:16:38 +0100 Subject: [PATCH 40/74] add host constant --- Sources/WalletConnectEcho/Echo.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectEcho/Echo.swift b/Sources/WalletConnectEcho/Echo.swift index a9ade07f4..6ebc721e2 100644 --- a/Sources/WalletConnectEcho/Echo.swift +++ b/Sources/WalletConnectEcho/Echo.swift @@ -2,7 +2,7 @@ import Foundation import WalletConnectNetworking public class Echo { - + static public let echoHost = "echo.walletconnect.com" public static var instance: EchoClient = { guard let config = Echo.config else { fatalError("Error - you must call Echo.configure(_:) before accessing the shared instance.") @@ -22,7 +22,7 @@ public class Echo { /// - Parameter clientId: https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/specs/clients/core/relay/relay-client-auth.md#overview static public func configure( clientId: String, - echoHost: String = "echo.walletconnect.com" + echoHost: String = echoHost ) { Echo.config = Echo.Config(clientId: clientId, echoHost: echoHost) } From 6d96d3466f96167c20bfc42d90621001d37754b3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 24 Jan 2023 15:02:45 +0100 Subject: [PATCH 41/74] update --- Example/DApp/Sign/Accounts/AccountsViewController.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index c4df4145b..c1d868b96 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -65,9 +65,8 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT func proposePushSubscription() { let account = session.namespaces.values.first!.accounts.first! - // temp solution - let pairingTopic = Pair.instance.getPairings().last!.topic - Task(priority: .high){ try! await Push.dapp.request(account: account, topic: pairingTopic)} + + Task(priority: .high){ try! await Push.dapp.request(account: account, topic: session.pairingTopic)} Push.dapp.responsePublisher.sink { (id: RPCID, result: Result) in switch result { case .success(let subscription): From 5de99f7e613591facc35bcfa21569492ea8de6dd Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 12:34:28 +0100 Subject: [PATCH 42/74] add test --- Tests/WalletConnectSignTests/WCSessionTests.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/WalletConnectSignTests/WCSessionTests.swift b/Tests/WalletConnectSignTests/WCSessionTests.swift index 3a0d567b4..4be39bb32 100644 --- a/Tests/WalletConnectSignTests/WCSessionTests.swift +++ b/Tests/WalletConnectSignTests/WCSessionTests.swift @@ -307,4 +307,11 @@ final class WCSessionTests: XCTestCase { var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) XCTAssertThrowsError(try session.updateNamespaces(invalid)) } + + func testEncodeDecode() { + let session = WCSession.stub() + let encodedSession = try! JSONEncoder().encode(session) + let decodedSession = try! JSONDecoder().decode(WCSession.self, from: encodedSession) + XCTAssertEqual(session, decodedSession) + } } From 10e6b19226bbe5e75a8048df8c949e0c1cddcd84 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 12:44:00 +0100 Subject: [PATCH 43/74] apply review suggestion --- Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 1b6667b7b..4fc00aebe 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -68,11 +68,7 @@ public final class RPCHistory { public func getAll(of type: Object.Type, topic: String) -> [Object] { return storage.getAll() .filter{$0.topic == topic} - .compactMap { record in - guard let object = try? record.request.params?.get(Object.self) - else { return nil } - return object - } + .compactMap { try? $0.request.params?.get(Object.self) } } public func getAll(of type: Object.Type) -> [Object] { From 05c42062343a0731682ceaa3a5b478a424f0f486 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 14:14:14 +0100 Subject: [PATCH 44/74] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 14 ++++++++++++-- .../ApplicationLayer/AppDelegate.swift | 7 ------- .../Configurator/ThirdPartyConfigurator.swift | 6 +++--- .../Wallet/PushRequest/PushRequestModule.swift | 17 +++++++++++++++++ .../Wallet/Wallet/WalletInteractor.swift | 7 ++++++- .../Wallet/Wallet/WalletPresenter.swift | 8 +++++++- .../Wallet/Wallet/WalletRouter.swift | 5 +++++ .../Client/Wallet/WalletPushClient.swift | 5 +++-- .../WalletConnectPush/Types/PushRequest.swift | 5 +++++ 9 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift create mode 100644 Sources/WalletConnectPush/Types/PushRequest.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index f17a47931..0f958a1db 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -61,6 +61,7 @@ 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B84929787A8000428BAF /* NotificationService.swift */; }; 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B85329787AAE00428BAF /* WalletConnectPush */; }; + 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8572981624F00428BAF /* PushRequestModule.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -381,6 +382,7 @@ 84E6B84729787A8000428BAF /* PNDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PNDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 84E6B8572981624F00428BAF /* PushRequestModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestModule.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; @@ -590,7 +592,6 @@ files = ( 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, A54195A52934E83F0035AD19 /* Web3 in Frameworks */, - 84E6B85629797CDE00428BAF /* WalletConnectPush in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -877,6 +878,14 @@ path = PNDecryptionService; sourceTree = ""; }; + 84E6B8592981625A00428BAF /* PushRequest */ = { + isa = PBXGroup; + children = ( + 84E6B8572981624F00428BAF /* PushRequestModule.swift */, + ); + path = PushRequest; + sourceTree = ""; + }; A50F3944288005A700064555 /* Types */ = { isa = PBXGroup; children = ( @@ -1436,6 +1445,7 @@ C5F32A2A2954812900A6476E /* ConnectionDetails */, C56EE236293F566A004840D1 /* Scan */, C56EE22A293F5668004840D1 /* Wallet */, + 84E6B8592981625A00428BAF /* PushRequest */, ); path = Wallet; sourceTree = ""; @@ -1674,7 +1684,6 @@ A5D85227286333E300DAF5C3 /* Starscream */, A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, A54195A42934E83F0035AD19 /* Web3 */, - 84E6B85529797CDE00428BAF /* WalletConnectPush */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -2131,6 +2140,7 @@ files = ( C5D4603A29687A5700302C7E /* DefaultSocketFactory.swift in Sources */, C53AA4362941251C008EA57C /* DefaultSignerFactory.swift in Sources */, + 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */, C55D3480295DD7140004314A /* AuthRequestPresenter.swift in Sources */, C5F32A2E2954814A00A6476E /* ConnectionDetailsRouter.swift in Sources */, C55D3482295DD7140004314A /* AuthRequestInteractor.swift in Sources */, diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift index e5a5e4bdd..e89ffe33d 100644 --- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift @@ -8,13 +8,6 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - - Push.wallet.requestPublisher.sink { (id: RPCID, account: Account, metadata: AppMetadata) in - Task(priority: .high) { try! await Push.wallet.approve(id: id) } - }.store(in: &publishers) - Push.wallet.pushMessagePublisher.sink { pm in - print(pm) - }.store(in: &publishers) return true } diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index 611e57b18..298207e2b 100644 --- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -15,8 +15,8 @@ struct ThirdPartyConfigurator: Configurator { ) Web3Wallet.configure(metadata: metadata, signerFactory: DefaultSignerFactory()) - Push.configure() - + Push.configure() + } - + } diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift new file mode 100644 index 000000000..09f195f5d --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift @@ -0,0 +1,17 @@ +import SwiftUI +import WalletConnectPush + +final class PushRequestModule { + @discardableResult + static func create(app: Application, pushRequest: PushRequest) -> UIViewController { + let router = SessionRequestRouter(app: app) + let interactor = SessionRequestInteractor() + let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest) + let view = SessionRequestView().environmentObject(presenter) + let viewController = SceneViewController(viewModel: presenter, content: view) + + router.viewController = viewController + + return viewController + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift index b79c6b255..3a4e3601e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift @@ -1,6 +1,7 @@ import Combine import Web3Wallet +import WalletConnectPush final class WalletInteractor { var requestPublisher: AnyPublisher { @@ -15,10 +16,14 @@ final class WalletInteractor { return Web3Wallet.instance.sessionRequestPublisher } + var pushRequestPublisher: AnyPublisher<(id: RPCID, account: Account, metadata: AppMetadata), Never> { + return Push.wallet.requestPublisher + } + var sessionsPublisher: AnyPublisher<[Session], Never> { return Web3Wallet.instance.sessionsPublisher } - + func getSessions() -> [Session] { return Web3Wallet.instance.getSessions() } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift index e6e677d8e..1e129ae89 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift @@ -71,7 +71,13 @@ extension WalletPresenter { .sink { [weak self] sessionRequest in self?.router.present(sessionRequest: sessionRequest) }.store(in: &disposeBag) - + + interactor.pushRequestPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] request in + self?.router.present(pushRequest: request) + }.store(in: &disposeBag) + interactor.sessionProposalPublisher .receive(on: DispatchQueue.main) .sink { [weak self] proposal in diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift index c32bfd8d3..d5288f424 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift @@ -1,6 +1,7 @@ import UIKit import Web3Wallet +import WalletConnectPush final class WalletRouter { weak var viewController: UIViewController! @@ -25,6 +26,10 @@ final class WalletRouter { SessionRequestModule.create(app: app, sessionRequest: sessionRequest) .presentFullScreen(from: viewController, transparentBackground: true) } + + func present(pushRequest: PushRequest) { + + } func presentPaste(onValue: @escaping (String) -> Void, onError: @escaping (Error) -> Void) { PasteUriModule.create(app: app, onValue: onValue, onError: onError) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index b38596db5..65ef69e89 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -4,13 +4,14 @@ import WalletConnectNetworking import WalletConnectPairing import WalletConnectEcho + public class WalletPushClient { private var publishers = Set() - private let requestPublisherSubject = PassthroughSubject<(id: RPCID, account: Account, metadata: AppMetadata), Never>() + private let requestPublisherSubject = PassthroughSubject() - public var requestPublisher: AnyPublisher<(id: RPCID, account: Account, metadata: AppMetadata), Never> { + public var requestPublisher: AnyPublisher { requestPublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPush/Types/PushRequest.swift b/Sources/WalletConnectPush/Types/PushRequest.swift new file mode 100644 index 000000000..a27b87a9b --- /dev/null +++ b/Sources/WalletConnectPush/Types/PushRequest.swift @@ -0,0 +1,5 @@ + +import Foundation +import WalletConnectPairing + +public typealias PushRequest = (id: RPCID, account: Account, metadata: AppMetadata) From 9e2f66962715f234177df8b9c1a548fea6757f8f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 14:24:16 +0100 Subject: [PATCH 45/74] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 16 +++ .../PushRequest/PushRequestInteractor.swift | 12 ++ .../PushRequest/PushRequestModule.swift | 8 +- .../PushRequest/PushRequestPresenter.swift | 51 +++++++ .../PushRequest/PushRequestRouter.swift | 15 +++ .../Wallet/PushRequest/PushRequestView.swift | 127 ++++++++++++++++++ 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift create mode 100644 Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift create mode 100644 Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift create mode 100644 Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 0f958a1db..99569608b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -62,6 +62,10 @@ 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B85329787AAE00428BAF /* WalletConnectPush */; }; 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8572981624F00428BAF /* PushRequestModule.swift */; }; + 84E6B85B298162EF00428BAF /* PushRequestPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */; }; + 84E6B85D298162F700428BAF /* PushRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85C298162F700428BAF /* PushRequestRouter.swift */; }; + 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */; }; + 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8602981630C00428BAF /* PushRequestView.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -383,6 +387,10 @@ 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84E6B8572981624F00428BAF /* PushRequestModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestModule.swift; sourceTree = ""; }; + 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestPresenter.swift; sourceTree = ""; }; + 84E6B85C298162F700428BAF /* PushRequestRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestRouter.swift; sourceTree = ""; }; + 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestInteractor.swift; sourceTree = ""; }; + 84E6B8602981630C00428BAF /* PushRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRequestView.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; @@ -882,6 +890,10 @@ isa = PBXGroup; children = ( 84E6B8572981624F00428BAF /* PushRequestModule.swift */, + 84E6B85A298162EF00428BAF /* PushRequestPresenter.swift */, + 84E6B85C298162F700428BAF /* PushRequestRouter.swift */, + 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */, + 84E6B8602981630C00428BAF /* PushRequestView.swift */, ); path = PushRequest; sourceTree = ""; @@ -2142,6 +2154,7 @@ C53AA4362941251C008EA57C /* DefaultSignerFactory.swift in Sources */, 84E6B8582981624F00428BAF /* PushRequestModule.swift in Sources */, C55D3480295DD7140004314A /* AuthRequestPresenter.swift in Sources */, + 84E6B85B298162EF00428BAF /* PushRequestPresenter.swift in Sources */, C5F32A2E2954814A00A6476E /* ConnectionDetailsRouter.swift in Sources */, C55D3482295DD7140004314A /* AuthRequestInteractor.swift in Sources */, C55D34B12965FB750004314A /* SessionProposalInteractor.swift in Sources */, @@ -2171,6 +2184,8 @@ C55D3493295DFA750004314A /* WelcomeModule.swift in Sources */, C56EE270293F56D7004840D1 /* String.swift in Sources */, C56EE279293F56D7004840D1 /* Color.swift in Sources */, + 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */, + 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */, C55D3483295DD7140004314A /* AuthRequestView.swift in Sources */, C56EE243293F566D004840D1 /* ScanView.swift in Sources */, C56EE288293F5757004840D1 /* ThirdPartyConfigurator.swift in Sources */, @@ -2197,6 +2212,7 @@ C5B2F6FB297055B0000DBA0E /* ETHSigner.swift in Sources */, C56EE274293F56D7004840D1 /* SceneViewController.swift in Sources */, C55D3496295DFA750004314A /* WelcomeInteractor.swift in Sources */, + 84E6B85D298162F700428BAF /* PushRequestRouter.swift in Sources */, C5B2F6FC297055B0000DBA0E /* SOLSigner.swift in Sources */, C55D348D295DD8CA0004314A /* PasteUriView.swift in Sources */, C5F32A2C2954814200A6476E /* ConnectionDetailsModule.swift in Sources */, diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift new file mode 100644 index 000000000..8848eeea8 --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -0,0 +1,12 @@ +import Foundation +import WalletConnectPush + +final class PushRequestInteractor { + func approve(pushRequest: PushRequest) async throws { + + } + + func reject(pushRequest: PushRequest) async throws { + + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift index 09f195f5d..3062207e9 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestModule.swift @@ -4,10 +4,10 @@ import WalletConnectPush final class PushRequestModule { @discardableResult static func create(app: Application, pushRequest: PushRequest) -> UIViewController { - let router = SessionRequestRouter(app: app) - let interactor = SessionRequestInteractor() - let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest) - let view = SessionRequestView().environmentObject(presenter) + let router = PushRequestRouter(app: app) + let interactor = PushRequestInteractor() + let presenter = PushRequestPresenter(interactor: interactor, router: router, pushRequest: pushRequest) + let view = PushRequestView().environmentObject(presenter) let viewController = SceneViewController(viewModel: presenter, content: view) router.viewController = viewController diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift new file mode 100644 index 000000000..dab573703 --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift @@ -0,0 +1,51 @@ +import UIKit +import Combine +import WalletConnectPush + +final class PushRequestPresenter: ObservableObject { + private let interactor: PushRequestInteractor + private let router: PushRequestRouter + + let pushRequest: PushRequest + + var message: String { + return String(describing: pushRequest.account) + } + + private var disposeBag = Set() + + init( + interactor: PushRequestInteractor, + router: PushRequestRouter, + pushRequest: PushRequest + ) { + defer { setupInitialState() } + self.interactor = interactor + self.router = router + self.pushRequest = pushRequest + } + + @MainActor + func onApprove() async throws { + try await interactor.approve(pushRequest: pushRequest) + router.dismiss() + } + + @MainActor + func onReject() async throws { + try await interactor.reject(pushRequest: pushRequest) + router.dismiss() + } +} + +// MARK: - Private functions +private extension SessionRequestPresenter { + func setupInitialState() { + + } +} + +// MARK: - SceneViewModel +extension SessionRequestPresenter: SceneViewModel { + +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift new file mode 100644 index 000000000..6ac5f730c --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestRouter.swift @@ -0,0 +1,15 @@ +import UIKit + +final class PushRequestRouter { + weak var viewController: UIViewController! + + private let app: Application + + init(app: Application) { + self.app = app + } + + func dismiss() { + viewController.dismiss() + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift new file mode 100644 index 000000000..86ebffe65 --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift @@ -0,0 +1,127 @@ +import SwiftUI + +struct PushRequestView: View { + @EnvironmentObject var presenter: PushRequestPresenter + + @State var text = "" + + var body: some View { + ZStack { + Color.black.opacity(0.6) + + VStack { + Spacer() + + VStack(spacing: 0) { + Image("header") + .resizable() + .scaledToFit() + + Text(presenter.pushRequest.account.absoluteString) + .foregroundColor(.grey8) + .font(.system(size: 22, weight: .bold, design: .rounded)) + .padding(.top, 10) + + authRequestView() + + HStack(spacing: 20) { + Button { + Task(priority: .userInitiated) { try await + presenter.onReject() + } + } label: { + Text("Decline") + .frame(maxWidth: .infinity) + .foregroundColor(.white) + .font(.system(size: 20, weight: .semibold, design: .rounded)) + .padding(.vertical, 11) + .background( + LinearGradient( + gradient: Gradient(colors: [ + .foregroundNegative, + .lightForegroundNegative + ]), + startPoint: .top, endPoint: .bottom) + ) + .cornerRadius(20) + } + .shadow(color: .white.opacity(0.25), radius: 8, y: 2) + + Button { + Task(priority: .userInitiated) { try await + presenter.onApprove() + } + } label: { + Text("Allow") + .frame(maxWidth: .infinity) + .foregroundColor(.white) + .font(.system(size: 20, weight: .semibold, design: .rounded)) + .padding(.vertical, 11) + .background( + LinearGradient( + gradient: Gradient(colors: [ + .foregroundPositive, + .lightForegroundPositive + ]), + startPoint: .top, endPoint: .bottom) + ) + .cornerRadius(20) + } + .shadow(color: .white.opacity(0.25), radius: 8, y: 2) + } + .padding(.top, 25) + } + .padding(20) + .background(.ultraThinMaterial) + .cornerRadius(34) + .padding(.horizontal, 10) + + Spacer() + } + } + .edgesIgnoringSafeArea(.all) + } + + private func authRequestView() -> some View { + VStack { + VStack(alignment: .leading) { + Text("Message") + .font(.system(size: 15, weight: .semibold, design: .rounded)) + .foregroundColor(.whiteBackground) + .padding(.horizontal, 8) + .padding(.vertical, 5) + .background(Color.grey70) + .cornerRadius(28, corners: .allCorners) + .padding(.leading, 15) + .padding(.top, 9) + + VStack(spacing: 0) { + ScrollView { + Text(presenter.message) + .foregroundColor(.grey50) + .font(.system(size: 13, weight: .semibold, design: .rounded)) + } + .padding(.horizontal, 18) + .padding(.vertical, 10) + .frame(height: 250) + } + .background(Color.whiteBackground) + .cornerRadius(20, corners: .allCorners) + .padding(.horizontal, 5) + .padding(.bottom, 5) + + } + .background(.thinMaterial) + .cornerRadius(25, corners: .allCorners) + } + .padding(.top, 30) + } +} + +#if DEBUG +struct PushRequestView_Previews: PreviewProvider { + static var previews: some View { + PushRequestView() + } +} +#endif From 29c498d6e19a9a312ba266ba478b54573f7a3562 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 14:48:51 +0100 Subject: [PATCH 46/74] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 9 +++++++++ Example/PNDecryptionService/NotificationService.swift | 2 +- .../Wallet/PushRequest/PushRequestPresenter.swift | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 99569608b..f2afacd9a 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ 84E6B85D298162F700428BAF /* PushRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85C298162F700428BAF /* PushRequestRouter.swift */; }; 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */; }; 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8602981630C00428BAF /* PushRequestView.swift */; }; + 84E6B86329816A7900428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B86229816A7900428BAF /* WalletConnectPush */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -609,6 +610,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 84E6B86329816A7900428BAF /* WalletConnectPush in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1714,6 +1716,9 @@ dependencies = ( ); name = PNDecryptionService; + packageProductDependencies = ( + 84E6B86229816A7900428BAF /* WalletConnectPush */, + ); productName = PNDecryptionService; productReference = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; productType = "com.apple.product-type.app-extension"; @@ -2981,6 +2986,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectPush; }; + 84E6B86229816A7900428BAF /* WalletConnectPush */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectPush; + }; A54195A42934E83F0035AD19 /* Web3 */ = { isa = XCSwiftPackageProductDependency; package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; diff --git a/Example/PNDecryptionService/NotificationService.swift b/Example/PNDecryptionService/NotificationService.swift index 3adee82cc..a818245f4 100644 --- a/Example/PNDecryptionService/NotificationService.swift +++ b/Example/PNDecryptionService/NotificationService.swift @@ -12,7 +12,7 @@ class NotificationService: UNNotificationServiceExtension { bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { let topic = bestAttemptContent.userInfo["topic"] as! String - let ciphertext = bestAttemptContent.userInfo["ciphertext"] as! String + let ciphertext = bestAttemptContent.userInfo["blob"] as! String do { let service = PushDecryptionService() let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext) diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift index dab573703..194b5e8e3 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestPresenter.swift @@ -39,13 +39,13 @@ final class PushRequestPresenter: ObservableObject { } // MARK: - Private functions -private extension SessionRequestPresenter { +private extension PushRequestPresenter { func setupInitialState() { } } // MARK: - SceneViewModel -extension SessionRequestPresenter: SceneViewModel { +extension PushRequestPresenter: SceneViewModel { } From 28ad4b8a7fdf7938c5a2bce721026a49e6d8ab1c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 25 Jan 2023 15:43:41 +0100 Subject: [PATCH 47/74] present push proposal --- Example/DApp/Sign/Accounts/AccountsViewController.swift | 1 - Example/ExampleApp.xcodeproj/project.pbxproj | 7 +++++++ .../PresentationLayer/Wallet/Wallet/WalletRouter.swift | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index c4df4145b..8ad7920a6 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -60,7 +60,6 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT accountsDetails.append(AccountDetails(chain: account.blockchainIdentifier, methods: Array(namespace.methods), account: account.address)) // TODO: Rethink how this info is displayed on example } } - proposePushSubscription() } func proposePushSubscription() { diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index f2afacd9a..47480e8c1 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -67,6 +67,7 @@ 84E6B85F2981630000428BAF /* PushRequestInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B85E2981630000428BAF /* PushRequestInteractor.swift */; }; 84E6B8612981630C00428BAF /* PushRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B8602981630C00428BAF /* PushRequestView.swift */; }; 84E6B86329816A7900428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B86229816A7900428BAF /* WalletConnectPush */; }; + 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 84E6B8642981720400428BAF /* WalletConnectPush */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; @@ -601,6 +602,7 @@ files = ( 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, A54195A52934E83F0035AD19 /* Web3 in Frameworks */, + 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -1698,6 +1700,7 @@ A5D85227286333E300DAF5C3 /* Starscream */, A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, A54195A42934E83F0035AD19 /* Web3 */, + 84E6B8642981720400428BAF /* WalletConnectPush */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -2990,6 +2993,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectPush; }; + 84E6B8642981720400428BAF /* WalletConnectPush */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectPush; + }; A54195A42934E83F0035AD19 /* Web3 */ = { isa = XCSwiftPackageProductDependency; package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift index d5288f424..8cc1c31ed 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift @@ -28,7 +28,8 @@ final class WalletRouter { } func present(pushRequest: PushRequest) { - + PushRequestModule.create(app: app, pushRequest: pushRequest) + .presentFullScreen(from: viewController, transparentBackground: true) } func presentPaste(onValue: @escaping (String) -> Void, onError: @escaping (Error) -> Void) { From 40d2bdb04ec880a155124e3ad05980f15d1c1b36 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 25 Jan 2023 22:33:18 +0530 Subject: [PATCH 48/74] ChatStorage with KayedDatabases --- Sources/Chat/Chat.swift | 14 +++++- Sources/Chat/ChatClientFactory.swift | 11 +++-- Sources/Chat/ChatStorage.swift | 49 ++++++++++++------- Sources/Chat/ChatStorageIdentifiers.swift | 2 + Sources/Chat/Database.swift | 37 -------------- Sources/Chat/Extensions/Dictionary.swift | 19 +++++++ Sources/Chat/KeyedDatabase.swift | 42 ++++++++++++++++ .../Accounts/AccountService.swift | 14 ++++++ .../Invitee/InvitationHandlingService.swift | 3 +- .../Inviter/InviteService.swift | 2 +- Sources/Chat/Types/Invite.swift | 6 ++- Sources/Chat/Types/Thread.swift | 2 +- Sources/Web3Inbox/Web3Inbox.swift | 4 +- 13 files changed, 140 insertions(+), 65 deletions(-) delete mode 100644 Sources/Chat/Database.swift create mode 100644 Sources/Chat/Extensions/Dictionary.swift create mode 100644 Sources/Chat/KeyedDatabase.swift create mode 100644 Sources/Chat/ProtocolServices/Accounts/AccountService.swift diff --git a/Sources/Chat/Chat.swift b/Sources/Chat/Chat.swift index 9c10e0492..31430fe0b 100644 --- a/Sources/Chat/Chat.swift +++ b/Sources/Chat/Chat.swift @@ -5,8 +5,20 @@ public class Chat { /// Chat client instance public static var instance: ChatClient = { - return ChatClientFactory.create() + guard let account = account else { + fatalError("Error - you must call Chat.configure(_:) before accessing the shared instance.") + } + return ChatClientFactory.create(account: account) }() + private static var account: Account? + private init() { } + + /// Chat instance config method + /// - Parameters: + /// - account: Chat initial account + static public func configure(account: Account) { + Chat.account = account + } } diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 513316e26..9a5b9c1ad 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -2,11 +2,12 @@ import Foundation public struct ChatClientFactory { - static func create() -> ChatClient { + static func create(account: Account) -> ChatClient { let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.showcase") let client = HTTPNetworkClient(host: "keys.walletconnect.com") let registry = KeyserverRegistryProvider(client: client) return ChatClientFactory.create( + account: account, registry: registry, relayClient: Relay.instance, kms: KeyManagementService(keychain: keychain), @@ -16,6 +17,7 @@ public struct ChatClientFactory { } public static func create( + account: Account, registry: Registry, relayClient: RelayClient, kms: KeyManagementService, @@ -26,8 +28,11 @@ public struct ChatClientFactory { let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) - let threadStore = Database(keyValueStorage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) - let chatStorage = ChatStorage(history: rpcHistory, threadStore: threadStore) + let accountService = AccountService(currentAccount: account) + let messageStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue) + let inviteStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.invites.rawValue) + let threadStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) + let chatStorage = ChatStorage(accountService: accountService, messageStore: messageStore, inviteStore: inviteStore, threadStore: threadStore) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift index 0d7c0f00e..8c86e1472 100644 --- a/Sources/Chat/ChatStorage.swift +++ b/Sources/Chat/ChatStorage.swift @@ -2,55 +2,70 @@ import Foundation struct ChatStorage { - private let history: RPCHistory - private let threadStore: Database + private let accountService: AccountService + private let messageStore: KeyedDatabase + private let inviteStore: KeyedDatabase + private let threadStore: KeyedDatabase - init(history: RPCHistory, threadStore: Database) { - self.history = history + init( + accountService: AccountService, + messageStore: KeyedDatabase, + inviteStore: KeyedDatabase, + threadStore: KeyedDatabase + ) { + self.accountService = accountService + self.messageStore = messageStore + self.inviteStore = inviteStore self.threadStore = threadStore } // MARK: - Invites func getInvite(id: Int64) -> Invite? { - guard - let record = history.get(recordId: RPCID(id)), - let payload = try? record.request.params?.get(InvitePayload.self) - else { return nil } + return inviteStore.getElements(for: accountKey) + .first(where: { $0.id == id }) + } - return Invite(id: record.id.integer, payload: payload) + func set(_ invite: Invite) { + inviteStore.set(invite, for: accountKey) } func getInviteTopic(id: Int64) -> String? { - return history.get(recordId: RPCID(id))?.topic + return getInvites().first(where: { $0.id == id })?.topic } func getInvites() -> [Invite] { - return history.getAllWithIDs(of: InvitePayload.self) - .map { Invite(id: $0.id.integer, payload: $0.value) } + return inviteStore.getElements(for: accountKey) } func delete(invite: Invite) { - history.delete(id: RPCID(invite.id)) + inviteStore.delete(invite, for: accountKey) } // MARK: - Threads func getThreads() -> [Thread] { - return threadStore.getAll() + return threadStore.getElements(for: accountKey) } func getThread(topic: String) -> Thread? { return getThreads().first(where: { $0.topic == topic }) } - func add(thread: Thread) { - threadStore.add(thread) + func set(thread: Thread) { + threadStore.set(thread, for: accountKey) } // MARK: - Messages func getMessages(topic: String) -> [Message] { - return history.getAll(of: Message.self).filter { $0.topic == topic } + return messageStore.getElements(for: accountKey).filter { $0.topic == topic } + } +} + +private extension ChatStorage { + + var accountKey: String { + return accountService.currentAccount.absoluteString } } diff --git a/Sources/Chat/ChatStorageIdentifiers.swift b/Sources/Chat/ChatStorageIdentifiers.swift index dd8ba5b2d..e9eda634d 100644 --- a/Sources/Chat/ChatStorageIdentifiers.swift +++ b/Sources/Chat/ChatStorageIdentifiers.swift @@ -2,5 +2,7 @@ import Foundation enum ChatStorageIdentifiers: String { case topicToInvitationPubKey = "com.walletconnect.chat.topicToInvitationPubKey" + case messages = "com.walletconnect.chat.messages" case threads = "com.walletconnect.chat.threads" + case invites = "com.walletconnect.chat.invites" } diff --git a/Sources/Chat/Database.swift b/Sources/Chat/Database.swift deleted file mode 100644 index 6d8aa098b..000000000 --- a/Sources/Chat/Database.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -class Database where Element: Codable { - - private var array = [Element]() - private let keyValueStorage: KeyValueStorage - private let identifier: String - - init(keyValueStorage: KeyValueStorage, identifier: String) { - self.keyValueStorage = keyValueStorage - self.identifier = identifier - - if let data = keyValueStorage.object(forKey: identifier) as? Data, - let decoded = try? JSONDecoder().decode([Element].self, from: data) { - array = decoded - } - } - - func filter(_ isIncluded: (Element) -> Bool) -> [Element]? { - return Array(array.filter(isIncluded)) - } - - func getAll() -> [Element] { - array - } - - func add(_ element: Element) { - array.append(element) - if let encoded = try? JSONEncoder().encode(array) { - keyValueStorage.set(encoded, forKey: identifier) - } - } - - func first(where predicate: (Element) -> Bool) -> Element? { - array.first(where: predicate) - } -} diff --git a/Sources/Chat/Extensions/Dictionary.swift b/Sources/Chat/Extensions/Dictionary.swift new file mode 100644 index 000000000..90fb7ffc6 --- /dev/null +++ b/Sources/Chat/Extensions/Dictionary.swift @@ -0,0 +1,19 @@ +import Foundation + +extension Dictionary where Value: RangeReplaceableCollection, Value.Element: Equatable { + + mutating func append(_ element: Value.Iterator.Element, for key: Key) { + var value: Value = self[key] ?? Value() + value.append(element) + self[key] = value + } + + mutating func delete(_ element: Value.Iterator.Element, for key: Key) { + guard + let value: Value = self[key], + value.contains(where: { $0 == element }) + else { return } + + self[key] = value.filter { $0 != element } + } +} diff --git a/Sources/Chat/KeyedDatabase.swift b/Sources/Chat/KeyedDatabase.swift new file mode 100644 index 000000000..923ffa0b2 --- /dev/null +++ b/Sources/Chat/KeyedDatabase.swift @@ -0,0 +1,42 @@ +import Foundation + +class KeyedDatabase where Element: Codable & Equatable { + + private var index: [String: [Element]] = [:] { + didSet { storage.set(index, forKey: identifier) } + } + + private let storage: KeyValueStorage + private let identifier: String + + init(storage: KeyValueStorage, identifier: String) { + self.storage = storage + self.identifier = identifier + + initializeIndex() + } + + func getElements(for key: String) -> [Element] { + return index[key] ?? [] + } + + func set(_ element: Element, for key: String) { + index.append(element, for: key) + } + + func delete(_ element: Element, for key: String) { + index.delete(element, for: key) + } +} + +private extension KeyedDatabase { + + func initializeIndex() { + guard + let data = storage.object(forKey: identifier) as? Data, + let decoded = try? JSONDecoder().decode([String: [Element]].self, from: data) + else { return } + + index = decoded + } +} diff --git a/Sources/Chat/ProtocolServices/Accounts/AccountService.swift b/Sources/Chat/ProtocolServices/Accounts/AccountService.swift new file mode 100644 index 000000000..e0343aa0d --- /dev/null +++ b/Sources/Chat/ProtocolServices/Accounts/AccountService.swift @@ -0,0 +1,14 @@ +import Foundation + +final class AccountService { + + private(set) var currentAccount: Account + + init(currentAccount: Account) { + self.currentAccount = currentAccount + } + + func setAccount(_ account: Account) { + currentAccount = account + } +} diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index fd7039aba..ee865ed0f 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -69,7 +69,7 @@ class InvitationHandlingService { peerAccount: invite.account ) - chatStorage.add(thread: thread) + chatStorage.set(thread: thread) chatStorage.delete(invite: invite) onNewThread?(thread) @@ -99,6 +99,7 @@ class InvitationHandlingService { logger.debug("did receive an invite") onInvite?(Invite( id: payload.id.integer, + topic: payload.topic, payload: payload.request )) }.store(in: &publishers) diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index c030ede0c..cee79341d 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -85,7 +85,7 @@ class InviteService { peerAccount: peerAccount ) - chatStorage.add(thread: thread) + chatStorage.set(thread: thread) onNewThread?(thread) // TODO - remove symKeyI diff --git a/Sources/Chat/Types/Invite.swift b/Sources/Chat/Types/Invite.swift index 5ca3bd37d..4815731d7 100644 --- a/Sources/Chat/Types/Invite.swift +++ b/Sources/Chat/Types/Invite.swift @@ -1,13 +1,15 @@ import Foundation -public struct Invite: Codable { +public struct Invite: Codable, Equatable { public let id: Int64 + public let topic: String public let message: String public let account: Account public let publicKey: String - init(id: Int64, payload: InvitePayload) { + init(id: Int64, topic: String, payload: InvitePayload) { self.id = id + self.topic = topic self.message = payload.message self.account = payload.account self.publicKey = payload.publicKey diff --git a/Sources/Chat/Types/Thread.swift b/Sources/Chat/Types/Thread.swift index 8768c20a6..ed5ef5ca0 100644 --- a/Sources/Chat/Types/Thread.swift +++ b/Sources/Chat/Types/Thread.swift @@ -1,6 +1,6 @@ import Foundation -public struct Thread: Codable { +public struct Thread: Codable, Equatable { public let topic: String public let selfAccount: Account public let peerAccount: Account diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index a482f2b21..2ce3d85ee 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -14,9 +14,9 @@ public final class Web3Inbox { private init() { } - /// Sign instance config method + /// Web3Inbox instance config method /// - Parameters: - /// - metadata: App metadata + /// - account: Web3Inbox initial account static public func configure(account: Account) { Web3Inbox.account = account } From cfe8f01ffea29ddcb19e53e09f41c4d4fc95f3aa Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 25 Jan 2023 23:03:25 +0530 Subject: [PATCH 49/74] Setting Invite and Message Stores --- Sources/Chat/ChatStorage.swift | 10 +++++++++- .../ProtocolServices/Common/MessagingService.swift | 2 ++ .../Invitee/InvitationHandlingService.swift | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift index 8c86e1472..04c55117f 100644 --- a/Sources/Chat/ChatStorage.swift +++ b/Sources/Chat/ChatStorage.swift @@ -26,7 +26,7 @@ struct ChatStorage { .first(where: { $0.id == id }) } - func set(_ invite: Invite) { + func set(invite: Invite) { inviteStore.set(invite, for: accountKey) } @@ -58,6 +58,14 @@ struct ChatStorage { // MARK: - Messages + func set(message: Message) { + messageStore.set(message, for: accountKey) + } + + func getMessages() -> [Message] { + return messageStore.getElements(for: accountKey) + } + func getMessages(topic: String) -> [Message] { return messageStore.getElements(for: accountKey).filter { $0.topic == topic } } diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index acd5ff051..9396012c6 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -37,6 +37,7 @@ class MessagingService { let request = RPCRequest(method: protocolMethod.method, params: message) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) + chatStorage.set(message: message) onMessage?(message) } @@ -64,6 +65,7 @@ class MessagingService { protocolMethod: ChatMessageProtocolMethod() ) logger.debug("Received message") + chatStorage.set(message: message) onMessage?(message) } } diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index ee865ed0f..e42a09827 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -97,11 +97,13 @@ class InvitationHandlingService { networkingInteractor.requestSubscription(on: ChatInviteProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("did receive an invite") - onInvite?(Invite( + let invite = Invite( id: payload.id.integer, topic: payload.topic, payload: payload.request - )) + ) + chatStorage.set(invite: invite) + onInvite?(invite) }.store(in: &publishers) } From 0af1a64e01ea0ca7ba86a1e46bb079dee4b1d961 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 25 Jan 2023 23:16:32 +0530 Subject: [PATCH 50/74] setAccount on registration --- Example/IntegrationTests/Chat/ChatTests.swift | 3 ++- Sources/Chat/ChatClientFactory.swift | 2 +- .../Invitee/RegistryService.swift | 14 +++++++++----- Tests/ChatTests/RegistryManagerTests.swift | 15 ++++++++++++--- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 0f7d770ce..b5d8d20c3 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -22,7 +22,8 @@ final class ChatTests: XCTestCase { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger) - return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + let account = Account("eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07")! + return ChatClientFactory.create(account: account, registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } func testInvite() async { diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 9a5b9c1ad..726ae88ce 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -33,7 +33,7 @@ public struct ChatClientFactory { let inviteStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.invites.rawValue) let threadStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) let chatStorage = ChatStorage(accountService: accountService, messageStore: messageStore, inviteStore: inviteStore, threadStore: threadStore) - let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) + let registryService = RegistryService(registry: registry, accountService: accountService, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry) diff --git a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift index 002102fb3..8d990a2cb 100644 --- a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift @@ -1,19 +1,22 @@ import Foundation actor RegistryService { - let networkingInteractor: NetworkInteracting - let topicToRegistryRecordStore: CodableStore - let registry: Registry - let logger: ConsoleLogging - let kms: KeyManagementServiceProtocol + private let networkingInteractor: NetworkInteracting + private let accountService: AccountService + private let topicToRegistryRecordStore: CodableStore + private let registry: Registry + private let logger: ConsoleLogging + private let kms: KeyManagementServiceProtocol init(registry: Registry, + accountService: AccountService, networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, logger: ConsoleLogging, topicToRegistryRecordStore: CodableStore) { self.registry = registry self.kms = kms + self.accountService = accountService self.networkingInteractor = networkingInteractor self.logger = logger self.topicToRegistryRecordStore = topicToRegistryRecordStore @@ -28,6 +31,7 @@ actor RegistryService { let record = RegistryRecord(account: account, pubKey: pubKeyHex) topicToRegistryRecordStore.set(record, forKey: topic) try await networkingInteractor.subscribe(topic: topic) + accountService.setAccount(account) logger.debug("Did register an account: \(account) and is subscribing on topic: \(topic)") return pubKeyHex } diff --git a/Tests/ChatTests/RegistryManagerTests.swift b/Tests/ChatTests/RegistryManagerTests.swift index 1b87397c6..ec1fcfa2c 100644 --- a/Tests/ChatTests/RegistryManagerTests.swift +++ b/Tests/ChatTests/RegistryManagerTests.swift @@ -11,15 +11,21 @@ final class RegistryManagerTests: XCTestCase { var networkingInteractor: NetworkingInteractorMock! var topicToRegistryRecordStore: CodableStore! var registry: Registry! + var accountService: AccountService! var kms: KeyManagementServiceMock! + let initialAccount = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! + let newAccount = Account("eip155:2:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! + override func setUp() { registry = KeyValueRegistry() networkingInteractor = NetworkingInteractorMock() kms = KeyManagementServiceMock() topicToRegistryRecordStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") + accountService = AccountService(currentAccount: initialAccount) registryManager = RegistryService( registry: registry, + accountService: accountService, networkingInteractor: networkingInteractor, kms: kms, logger: ConsoleLoggerMock(), @@ -27,11 +33,14 @@ final class RegistryManagerTests: XCTestCase { } func testRegister() async { - let account = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! - _ = try! await registryManager.register(account: account) + XCTAssertEqual(accountService.currentAccount, initialAccount) + + _ = try! await registryManager.register(account: newAccount) XCTAssert(!networkingInteractor.subscriptions.isEmpty, "networkingInteractors subscribes to new topic") - let resolved = try! await registry.resolve(account: account) + + let resolved = try! await registry.resolve(account: newAccount) XCTAssertNotNil(resolved, "register account is resolvable") XCTAssertFalse(topicToRegistryRecordStore.getAll().isEmpty, "stores topic to invitation") + XCTAssertEqual(accountService.currentAccount, newAccount) } } From af91fde437a006a50bc7a9fbdf26566ae445e7d1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 26 Jan 2023 12:32:46 +0530 Subject: [PATCH 51/74] account param removed from client interface --- Example/IntegrationTests/Chat/ChatTests.swift | 23 +++++++----------- Sources/Chat/ChatClient.swift | 10 ++++---- Sources/Chat/ChatClientFactory.swift | 2 +- .../Common/MessagingService.swift | 5 ++-- .../Inviter/InviteService.swift | 12 +++++++--- Sources/Chat/Types/Message.swift | 24 ++++++++++++------- .../ChatClient/ChatClientProxy.swift | 16 +++---------- 7 files changed, 43 insertions(+), 49 deletions(-) diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index b5d8d20c3..a10fb95e2 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -12,26 +12,26 @@ final class ChatTests: XCTestCase { var registry: KeyValueRegistry! private var publishers = [AnyCancellable]() + let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! + let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! + override func setUp() { registry = KeyValueRegistry() - invitee = makeClient(prefix: "🦖 Registered") - inviter = makeClient(prefix: "🍄 Inviter") + invitee = makeClient(prefix: "🦖 Registered", account: inviteeAccount) + inviter = makeClient(prefix: "🍄 Inviter", account: inviterAccount) } - func makeClient(prefix: String) -> ChatClient { + func makeClient(prefix: String, account: Account) -> ChatClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger) - let account = Account("eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07")! return ChatClientFactory.create(account: account, registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } func testInvite() async { let inviteExpectation = expectation(description: "invitation expectation") - let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! - let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! try! await invitee.register(account: inviteeAccount) - try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "", account: inviterAccount) + try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "") invitee.invitePublisher.sink { _ in inviteExpectation.fulfill() }.store(in: &publishers) @@ -41,12 +41,10 @@ final class ChatTests: XCTestCase { func testAcceptAndCreateNewThread() { let newThreadInviterExpectation = expectation(description: "new thread on inviting client expectation") let newThreadinviteeExpectation = expectation(description: "new thread on invitee client expectation") - let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! - let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! Task(priority: .high) { try! await invitee.register(account: inviteeAccount) - try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount) + try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message") } invitee.invitePublisher.sink { [unowned self] invite in @@ -68,12 +66,9 @@ final class ChatTests: XCTestCase { let messageExpectation = expectation(description: "message received") messageExpectation.expectedFulfillmentCount = 4 // expectedFulfillmentCount 4 because onMessage() called on send too - let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! - let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! - Task(priority: .high) { try! await invitee.register(account: inviteeAccount) - try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount) + try! await inviter.invite(peerAccount: inviteeAccount, openingMessage: "opening message") } invitee.invitePublisher.sink { [unowned self] invite in diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index 97ea14e4a..c000d0ca5 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -80,8 +80,8 @@ public class ChatClient { /// - publicKey: publicKey associated with a peer /// - openingMessage: oppening message for a chat invite /// TODO - peerAccount should be derived - public func invite(peerAccount: Account, openingMessage: String, account: Account) async throws { - try await inviteService.invite(peerAccount: peerAccount, openingMessage: openingMessage, account: account) + public func invite(peerAccount: Account, openingMessage: String) async throws { + try await inviteService.invite(peerAccount: peerAccount, openingMessage: openingMessage) } public func accept(inviteId: Int64) async throws { @@ -110,13 +110,11 @@ public class ChatClient { try await leaveService.leave(topic: topic) } - public func getInvites(account: Account) -> [Invite] { - // TODO: Account based storage + public func getInvites() -> [Invite] { return chatStorage.getInvites() } - public func getThreads(account: Account) -> [Thread] { - // TODO: Account based storage + public func getThreads() -> [Thread] { return chatStorage.getThreads() } diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 726ae88ce..8a70e9f47 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -36,7 +36,7 @@ public struct ChatClientFactory { let registryService = RegistryService(registry: registry, accountService: accountService, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) - let inviteService = InviteService(networkingInteractor: networkingInteractor, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry) + let inviteService = InviteService(networkingInteractor: networkingInteractor, accountService: accountService, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry) let leaveService = LeaveService() let messagingService = MessagingService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index 9396012c6..df0e96ada 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -50,9 +50,8 @@ class MessagingService { private func setUpRequestHandling() { networkingInteractor.requestSubscription(on: ChatMessageProtocolMethod()) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - var message = payload.request - message.topic = payload.topic + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + let message = Message(topic: payload.topic, payload: payload.request) handleMessage(message, topic: payload.topic, requestId: payload.id) }.store(in: &publishers) } diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index cee79341d..421ce8193 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -4,6 +4,7 @@ import Combine class InviteService { private var publishers = [AnyCancellable]() private let networkingInteractor: NetworkInteracting + private let accountService: AccountService private let logger: ConsoleLogging private let kms: KeyManagementService private let chatStorage: ChatStorage @@ -14,6 +15,7 @@ class InviteService { init( networkingInteractor: NetworkInteracting, + accountService: AccountService, kms: KeyManagementService, chatStorage: ChatStorage, logger: ConsoleLogging, @@ -21,6 +23,7 @@ class InviteService { ) { self.kms = kms self.networkingInteractor = networkingInteractor + self.accountService = accountService self.logger = logger self.chatStorage = chatStorage self.registry = registry @@ -29,12 +32,11 @@ class InviteService { var peerAccount: Account! - func invite(peerAccount: Account, openingMessage: String, account: Account) async throws { + func invite(peerAccount: Account, openingMessage: String) async throws { // TODO ad storage let protocolMethod = ChatInviteProtocolMethod() self.peerAccount = peerAccount let selfPubKeyY = try kms.createX25519KeyPair() - let invite = InvitePayload(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation) let peerPubKey = try await registry.resolve(account: peerAccount) let symKeyI = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: peerPubKey) let inviteTopic = try AgreementPublicKey(hex: peerPubKey).rawRepresentation.sha256().toHexString() @@ -42,7 +44,11 @@ class InviteService { // overrides on invite toipic try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic) - let request = RPCRequest(method: protocolMethod.method, params: invite) + let request = RPCRequest(method: protocolMethod.method, params: InvitePayload( + message: openingMessage, + account: accountService.currentAccount, + publicKey: selfPubKeyY.hexRepresentation + )) // 2. Proposer subscribes to topic R which is the hash of the derived symKey let responseTopic = symKeyI.derivedTopic() diff --git a/Sources/Chat/Types/Message.swift b/Sources/Chat/Types/Message.swift index ac4e1b33e..406c2c682 100644 --- a/Sources/Chat/Types/Message.swift +++ b/Sources/Chat/Types/Message.swift @@ -1,22 +1,28 @@ import Foundation public struct Message: Codable, Equatable { - public var topic: String? + public let topic: String public let message: String public let authorAccount: Account public let timestamp: Int64 - enum CodingKeys: String, CodingKey { - case topic - case message - case authorAccount - case timestamp - } - - init(topic: String? = nil, message: String, authorAccount: Account, timestamp: Int64) { + init(topic: String, message: String, authorAccount: Account, timestamp: Int64) { self.topic = topic self.message = message self.authorAccount = authorAccount self.timestamp = timestamp } + + init(topic: String, payload: MessagePayload) { + self.topic = topic + self.message = payload.message + self.authorAccount = payload.authorAccount + self.timestamp = payload.timestamp + } +} + +struct MessagePayload: Codable { + let message: String + let authorAccount: Account + let timestamp: Int64 } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index ed9ce3101..b2382ceb6 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -16,13 +16,11 @@ final class ChatClientProxy { switch event { case .getInvites: - let params = try parse(GetInvitesRequest.self, params: request.params) - let invites = client.getInvites(account: params.account) + let invites = client.getInvites() try await respond(with: invites, request: request) case .getThreads: - let params = try parse(GetThreadsRequest.self, params: request.params) - let threads = client.getThreads(account: params.account) + let threads = client.getThreads() try await respond(with: threads, request: request) case .register: @@ -52,7 +50,7 @@ final class ChatClientProxy { case .invite: let params = try parse(InviteRequest.self, params: request.params) - try await client.invite(peerAccount: params.invite.account, openingMessage: params.invite.message, account: params.account) + try await client.invite(peerAccount: params.invite.account, openingMessage: params.invite.message) try await respond(request: request) } } @@ -67,14 +65,6 @@ private extension ChatClientProxy { case unregisteredParams } - struct GetInvitesRequest: Codable { - let account: Account - } - - struct GetThreadsRequest: Codable { - let account: Account - } - struct RegisterRequest: Codable { let account: Account } From de08da8f211bb645961342f325e6fedbc86bbe6c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 26 Jan 2023 14:05:15 +0530 Subject: [PATCH 52/74] Showcase ChatService updated --- .../ApplicationLayer/Application.swift | 2 +- .../DomainLayer/Chat/ChatService.swift | 38 ++++++++++++------- .../Chat/ChatList/ChatListInteractor.swift | 8 ++-- .../Chat/ChatList/ChatListPresenter.swift | 14 +++---- .../Chat/Invite/InviteInteractor.swift | 2 +- .../InviteList/InviteListInteractor.swift | 4 +- .../Chat/InviteList/InviteListPresenter.swift | 11 +++--- Sources/Web3Inbox/Web3Inbox.swift | 1 + 8 files changed, 45 insertions(+), 35 deletions(-) diff --git a/Example/Showcase/Classes/ApplicationLayer/Application.swift b/Example/Showcase/Classes/ApplicationLayer/Application.swift index 75d88c640..46b30ef87 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Application.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Application.swift @@ -4,7 +4,7 @@ import WalletConnectChat final class Application { lazy var chatService: ChatService = { - return ChatService(client: Chat.instance) + return ChatService(accountStorage: accountStorage) }() lazy var accountStorage: AccountStorage = { diff --git a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift index 76264ff76..839ef53a6 100644 --- a/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift +++ b/Example/Showcase/Classes/DomainLayer/Chat/ChatService.swift @@ -7,14 +7,26 @@ typealias Stream = AsyncPublisher> final class ChatService { - private let client: ChatClient - - init(client: ChatClient) { - self.client = client + private lazy var client: ChatClient = { + guard let account = accountStorage.account else { + fatalError("Error - you must call Chat.configure(_:) before accessing the shared instance.") + } + Chat.configure(account: account) + return Chat.instance + }() + + private lazy var networking: NetworkingClient = { + return Networking.instance + }() + + private let accountStorage: AccountStorage + + init(accountStorage: AccountStorage) { + self.accountStorage = accountStorage } var connectionPublisher: Stream { - return client.socketConnectionStatusPublisher.values + return networking.socketConnectionStatusPublisher.values } var messagePublisher: Stream { @@ -29,16 +41,16 @@ final class ChatService { return client.invitePublisher.values } - func getMessages(thread: WalletConnectChat.Thread) async -> [WalletConnectChat.Message] { - await client.getMessages(topic: thread.topic) + func getMessages(thread: WalletConnectChat.Thread) -> [WalletConnectChat.Message] { + client.getMessages(topic: thread.topic) } - func getThreads(account: Account) async -> [WalletConnectChat.Thread] { - await client.getThreads(account: account) + func getThreads() -> [WalletConnectChat.Thread] { + client.getThreads() } - func getInvites(account: Account) async -> [WalletConnectChat.Invite] { - client.getInvites(account: account) + func getInvites() -> [WalletConnectChat.Invite] { + client.getInvites() } func sendMessage(topic: String, message: String) async throws { @@ -53,8 +65,8 @@ final class ChatService { try await client.reject(inviteId: invite.id) } - func invite(peerAccount: Account, message: String, selfAccount: Account) async throws { - try await client.invite(peerAccount: peerAccount, openingMessage: message, account: selfAccount) + func invite(peerAccount: Account, message: String) async throws { + try await client.invite(peerAccount: peerAccount, openingMessage: message) } func register(account: Account) async throws { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift index a5a28b513..09a0c976a 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListInteractor.swift @@ -10,16 +10,16 @@ final class ChatListInteractor { self.accountStorage = accountStorage } - func getThreads(account: Account) async -> [WalletConnectChat.Thread] { - return await chatService.getThreads(account: account) + func getThreads() -> [WalletConnectChat.Thread] { + return chatService.getThreads() } func threadsSubscription() -> Stream { return chatService.threadPublisher } - func getInvites(account: Account) async -> [Invite] { - return await chatService.getInvites(account: account) + func getInvites() -> [Invite] { + return chatService.getInvites() } func invitesSubscription() -> Stream { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift index 204afb1d0..a947cdeba 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift @@ -88,26 +88,24 @@ private extension ChatListPresenter { @MainActor func setupInvites() async { - await loadInvites() + loadInvites() for await _ in interactor.invitesSubscription() { - await loadInvites() + loadInvites() } } @MainActor func loadThreads() async { - let threads = await interactor.getThreads(account: account) - self.threads = threads - .filter { $0.selfAccount == account } + self.threads = interactor.getThreads() .sorted(by: { $0.topic < $1.topic }) .map { ThreadViewModel(thread: $0) } } @MainActor - func loadInvites() async { - let invites = await interactor.getInvites(account: account) - self.invites = invites.sorted(by: { $0.publicKey < $1.publicKey }) + func loadInvites() { + self.invites = interactor.getInvites() + .sorted(by: { $0.publicKey < $1.publicKey }) .map { InviteViewModel(invite: $0) } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift index 26af501c6..e707b2a1a 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Invite/InviteInteractor.swift @@ -6,6 +6,6 @@ final class InviteInteractor { } func invite(peerAccount: Account, message: String, selfAccount: Account) async { - try! await chatService.invite(peerAccount: peerAccount, message: message, selfAccount: selfAccount) + try! await chatService.invite(peerAccount: peerAccount, message: message) } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift index 58c404ab9..1880de742 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListInteractor.swift @@ -7,8 +7,8 @@ final class InviteListInteractor { self.chatService = chatService } - func getInvites(account: Account) async -> [Invite] { - return await chatService.getInvites(account: account) + func getInvites() -> [Invite] { + return chatService.getInvites() } func invitesSubscription() -> Stream { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift index 385a0d37d..915f08ed4 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/InviteList/InviteListPresenter.swift @@ -19,10 +19,10 @@ final class InviteListPresenter: ObservableObject { @MainActor func setupInitialState() async { - await loadInvites(account: account) + loadInvites() for await _ in interactor.invitesSubscription() { - await loadInvites(account: account) + loadInvites() } } @@ -58,10 +58,9 @@ extension InviteListPresenter: SceneViewModel { private extension InviteListPresenter { - @MainActor - func loadInvites(account: Account) async { - let invites = await interactor.getInvites(account: account) - self.invites = invites.sorted(by: { $0.publicKey < $1.publicKey }) + func loadInvites() { + invites = interactor.getInvites() + .sorted(by: { $0.publicKey < $1.publicKey }) .map { InviteViewModel(invite: $0) } } diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index 2ce3d85ee..c6fdb4a69 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -18,6 +18,7 @@ public final class Web3Inbox { /// - Parameters: /// - account: Web3Inbox initial account static public func configure(account: Account) { + Chat.configure(account: account) Web3Inbox.account = account } } From 9d666fe76ac35bc49f3241e8bb95597fb697cc07 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 26 Jan 2023 11:19:10 +0100 Subject: [PATCH 53/74] add entitlements --- Example/ExampleApp.xcodeproj/project.pbxproj | 24 +++++++++++++++---- .../PNDecryptionService.entitlements | 10 ++++++++ .../PNDecryptionServiceRelease.entitlements | 10 ++++++++ .../PushRequest/PushRequestInteractor.swift | 4 ++-- .../Wallet/PushRequest/PushRequestView.swift | 8 +++---- Example/WalletApp/WalletApp.entitlements | 10 ++++++++ .../WalletApp/WalletAppRelease.entitlements | 10 ++++++++ .../NetworkingClientFactory.swift | 2 +- .../PairingClientFactory.swift | 2 +- .../Wallet/WalletPushClientFactory.swift | 2 +- Sources/WalletConnectRelay/RelayClient.swift | 2 +- 11 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 Example/PNDecryptionService/PNDecryptionService.entitlements create mode 100644 Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements create mode 100644 Example/WalletApp/WalletApp.entitlements create mode 100644 Example/WalletApp/WalletAppRelease.entitlements diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 47480e8c1..7abe9e3a0 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -364,6 +364,8 @@ 845B8D8B2934B36C0084A966 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8485617E295307C20064877B /* PushNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationTests.swift; sourceTree = ""; }; + 849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletAppRelease.entitlements; sourceTree = ""; }; + 849A4F19298281F100E61ACE /* PNDecryptionServiceRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionServiceRelease.entitlements; sourceTree = ""; }; 849D7A92292E2169006A2BD4 /* PushTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushTests.swift; sourceTree = ""; }; 84AA01DA28CF0CD7005D48D8 /* XCTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTest.swift; sourceTree = ""; }; 84B767762954554A00E92316 /* PushDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PushDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -385,6 +387,8 @@ 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; 84CEC64528D89D6B00D081A8 /* PairingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTests.swift; sourceTree = ""; }; 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; + 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletApp.entitlements; sourceTree = ""; }; + 84DB38F129828A7F00BFEE37 /* PNDecryptionService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionService.entitlements; sourceTree = ""; }; 84E6B84729787A8000428BAF /* PNDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PNDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -884,6 +888,8 @@ 84E6B84829787A8000428BAF /* PNDecryptionService */ = { isa = PBXGroup; children = ( + 84DB38F129828A7F00BFEE37 /* PNDecryptionService.entitlements */, + 849A4F19298281F100E61ACE /* PNDecryptionServiceRelease.entitlements */, 84E6B84929787A8000428BAF /* NotificationService.swift */, 84E6B84B29787A8000428BAF /* Info.plist */, ); @@ -1442,6 +1448,8 @@ C56EE21C293F55ED004840D1 /* WalletApp */ = { isa = PBXGroup; children = ( + 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */, + 849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */, C56EE25C293F56D6004840D1 /* Common */, C56EE27E293F5756004840D1 /* ApplicationLayer */, C56EE29E293F577B004840D1 /* PresentationLayer */, @@ -2588,9 +2596,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionService.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = RQ4NX29V75; + DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PNDecryptionService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; @@ -2615,9 +2624,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionServiceRelease.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = RQ4NX29V75; + DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PNDecryptionService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; @@ -2631,6 +2642,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -2784,9 +2796,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = RQ4NX29V75; + DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WalletApp/Other/Info.plist; @@ -2815,9 +2828,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WalletApp/WalletAppRelease.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = RQ4NX29V75; + DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WalletApp/Other/Info.plist; @@ -2833,6 +2848,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Example/PNDecryptionService/PNDecryptionService.entitlements b/Example/PNDecryptionService/PNDecryptionService.entitlements new file mode 100644 index 000000000..0bd7a7310 --- /dev/null +++ b/Example/PNDecryptionService/PNDecryptionService.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.walletconnect.sdk + + + diff --git a/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements b/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements new file mode 100644 index 000000000..0bd7a7310 --- /dev/null +++ b/Example/PNDecryptionService/PNDecryptionServiceRelease.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.walletconnect.sdk + + + diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift index 8848eeea8..b21fdb8a6 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -3,10 +3,10 @@ import WalletConnectPush final class PushRequestInteractor { func approve(pushRequest: PushRequest) async throws { - + try await Push.wallet.approve(id: pushRequest.id) } func reject(pushRequest: PushRequest) async throws { - + try await Push.wallet.reject(id: pushRequest.id) } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift index 86ebffe65..62a21e17f 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestView.swift @@ -17,12 +17,12 @@ struct PushRequestView: View { .resizable() .scaledToFit() - Text(presenter.pushRequest.account.absoluteString) + Text("would you like to send notifications") .foregroundColor(.grey8) .font(.system(size: 22, weight: .bold, design: .rounded)) .padding(.top, 10) - authRequestView() + pushRequestView() HStack(spacing: 20) { Button { @@ -82,10 +82,10 @@ struct PushRequestView: View { .edgesIgnoringSafeArea(.all) } - private func authRequestView() -> some View { + private func pushRequestView() -> some View { VStack { VStack(alignment: .leading) { - Text("Message") + Text("Notifications") .font(.system(size: 15, weight: .semibold, design: .rounded)) .foregroundColor(.whiteBackground) .padding(.horizontal, 8) diff --git a/Example/WalletApp/WalletApp.entitlements b/Example/WalletApp/WalletApp.entitlements new file mode 100644 index 000000000..0bd7a7310 --- /dev/null +++ b/Example/WalletApp/WalletApp.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.walletconnect.sdk + + + diff --git a/Example/WalletApp/WalletAppRelease.entitlements b/Example/WalletApp/WalletAppRelease.entitlements new file mode 100644 index 000000000..0bd7a7310 --- /dev/null +++ b/Example/WalletApp/WalletAppRelease.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.walletconnect.sdk + + + diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift index b1d249880..6b5e39a2c 100644 --- a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -3,7 +3,7 @@ import Foundation public struct NetworkingClientFactory { public static func create(relayClient: RelayClient) -> NetworkingInteractor { - let logger = ConsoleLogger(loggingLevel: .off) + let logger = ConsoleLogger(loggingLevel: .debug) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 8666747ac..40ac67ee8 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -3,7 +3,7 @@ import Foundation public struct PairingClientFactory { public static func create(networkingClient: NetworkingInteractor) -> PairingClient { - let logger = ConsoleLogger(loggingLevel: .off) + let logger = ConsoleLogger(loggingLevel: .debug) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 2ed976c16..4b8d1115a 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -5,7 +5,7 @@ import WalletConnectEcho public struct WalletPushClientFactory { public static func create(networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient { - let logger = ConsoleLogger(loggingLevel: .off) + let logger = ConsoleLogger(loggingLevel: .debug) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 59b2fb4a7..fef002f8d 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -81,7 +81,7 @@ public final class RelayClient { keychainStorage: KeychainStorageProtocol = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk"), socketFactory: WebSocketFactory, socketConnectionType: SocketConnectionType = .automatic, - logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off) + logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug) ) { let didKeyFactory = ED25519DIDKeyFactory() let clientIdStorage = ClientIdStorage(keychain: keychainStorage, didKeyFactory: didKeyFactory) From 5e74c92eb1546fe3b56a3b034ab5ee484e599362 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 26 Jan 2023 14:02:28 +0100 Subject: [PATCH 54/74] turn off logs --- .../Wallet/PushRequest/PushRequestInteractor.swift | 4 ++-- Sources/WalletConnectEcho/EchoClientFactory.swift | 2 +- Sources/WalletConnectNetworking/NetworkingClientFactory.swift | 2 +- Sources/WalletConnectPairing/PairingClientFactory.swift | 2 +- .../Client/Wallet/WalletPushClientFactory.swift | 2 +- Sources/WalletConnectRelay/RelayClient.swift | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift index b21fdb8a6..eb6d6f329 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -3,9 +3,9 @@ import WalletConnectPush final class PushRequestInteractor { func approve(pushRequest: PushRequest) async throws { - try await Push.wallet.approve(id: pushRequest.id) + try await Push.wallet.approve(id: pushRequest.id) } - + func reject(pushRequest: PushRequest) async throws { try await Push.wallet.reject(id: pushRequest.id) } diff --git a/Sources/WalletConnectEcho/EchoClientFactory.swift b/Sources/WalletConnectEcho/EchoClientFactory.swift index f1515d5a8..ff7b8cbb6 100644 --- a/Sources/WalletConnectEcho/EchoClientFactory.swift +++ b/Sources/WalletConnectEcho/EchoClientFactory.swift @@ -16,7 +16,7 @@ public struct EchoClientFactory { clientId: String, httpClient: HTTPClient) -> EchoClient { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(loggingLevel: .off) let registerService = EchoRegisterService(httpClient: httpClient, projectId: projectId, clientId: clientId, logger: logger) return EchoClient( diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift index 6b5e39a2c..b1d249880 100644 --- a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -3,7 +3,7 @@ import Foundation public struct NetworkingClientFactory { public static func create(relayClient: RelayClient) -> NetworkingInteractor { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 40ac67ee8..8666747ac 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -3,7 +3,7 @@ import Foundation public struct PairingClientFactory { public static func create(networkingClient: NetworkingInteractor) -> PairingClient { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 4b8d1115a..2ed976c16 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -5,7 +5,7 @@ import WalletConnectEcho public struct WalletPushClientFactory { public static func create(networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index fef002f8d..59b2fb4a7 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -81,7 +81,7 @@ public final class RelayClient { keychainStorage: KeychainStorageProtocol = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk"), socketFactory: WebSocketFactory, socketConnectionType: SocketConnectionType = .automatic, - logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug) + logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off) ) { let didKeyFactory = ED25519DIDKeyFactory() let clientIdStorage = ClientIdStorage(keychain: keychainStorage, didKeyFactory: didKeyFactory) From 607c0c0594c45e8c4115ffbecc28d3b00da15c91 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 27 Jan 2023 00:10:20 +0530 Subject: [PATCH 55/74] Web3Inbox app repaired --- Sources/Chat/KeyedDatabase.swift | 7 ++++++- Sources/Chat/Types/Message.swift | 8 ++++---- Sources/Web3Inbox/ChatClient/ChatClientProxy.swift | 2 +- Sources/Web3Inbox/Web3InboxClientFactory.swift | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Sources/Chat/KeyedDatabase.swift b/Sources/Chat/KeyedDatabase.swift index 923ffa0b2..bc110e572 100644 --- a/Sources/Chat/KeyedDatabase.swift +++ b/Sources/Chat/KeyedDatabase.swift @@ -3,7 +3,7 @@ import Foundation class KeyedDatabase where Element: Codable & Equatable { private var index: [String: [Element]] = [:] { - didSet { storage.set(index, forKey: identifier) } + didSet { self.set(index, for: identifier) } } private let storage: KeyValueStorage @@ -39,4 +39,9 @@ private extension KeyedDatabase { index = decoded } + + func set(_ value: [String: [Element]], for key: String) { + let data = try! JSONEncoder().encode(value) + storage.set(data, forKey: key) + } } diff --git a/Sources/Chat/Types/Message.swift b/Sources/Chat/Types/Message.swift index 406c2c682..ba6e3ac43 100644 --- a/Sources/Chat/Types/Message.swift +++ b/Sources/Chat/Types/Message.swift @@ -21,8 +21,8 @@ public struct Message: Codable, Equatable { } } -struct MessagePayload: Codable { - let message: String - let authorAccount: Account - let timestamp: Int64 +public struct MessagePayload: Codable { + public let message: String + public let authorAccount: Account + public let timestamp: Int64 } diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index b2382ceb6..291d0b1a3 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -75,7 +75,7 @@ private extension ChatClientProxy { struct MessageRequest: Codable { let topic: String - let payload: Message + let payload: MessagePayload } struct AcceptRequest: Codable { diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index b24e1993b..5a54dc042 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -4,7 +4,7 @@ import WebKit final class Web3InboxClientFactory { static func create(chatClient: ChatClient, account: Account) -> Web3InboxClient { - let host = "https://web3inbox-dev-hidden-git-feat-figma-matching-walletconnect1.vercel.app/?chatProvider=ios" + let host = "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios" let logger = ConsoleLogger(suffix: "📬") let webviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() From 4ebd15b0c6ecabe1da48d16b22968cbdc57eb462 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 27 Jan 2023 12:54:12 +0530 Subject: [PATCH 56/74] Multi-account subscriptions --- .../Chat/Import/ImportPresenter.swift | 11 ++-- .../Chat/Import/ImportView.swift | 6 +-- Sources/Chat/ChatClient.swift | 12 ++--- Sources/Chat/ChatClientFactory.swift | 14 ++--- Sources/Chat/ChatStorage.swift | 54 +++++++------------ .../Common/MessagingService.swift | 18 ++++--- .../Common/ResubscriptionService.swift | 16 +++++- .../Invitee/InvitationHandlingService.swift | 23 +++++--- .../Invitee/RegistryService.swift | 12 +++++ .../Inviter/InviteService.swift | 9 ++-- Tests/ChatTests/RegistryManagerTests.swift | 36 +++++++++++-- 11 files changed, 125 insertions(+), 86 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift index 9768456f4..09428a950 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift @@ -15,11 +15,12 @@ final class ImportPresenter: ObservableObject { self.router = router } - func didPressImport() { + @MainActor + func didPressImport() async { guard let account = AccountNameResolver.resolveAccount(input) else { return input = .empty } interactor.save(account: account) - register(account: account) + await interactor.register(account: account) router.presentChat(account: account) } } @@ -44,10 +45,4 @@ private extension ImportPresenter { func setupInitialState() { } - - func register(account: Account) { - Task(priority: .high) { - await interactor.register(account: account) - } - } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift index 6f83f4a4b..028156aaf 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift @@ -15,9 +15,9 @@ struct ImportView: View { Spacer() - BrandButton(title: "Ok, done", action: { - presenter.didPressImport() - }) + BrandButton(title: "Ok, done" ) { Task(priority: .userInitiated) { + await presenter.didPressImport() + }} .padding(16.0) } } diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index c000d0ca5..57cdf66bf 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -6,10 +6,10 @@ public class ChatClient { private let registry: Registry private let registryService: RegistryService private let messagingService: MessagingService + private let accountService: AccountService private let invitationHandlingService: InvitationHandlingService private let inviteService: InviteService private let leaveService: LeaveService - private let resubscriptionService: ResubscriptionService private let kms: KeyManagementService private let chatStorage: ChatStorage @@ -35,10 +35,10 @@ public class ChatClient { init(registry: Registry, registryService: RegistryService, messagingService: MessagingService, + accountService: AccountService, invitationHandlingService: InvitationHandlingService, inviteService: InviteService, leaveService: LeaveService, - resubscriptionService: ResubscriptionService, kms: KeyManagementService, chatStorage: ChatStorage, socketConnectionStatusPublisher: AnyPublisher @@ -46,10 +46,10 @@ public class ChatClient { self.registry = registry self.registryService = registryService self.messagingService = messagingService + self.accountService = accountService self.invitationHandlingService = invitationHandlingService self.inviteService = inviteService self.leaveService = leaveService - self.resubscriptionService = resubscriptionService self.kms = kms self.chatStorage = chatStorage self.socketConnectionStatusPublisher = socketConnectionStatusPublisher @@ -111,15 +111,15 @@ public class ChatClient { } public func getInvites() -> [Invite] { - return chatStorage.getInvites() + return chatStorage.getInvites(account: accountService.currentAccount) } public func getThreads() -> [Thread] { - return chatStorage.getThreads() + return chatStorage.getThreads(account: accountService.currentAccount) } public func getMessages(topic: String) -> [Message] { - return chatStorage.getMessages(topic: topic) + return chatStorage.getMessages(topic: topic, account: accountService.currentAccount) } private func setUpEnginesCallbacks() { diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 8a70e9f47..526261241 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -28,26 +28,26 @@ public struct ChatClientFactory { let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) - let accountService = AccountService(currentAccount: account) let messageStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.messages.rawValue) let inviteStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.invites.rawValue) let threadStore = KeyedDatabase(storage: keyValueStorage, identifier: ChatStorageIdentifiers.threads.rawValue) - let chatStorage = ChatStorage(accountService: accountService, messageStore: messageStore, inviteStore: inviteStore, threadStore: threadStore) - let registryService = RegistryService(registry: registry, accountService: accountService, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) - let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) - let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) + let accountService = AccountService(currentAccount: account) + let chatStorage = ChatStorage(messageStore: messageStore, inviteStore: inviteStore, threadStore: threadStore) + let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: logger) + let registryService = RegistryService(registry: registry, accountService: accountService, resubscriptionService: resubscriptionService, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) + let invitationHandlingService = InvitationHandlingService(registry: registry, networkingInteractor: networkingInteractor, accountService: accountService, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore, chatStorage: chatStorage) let inviteService = InviteService(networkingInteractor: networkingInteractor, accountService: accountService, kms: kms, chatStorage: chatStorage, logger: logger, registry: registry) let leaveService = LeaveService() - let messagingService = MessagingService(networkingInteractor: networkingInteractor, chatStorage: chatStorage, logger: logger) + let messagingService = MessagingService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: logger) let client = ChatClient( registry: registry, registryService: registryService, messagingService: messagingService, + accountService: accountService, invitationHandlingService: invitationHandlingService, inviteService: inviteService, leaveService: leaveService, - resubscriptionService: resubscriptionService, kms: kms, chatStorage: chatStorage, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher diff --git a/Sources/Chat/ChatStorage.swift b/Sources/Chat/ChatStorage.swift index 04c55117f..f6f0c0a72 100644 --- a/Sources/Chat/ChatStorage.swift +++ b/Sources/Chat/ChatStorage.swift @@ -2,18 +2,15 @@ import Foundation struct ChatStorage { - private let accountService: AccountService private let messageStore: KeyedDatabase private let inviteStore: KeyedDatabase private let threadStore: KeyedDatabase init( - accountService: AccountService, messageStore: KeyedDatabase, inviteStore: KeyedDatabase, threadStore: KeyedDatabase ) { - self.accountService = accountService self.messageStore = messageStore self.inviteStore = inviteStore self.threadStore = threadStore @@ -21,59 +18,48 @@ struct ChatStorage { // MARK: - Invites - func getInvite(id: Int64) -> Invite? { - return inviteStore.getElements(for: accountKey) + func getInvite(id: Int64, account: Account) -> Invite? { + return inviteStore.getElements(for: account.absoluteString) .first(where: { $0.id == id }) } - func set(invite: Invite) { - inviteStore.set(invite, for: accountKey) + func set(invite: Invite, account: Account) { + inviteStore.set(invite, for: account.absoluteString) } - func getInviteTopic(id: Int64) -> String? { - return getInvites().first(where: { $0.id == id })?.topic + func getInviteTopic(id: Int64, account: Account) -> String? { + return getInvites(account: account).first(where: { $0.id == id })?.topic } - func getInvites() -> [Invite] { - return inviteStore.getElements(for: accountKey) + func getInvites(account: Account) -> [Invite] { + return inviteStore.getElements(for: account.absoluteString) } - func delete(invite: Invite) { - inviteStore.delete(invite, for: accountKey) + func delete(invite: Invite, account: Account) { + inviteStore.delete(invite, for: account.absoluteString) } // MARK: - Threads - func getThreads() -> [Thread] { - return threadStore.getElements(for: accountKey) + func getThreads(account: Account) -> [Thread] { + return threadStore.getElements(for: account.absoluteString) } - func getThread(topic: String) -> Thread? { - return getThreads().first(where: { $0.topic == topic }) - } - - func set(thread: Thread) { - threadStore.set(thread, for: accountKey) + func set(thread: Thread, account: Account) { + threadStore.set(thread, for: account.absoluteString) } // MARK: - Messages - func set(message: Message) { - messageStore.set(message, for: accountKey) - } - - func getMessages() -> [Message] { - return messageStore.getElements(for: accountKey) + func set(message: Message, account: Account) { + messageStore.set(message, for: account.absoluteString) } - func getMessages(topic: String) -> [Message] { - return messageStore.getElements(for: accountKey).filter { $0.topic == topic } + func getMessages(account: Account) -> [Message] { + return messageStore.getElements(for: account.absoluteString) } -} - -private extension ChatStorage { - var accountKey: String { - return accountService.currentAccount.absoluteString + func getMessages(topic: String, account: Account) -> [Message] { + return messageStore.getElements(for: account.absoluteString).filter { $0.topic == topic } } } diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index df0e96ada..323fe66ba 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -9,15 +9,22 @@ class MessagingService { var onMessage: ((Message) -> Void)? private let networkingInteractor: NetworkInteracting + private let accountService: AccountService private let chatStorage: ChatStorage private let logger: ConsoleLogging private var publishers = [AnyCancellable]() + private var currentAccount: Account { + return accountService.currentAccount + } + init(networkingInteractor: NetworkInteracting, + accountService: AccountService, chatStorage: ChatStorage, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.accountService = accountService self.chatStorage = chatStorage self.logger = logger setUpResponseHandling() @@ -25,19 +32,14 @@ class MessagingService { } func send(topic: String, messageString: String) async throws { - // TODO - manage author account - - guard let authorAccount = chatStorage.getThread(topic: topic)?.selfAccount - else { throw Errors.threadDoNotExist} - let timestamp = Int64(Date().timeIntervalSince1970 * 1000) - let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp) + let message = Message(topic: topic, message: messageString, authorAccount: currentAccount, timestamp: timestamp) let protocolMethod = ChatMessageProtocolMethod() let request = RPCRequest(method: protocolMethod.method, params: message) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) - chatStorage.set(message: message) + chatStorage.set(message: message, account: currentAccount) onMessage?(message) } @@ -64,7 +66,7 @@ class MessagingService { protocolMethod: ChatMessageProtocolMethod() ) logger.debug("Received message") - chatStorage.set(message: message) + chatStorage.set(message: message, account: currentAccount) onMessage?(message) } } diff --git a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift index 0cb40299f..754ceedfc 100644 --- a/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift +++ b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift @@ -3,14 +3,17 @@ import Combine class ResubscriptionService { private let networkingInteractor: NetworkInteracting + private let accountService: AccountService private let logger: ConsoleLogging private var chatStorage: ChatStorage private var publishers = [AnyCancellable]() init(networkingInteractor: NetworkInteracting, + accountService: AccountService, chatStorage: ChatStorage, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.accountService = accountService self.logger = logger self.chatStorage = chatStorage setUpResubscription() @@ -22,9 +25,18 @@ class ResubscriptionService { guard status == .connected else { return } Task(priority: .high) { - let topics = chatStorage.getThreads().map { $0.topic } - try await networkingInteractor.batchSubscribe(topics: topics) + try await resubscribe(account: accountService.currentAccount) } }.store(in: &publishers) } + + func resubscribe(account: Account) async throws { + let topics = chatStorage.getThreads(account: account).map { $0.topic } + try await networkingInteractor.batchSubscribe(topics: topics) + } + + func unsubscribe(account: Account) async throws { + let topics = chatStorage.getThreads(account: account).map { $0.topic } + try await networkingInteractor.batchUnsubscribe(topics: topics) + } } diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index e42a09827..27f7b14c0 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -9,14 +9,20 @@ class InvitationHandlingService { var onNewThread: ((Thread) -> Void)? private let networkingInteractor: NetworkInteracting private let chatStorage: ChatStorage + private let accountService: AccountService private let topicToRegistryRecordStore: CodableStore private let registry: Registry private let logger: ConsoleLogging private let kms: KeyManagementService private var publishers = [AnyCancellable]() + private var currentAccount: Account { + return accountService.currentAccount + } + init(registry: Registry, networkingInteractor: NetworkInteracting, + accountService: AccountService, kms: KeyManagementService, logger: ConsoleLogging, topicToRegistryRecordStore: CodableStore, @@ -24,6 +30,7 @@ class InvitationHandlingService { self.registry = registry self.kms = kms self.networkingInteractor = networkingInteractor + self.accountService = accountService self.logger = logger self.topicToRegistryRecordStore = topicToRegistryRecordStore self.chatStorage = chatStorage @@ -32,8 +39,8 @@ class InvitationHandlingService { func accept(inviteId: Int64) async throws { guard - let invite = chatStorage.getInvite(id: inviteId), - let inviteTopic = chatStorage.getInviteTopic(id: inviteId) + let invite = chatStorage.getInvite(id: inviteId, account: currentAccount), + let inviteTopic = chatStorage.getInviteTopic(id: inviteId, account: currentAccount) else { throw Error.inviteForIdNotFound } let selfThreadPubKey = try kms.createX25519KeyPair() @@ -69,16 +76,16 @@ class InvitationHandlingService { peerAccount: invite.account ) - chatStorage.set(thread: thread) - chatStorage.delete(invite: invite) + chatStorage.set(thread: thread, account: currentAccount) + chatStorage.delete(invite: invite, account: currentAccount) onNewThread?(thread) } func reject(inviteId: Int64) async throws { guard - let invite = chatStorage.getInvite(id: inviteId), - let inviteTopic = chatStorage.getInviteTopic(id: inviteId) + let invite = chatStorage.getInvite(id: inviteId, account: currentAccount), + let inviteTopic = chatStorage.getInviteTopic(id: inviteId, account: currentAccount) else { throw Error.inviteForIdNotFound } let responseTopic = try getInviteResponseTopic(requestTopic: inviteTopic, invite: invite) @@ -90,7 +97,7 @@ class InvitationHandlingService { reason: ChatError.userRejected ) - chatStorage.delete(invite: invite) + chatStorage.delete(invite: invite, account: currentAccount) } private func setUpRequestHandling() { @@ -102,7 +109,7 @@ class InvitationHandlingService { topic: payload.topic, payload: payload.request ) - chatStorage.set(invite: invite) + chatStorage.set(invite: invite, account: currentAccount) onInvite?(invite) }.store(in: &publishers) } diff --git a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift index 8d990a2cb..47f34e155 100644 --- a/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/RegistryService.swift @@ -3,6 +3,7 @@ import Foundation actor RegistryService { private let networkingInteractor: NetworkInteracting private let accountService: AccountService + private let resubscriptionService: ResubscriptionService private let topicToRegistryRecordStore: CodableStore private let registry: Registry private let logger: ConsoleLogging @@ -10,6 +11,7 @@ actor RegistryService { init(registry: Registry, accountService: AccountService, + resubscriptionService: ResubscriptionService, networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, logger: ConsoleLogging, @@ -17,6 +19,7 @@ actor RegistryService { self.registry = registry self.kms = kms self.accountService = accountService + self.resubscriptionService = resubscriptionService self.networkingInteractor = networkingInteractor self.logger = logger self.topicToRegistryRecordStore = topicToRegistryRecordStore @@ -26,13 +29,22 @@ actor RegistryService { let pubKey = try kms.createX25519KeyPair() let pubKeyHex = pubKey.hexRepresentation try await registry.register(account: account, pubKey: pubKeyHex) + let topic = pubKey.rawRepresentation.sha256().toHexString() try kms.setPublicKey(publicKey: pubKey, for: topic) + let record = RegistryRecord(account: account, pubKey: pubKeyHex) topicToRegistryRecordStore.set(record, forKey: topic) + try await networkingInteractor.subscribe(topic: topic) + + let oldAccount = accountService.currentAccount + try await resubscriptionService.unsubscribe(account: oldAccount) accountService.setAccount(account) + try await resubscriptionService.resubscribe(account: account) + logger.debug("Did register an account: \(account) and is subscribing on topic: \(topic)") + return pubKeyHex } } diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index 421ce8193..8a357fba6 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -44,11 +44,8 @@ class InviteService { // overrides on invite toipic try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic) - let request = RPCRequest(method: protocolMethod.method, params: InvitePayload( - message: openingMessage, - account: accountService.currentAccount, - publicKey: selfPubKeyY.hexRepresentation - )) + let invite = InvitePayload(message: openingMessage, account: accountService.currentAccount, publicKey: selfPubKeyY.hexRepresentation) + let request = RPCRequest(method: protocolMethod.method, params: invite) // 2. Proposer subscribes to topic R which is the hash of the derived symKey let responseTopic = symKeyI.derivedTopic() @@ -91,7 +88,7 @@ class InviteService { peerAccount: peerAccount ) - chatStorage.set(thread: thread) + chatStorage.set(thread: thread, account: accountService.currentAccount) onNewThread?(thread) // TODO - remove symKeyI diff --git a/Tests/ChatTests/RegistryManagerTests.swift b/Tests/ChatTests/RegistryManagerTests.swift index ec1fcfa2c..ec9308be5 100644 --- a/Tests/ChatTests/RegistryManagerTests.swift +++ b/Tests/ChatTests/RegistryManagerTests.swift @@ -13,9 +13,13 @@ final class RegistryManagerTests: XCTestCase { var registry: Registry! var accountService: AccountService! var kms: KeyManagementServiceMock! + var resubscriptionService: ResubscriptionService! + var chatStorage: ChatStorage! + var threadStore: KeyedDatabase! let initialAccount = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! let newAccount = Account("eip155:2:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! + let mockAccount = Account("eip155:3:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")! override func setUp() { registry = KeyValueRegistry() @@ -23,24 +27,48 @@ final class RegistryManagerTests: XCTestCase { kms = KeyManagementServiceMock() topicToRegistryRecordStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") accountService = AccountService(currentAccount: initialAccount) + threadStore = KeyedDatabase(storage: RuntimeKeyValueStorage(), identifier: "") + chatStorage = ChatStorage( + messageStore: .init(storage: RuntimeKeyValueStorage(), identifier: ""), + inviteStore: .init(storage: RuntimeKeyValueStorage(), identifier: ""), + threadStore: threadStore + ) + resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, accountService: accountService, chatStorage: chatStorage, logger: ConsoleLoggerMock()) registryManager = RegistryService( registry: registry, accountService: accountService, + resubscriptionService: resubscriptionService, networkingInteractor: networkingInteractor, kms: kms, logger: ConsoleLoggerMock(), topicToRegistryRecordStore: topicToRegistryRecordStore) } - func testRegister() async { + func testRegister() async throws { + threadStore.set(Thread(topic: "topic1", selfAccount: mockAccount, peerAccount: mockAccount), for: newAccount.absoluteString) + threadStore.set(Thread(topic: "topic2", selfAccount: mockAccount, peerAccount: mockAccount), for: newAccount.absoluteString) + + // Test accountService initial state XCTAssertEqual(accountService.currentAccount, initialAccount) - _ = try! await registryManager.register(account: newAccount) + let pubKey = try await registryManager.register(account: newAccount) + + // Test subscription for invite topic XCTAssert(!networkingInteractor.subscriptions.isEmpty, "networkingInteractors subscribes to new topic") - let resolved = try! await registry.resolve(account: newAccount) - XCTAssertNotNil(resolved, "register account is resolvable") + // Test resubscription for threads + XCTAssertTrue(networkingInteractor.subscriptions.contains("topic1")) + XCTAssertTrue(networkingInteractor.subscriptions.contains("topic2")) + + let resolved = try await registry.resolve(account: newAccount) + + // Test resolved account pubKey + XCTAssertEqual(pubKey, resolved) + + // Test topicToRegistryRecordStore filled XCTAssertFalse(topicToRegistryRecordStore.getAll().isEmpty, "stores topic to invitation") + + // Test current account changed XCTAssertEqual(accountService.currentAccount, newAccount) } } From 42a10668c900f4915067471767d932f85cce9be5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 27 Jan 2023 13:42:46 +0530 Subject: [PATCH 57/74] [README] Bloken URL replaced --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d7724421b..9d0543d5f 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ open `Example/ExampleApp.xcodeproj` ## Web3Wallet Web3Wallet SDK introduces a new interface for all wallets that wraps the Sign and Auth clients internally. -- [Migration guide from Sign and Auth to Web3Wallet](https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/swift/guides/web3wallet-migration.md) +- [Migration guide from Sign and Auth to Web3Wallet](https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/swift/web3wallet/upgrade-guide.md) ## License From bf11b9d71c7cef300ce95455ea3ca5795ed3de72 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 27 Jan 2023 14:14:34 +0530 Subject: [PATCH 58/74] extra completion removed from unsubscribe --- Sources/WalletConnectRelay/RelayClient.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 59b2fb4a7..104c92d69 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -261,7 +261,6 @@ public final class RelayClient { self?.concurrentQueue.async(flags: .barrier) { self?.subscriptions[topic] = nil } - completion(nil) } } } From ecf5a958b582397b8a41c6fc7834a8b8d3863996 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 27 Jan 2023 10:20:54 +0100 Subject: [PATCH 59/74] add push registerer --- Example/ExampleApp.xcodeproj/project.pbxproj | 4 ++ .../ApplicationLayer/AppDelegate.swift | 17 ++++++++ .../ApplicationLayer/PushRegisterer.swift | 40 +++++++++++++++++++ .../ApplicationLayer/SceneDelegate.swift | 2 + Example/WalletApp/Common/InputConfig.swift | 7 ++++ Example/WalletApp/Other/Info.plist | 2 + Example/WalletApp/WalletApp.entitlements | 2 + .../WalletApp/WalletAppRelease.entitlements | 2 + .../WalletConnectEcho/EchoClientFactory.swift | 2 +- 9 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Example/WalletApp/ApplicationLayer/PushRegisterer.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 7abe9e3a0..1c486bd81 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; }; 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; + 84DB38F32983CDAE00BFEE37 /* PushRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */; }; 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B84929787A8000428BAF /* NotificationService.swift */; }; 84E6B84E29787A8000428BAF /* PNDecryptionService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -389,6 +390,7 @@ 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletApp.entitlements; sourceTree = ""; }; 84DB38F129828A7F00BFEE37 /* PNDecryptionService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionService.entitlements; sourceTree = ""; }; + 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRegisterer.swift; sourceTree = ""; }; 84E6B84729787A8000428BAF /* PNDecryptionService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PNDecryptionService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 84E6B84929787A8000428BAF /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 84E6B84B29787A8000428BAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1571,6 +1573,7 @@ C56EE280293F5757004840D1 /* Application.swift */, C56EE27F293F5757004840D1 /* AppDelegate.swift */, C56EE281293F5757004840D1 /* SceneDelegate.swift */, + 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */, ); path = ApplicationLayer; sourceTree = ""; @@ -2225,6 +2228,7 @@ C55D3489295DD8CA0004314A /* PasteUriModule.swift in Sources */, C55D3494295DFA750004314A /* WelcomePresenter.swift in Sources */, C5B2F6F929705293000DBA0E /* SessionRequestPresenter.swift in Sources */, + 84DB38F32983CDAE00BFEE37 /* PushRegisterer.swift in Sources */, C5B2F6FB297055B0000DBA0E /* ETHSigner.swift in Sources */, C56EE274293F56D7004840D1 /* SceneViewController.swift in Sources */, C55D3496295DFA750004314A /* WelcomeInteractor.swift in Sources */, diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift index e89ffe33d..b0fc7ed30 100644 --- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift @@ -19,4 +19,21 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} + + func application( + _ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data + ) { + Task(priority: .high) { + try await Push.wallet.register(deviceToken: deviceToken) + } + } + + func application( + _ application: UIApplication, + didFailToRegisterForRemoteNotificationsWithError error: Error + ) { + print("Failed to register: \(error)") + } + } diff --git a/Example/WalletApp/ApplicationLayer/PushRegisterer.swift b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift new file mode 100644 index 000000000..1f5f19162 --- /dev/null +++ b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift @@ -0,0 +1,40 @@ + +import WalletConnectPush +import Combine +import UIKit + +class PushRegisterer { + + private var publishers = [AnyCancellable]() + + func getNotificationSettings() { + UNUserNotificationCenter.current().getNotificationSettings { settings in + print("Notification settings: \(settings)") + guard settings.authorizationStatus == .authorized else { return } + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } + } + + func registerForPushNotifications() { + UNUserNotificationCenter.current() + .requestAuthorization( + options: [.alert, .sound, .badge]) { [weak self] granted, _ in + print("Permission granted: \(granted)") + guard granted else { return } + self?.getNotificationSettings() +#if targetEnvironment(simulator) + Networking.interactor.socketConnectionStatusPublisher + .first {$0 == .connected} + .sink{ status in + let deviceToken = InputConfig.simulatorIdentifier + assert(deviceToken != "SIMULATOR_IDENTIFIER", "Please set your Simulator identifier") + Task(priority: .high) { + try await Push.wallet.register(deviceToken: deviceToken) + } + }.store(in: &self!.publishers) +#endif + } + } +} diff --git a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift index 1c5b083e6..114dfeee4 100644 --- a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift @@ -8,6 +8,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { private let app = Application() + private let pushRegisterer = PushRegisterer() private var configurators: [Configurator] { return [ @@ -33,6 +34,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { app.uri = connectionOptions.urlContexts.first?.url.absoluteString.replacingOccurrences(of: "walletapp://wc?uri=", with: "") configurators.configure() + pushRegisterer.registerForPushNotifications() } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { diff --git a/Example/WalletApp/Common/InputConfig.swift b/Example/WalletApp/Common/InputConfig.swift index 1a4e505cf..4cdd5ee73 100644 --- a/Example/WalletApp/Common/InputConfig.swift +++ b/Example/WalletApp/Common/InputConfig.swift @@ -5,7 +5,14 @@ struct InputConfig { return config(for: "PROJECT_ID")! } +#if targetEnvironment(simulator) + static var simulatorIdentifier: String { + return config(for: "SIMULATOR_IDENTIFIER")! + } +#endif + private static func config(for key: String) -> String? { return Bundle.main.object(forInfoDictionaryKey: key) as? String } + } diff --git a/Example/WalletApp/Other/Info.plist b/Example/WalletApp/Other/Info.plist index ef9e6d46f..ef4c6367a 100644 --- a/Example/WalletApp/Other/Info.plist +++ b/Example/WalletApp/Other/Info.plist @@ -43,5 +43,7 @@ + SIMULATOR_IDENTIFIER + $(SIMULATOR_IDENTIFIER) diff --git a/Example/WalletApp/WalletApp.entitlements b/Example/WalletApp/WalletApp.entitlements index 0bd7a7310..2dccdcca9 100644 --- a/Example/WalletApp/WalletApp.entitlements +++ b/Example/WalletApp/WalletApp.entitlements @@ -2,6 +2,8 @@ + aps-environment + development com.apple.security.application-groups group.com.walletconnect.sdk diff --git a/Example/WalletApp/WalletAppRelease.entitlements b/Example/WalletApp/WalletAppRelease.entitlements index 0bd7a7310..2dccdcca9 100644 --- a/Example/WalletApp/WalletAppRelease.entitlements +++ b/Example/WalletApp/WalletAppRelease.entitlements @@ -2,6 +2,8 @@ + aps-environment + development com.apple.security.application-groups group.com.walletconnect.sdk diff --git a/Sources/WalletConnectEcho/EchoClientFactory.swift b/Sources/WalletConnectEcho/EchoClientFactory.swift index ff7b8cbb6..f1515d5a8 100644 --- a/Sources/WalletConnectEcho/EchoClientFactory.swift +++ b/Sources/WalletConnectEcho/EchoClientFactory.swift @@ -16,7 +16,7 @@ public struct EchoClientFactory { clientId: String, httpClient: HTTPClient) -> EchoClient { - let logger = ConsoleLogger(loggingLevel: .off) + let logger = ConsoleLogger(loggingLevel: .debug) let registerService = EchoRegisterService(httpClient: httpClient, projectId: projectId, clientId: clientId, logger: logger) return EchoClient( From 13c117d4baf92e1fb4c3fb16eeadedb547a30c9e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 27 Jan 2023 11:08:48 +0100 Subject: [PATCH 60/74] savepoint --- Example/WalletApp/ApplicationLayer/AppDelegate.swift | 5 +++++ Example/WalletApp/WalletAppRelease.entitlements | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift index b0fc7ed30..25fa75d10 100644 --- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift @@ -25,6 +25,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { Task(priority: .high) { + // Use pasteboard for testing purposes + let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } + let token = tokenParts.joined() + let pasteboard = UIPasteboard.general + pasteboard.string = token try await Push.wallet.register(deviceToken: deviceToken) } } diff --git a/Example/WalletApp/WalletAppRelease.entitlements b/Example/WalletApp/WalletAppRelease.entitlements index 2dccdcca9..315a4dbfc 100644 --- a/Example/WalletApp/WalletAppRelease.entitlements +++ b/Example/WalletApp/WalletAppRelease.entitlements @@ -3,7 +3,7 @@ aps-environment - development + production com.apple.security.application-groups group.com.walletconnect.sdk From 232babee1691af6c64ffe3001e8f8d08eb8cf1b5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 30 Jan 2023 12:30:09 +0530 Subject: [PATCH 61/74] Multiaccount demo --- .../ApplicationConfigurator.swift | 2 +- .../Chat/ChatList/ChatListPresenter.swift | 2 +- .../Chat/ChatList/ChatListRouter.swift | 4 ++-- .../Chat/ChatList/ChatListView.swift | 1 + .../Chat/Import/ImportRouter.swift | 4 +--- .../Chat/Main/MainModule.swift | 4 ++-- .../Chat/Main/MainPresenter.swift | 8 ++++--- .../Chat/Main/MainRouter.swift | 8 +++---- .../Chat/Welcome/WelcomePresenter.swift | 24 +------------------ .../Chat/Welcome/WelcomeRouter.swift | 5 ++-- .../Web3Inbox/Web3InboxModule.swift | 4 ++-- .../Web3Inbox/Web3InboxViewController.swift | 21 ++++++++++++---- 12 files changed, 39 insertions(+), 48 deletions(-) diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift index a50e9c6e6..810260eef 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ApplicationConfigurator.swift @@ -11,6 +11,6 @@ struct ApplicationConfigurator: Configurator { } func configure() { - MainModule.create(app: app).present() + WelcomeModule.create(app: app).present() } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift index a947cdeba..0f7a0b412 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListPresenter.swift @@ -44,7 +44,7 @@ final class ChatListPresenter: ObservableObject { func didLogoutPress() { interactor.logout() - router.presentMain() + router.presentWelcome() } func didPressNewChat() { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift index eb0564d37..94210189e 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift @@ -25,7 +25,7 @@ final class ChatListRouter { ChatModule.create(thread: thread, app: app).push(from: viewController) } - func presentMain() { - MainModule.create(app: app).present() + func presentWelcome() { + WelcomeModule.create(app: app).present() } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift index 11a6173de..72b5f62f7 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListView.swift @@ -47,6 +47,7 @@ struct ChatListView: View { presenter.didLogoutPress() } .foregroundColor(.red) + .padding(.bottom, 16) } .onAppear { presenter.setupInitialState() diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift index 0902e7251..9b8b2ac8f 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift @@ -11,8 +11,6 @@ final class ImportRouter { } func presentChat(account: Account) { - ChatListModule.create(app: app, account: account) - .wrapToNavigationController() - .present() + MainModule.create(app: app, account: account).present() } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift index f60ff5d77..fd6309823 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainModule.swift @@ -3,9 +3,9 @@ import SwiftUI final class MainModule { @discardableResult - static func create(app: Application) -> UIViewController { + static func create(app: Application, account: Account) -> UIViewController { let router = MainRouter(app: app) - let presenter = MainPresenter(router: router) + let presenter = MainPresenter(router: router, account: account) let viewController = MainViewController(presenter: presenter) router.viewController = viewController diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift index fe2f68269..10d1de36f 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainPresenter.swift @@ -3,6 +3,7 @@ import Combine final class MainPresenter { + private let account: Account private let router: MainRouter var tabs: [TabPage] { @@ -11,12 +12,13 @@ final class MainPresenter { var viewControllers: [UIViewController] { return [ - router.chatViewController, - router.web3InboxViewController, + router.chatViewController(account: account), + router.web3InboxViewController(account: account), ] } - init(router: MainRouter) { + init(router: MainRouter, account: Account) { + self.account = account self.router = router } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift index b6bb372b1..9072fcdf1 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Main/MainRouter.swift @@ -6,12 +6,12 @@ final class MainRouter { private let app: Application - var chatViewController: UIViewController { - return WelcomeModule.create(app: app) + func chatViewController(account: Account) -> UIViewController { + return ChatListModule.create(app: app, account: account).wrapToNavigationController() } - var web3InboxViewController: UIViewController { - return Web3InboxModule.create(app: app) + func web3InboxViewController(account: Account) -> UIViewController { + return Web3InboxModule.create(app: app, account: account).wrapToNavigationController() } init(app: Application) { diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift index 08565843e..dd9d00f1f 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomePresenter.swift @@ -28,31 +28,9 @@ final class WelcomePresenter: ObservableObject { func didPressImport() { if let account = interactor.account { - router.presentChats(account: account) + router.presentMain(account: account) } else { router.presentImport() } } - - private func authWithWallet() async { - let uri = await interactor.generateUri() - try? await Auth.instance.request( - RequestParams( - domain: "example.wallet", - chainId: "eip155:1", - nonce: "32891756", - aud: "https://example.wallet/login", - nbf: nil, - exp: nil, - statement: "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", - requestId: nil, - resources: ["ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json"] - ), - topic: uri.topic - ) - - DispatchQueue.main.async { - self.router.openWallet(uri: uri.absoluteString) - } - } } diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift index e69faac65..b6ff4209d 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Welcome/WelcomeRouter.swift @@ -16,9 +16,8 @@ final class WelcomeRouter { .present() } - func presentChats(account: Account) { - ChatListModule.create(app: app, account: account) - .wrapToNavigationController() + func presentMain(account: Account) { + MainModule.create(app: app, account: account) .present() } diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift index ad0633441..fe2242603 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxModule.swift @@ -3,9 +3,9 @@ import SwiftUI final class Web3InboxModule { @discardableResult - static func create(app: Application) -> UIViewController { + static func create(app: Application, account: Account) -> UIViewController { let router = Web3InboxRouter(app: app) - let viewController = Web3InboxViewController() + let viewController = Web3InboxViewController(account: account) router.viewController = viewController return viewController } diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index df78b24cb..7f5d62ab3 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -4,10 +4,23 @@ import WebKit final class Web3InboxViewController: UIViewController { - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) + private let account: Account - Web3Inbox.configure(account: Account("eip155:1:0x2F871A068a16BF4a970663dF5e417951aB79Bfd3")!) - self.view = Web3Inbox.instance.getWebView() + init(account: Account) { + self.account = account + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + Web3Inbox.configure(account: account) + view = Web3Inbox.instance.getWebView() + + navigationItem.title = "Web3Inbox SDK" } } From aab7112884ea281c0ad35634f787bbde630531f2 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 30 Jan 2023 11:29:40 +0100 Subject: [PATCH 62/74] apply review suggestions --- Example/PNDecryptionService/NotificationService.swift | 6 +++--- Example/WalletApp/ApplicationLayer/Application.swift | 1 + Example/WalletApp/ApplicationLayer/SceneDelegate.swift | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Example/PNDecryptionService/NotificationService.swift b/Example/PNDecryptionService/NotificationService.swift index a818245f4..26d9c8960 100644 --- a/Example/PNDecryptionService/NotificationService.swift +++ b/Example/PNDecryptionService/NotificationService.swift @@ -10,9 +10,9 @@ class NotificationService: UNNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - if let bestAttemptContent = bestAttemptContent { - let topic = bestAttemptContent.userInfo["topic"] as! String - let ciphertext = bestAttemptContent.userInfo["blob"] as! String + if let bestAttemptContent = bestAttemptContent, + let topic = bestAttemptContent.userInfo["topic"] as? String, + let ciphertext = bestAttemptContent.userInfo["blob"] as? String { do { let service = PushDecryptionService() let pushMessage = try service.decryptMessage(topic: topic, ciphertext: ciphertext) diff --git a/Example/WalletApp/ApplicationLayer/Application.swift b/Example/WalletApp/ApplicationLayer/Application.swift index 97fc2aaf6..91cd85b90 100644 --- a/Example/WalletApp/ApplicationLayer/Application.swift +++ b/Example/WalletApp/ApplicationLayer/Application.swift @@ -3,4 +3,5 @@ import WalletConnectChat final class Application { var uri: String? + let pushRegisterer = PushRegisterer() } diff --git a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift index 114dfeee4..999164f85 100644 --- a/Example/WalletApp/ApplicationLayer/SceneDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/SceneDelegate.swift @@ -8,7 +8,6 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { private let app = Application() - private let pushRegisterer = PushRegisterer() private var configurators: [Configurator] { return [ @@ -34,7 +33,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { app.uri = connectionOptions.urlContexts.first?.url.absoluteString.replacingOccurrences(of: "walletapp://wc?uri=", with: "") configurators.configure() - pushRegisterer.registerForPushNotifications() + app.pushRegisterer.registerForPushNotifications() } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { From c218a67b140f98b01acfaa2f321bfbe65948646f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 30 Jan 2023 12:43:43 +0100 Subject: [PATCH 63/74] Update approve engine cache --- .../Engine/Common/ApproveEngine.swift | 31 ++++++++----------- .../Services/SignCleanupService.swift | 5 +-- Sources/WalletConnectSign/Session.swift | 2 +- .../Sign/SignClientFactory.swift | 6 ++-- .../SignStorageIdentifiers.swift | 2 +- .../Types/SignReasonCode.swift | 7 +++++ .../AppProposalServiceTests.swift | 4 +-- .../ApproveEngineTests.swift | 9 +++--- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index accf087b2..90333da1b 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -15,13 +15,11 @@ final class ApproveEngine { var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionSettle: ((Session) -> Void)? - var settlingProposal: SessionProposal? - private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage private let proposalPayloadsStore: CodableStore> - private let sessionToPairingTopic: CodableStore + private var sessionTopicToProposal: CodableStore private let pairingRegisterer: PairingRegisterer private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol @@ -32,7 +30,7 @@ final class ApproveEngine { init( networkingInteractor: NetworkInteracting, proposalPayloadsStore: CodableStore>, - sessionToPairingTopic: CodableStore, + sessionTopicToProposal: CodableStore, pairingRegisterer: PairingRegisterer, metadata: AppMetadata, kms: KeyManagementServiceProtocol, @@ -42,7 +40,7 @@ final class ApproveEngine { ) { self.networkingInteractor = networkingInteractor self.proposalPayloadsStore = proposalPayloadsStore - self.sessionToPairingTopic = sessionToPairingTopic + self.sessionTopicToProposal = sessionTopicToProposal self.pairingRegisterer = pairingRegisterer self.metadata = metadata self.kms = kms @@ -216,10 +214,9 @@ private extension ApproveEngine { logger.debug("Received Session Proposal response") try kms.setAgreementSecret(agreementKeys, topic: sessionTopic) - sessionToPairingTopic.set(payload.topic, forKey: sessionTopic) - - settlingProposal = payload.request + let proposal = payload.request.publicRepresentation(pairingTopic: payload.topic) + sessionTopicToProposal.set(proposal, forKey: sessionTopic) Task(priority: .high) { try await networkingInteractor.subscribe(topic: sessionTopic) } @@ -290,11 +287,13 @@ private extension ApproveEngine { let protocolMethod = SessionSettleProtocolMethod() - guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { - return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) - } + let sessionTopic = payload.topic - settlingProposal = nil + guard let proposal = try? sessionTopicToProposal.get(key: sessionTopic) else { + return respondError(payload: payload, reason: .sessionSettlementFailed, protocolMethod: protocolMethod) + } + let pairingTopic = proposal.pairingTopic + let proposedNamespaces = proposal.requiredNamespaces let params = payload.request let sessionNamespaces = params.namespaces @@ -308,23 +307,19 @@ private extension ApproveEngine { return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) } - let topic = payload.topic - let agreementKeys = kms.getAgreementSecret(for: topic)! + let agreementKeys = kms.getAgreementSecret(for: sessionTopic)! let selfParticipant = Participant( publicKey: agreementKeys.publicKey.hexRepresentation, metadata: metadata ) - guard let pairingTopic = try? sessionToPairingTopic.get(key: topic) else { - return - } pairingRegisterer.activate( pairingTopic: pairingTopic, peerMetadata: params.controller.metadata ) let session = WCSession( - topic: topic, + topic: sessionTopic, pairingTopic: pairingTopic, timestamp: Date(), selfParticipant: selfParticipant, diff --git a/Sources/WalletConnectSign/Services/SignCleanupService.swift b/Sources/WalletConnectSign/Services/SignCleanupService.swift index 1da2aee3e..34d37a23e 100644 --- a/Sources/WalletConnectSign/Services/SignCleanupService.swift +++ b/Sources/WalletConnectSign/Services/SignCleanupService.swift @@ -5,13 +5,11 @@ final class SignCleanupService { private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage private let kms: KeyManagementServiceProtocol - private let sessionToPairingTopic: CodableStore private let networkInteractor: NetworkInteracting - init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore, networkInteractor: NetworkInteracting) { + init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, networkInteractor: NetworkInteracting) { self.pairingStore = pairingStore self.sessionStore = sessionStore - self.sessionToPairingTopic = sessionToPairingTopic self.networkInteractor = networkInteractor self.kms = kms } @@ -38,7 +36,6 @@ private extension SignCleanupService { func cleanupStorages() throws { pairingStore.deleteAll() sessionStore.deleteAll() - sessionToPairingTopic.deleteAll() try kms.deleteAll() } } diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift index 225c63432..8c85f4d6a 100644 --- a/Sources/WalletConnectSign/Session.swift +++ b/Sources/WalletConnectSign/Session.swift @@ -16,7 +16,7 @@ public struct Session { extension Session { - public struct Proposal: Equatable { + public struct Proposal: Equatable, Codable { public var id: String public let pairingTopic: String public let proposer: AppMetadata diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index ce27c1e4c..868853a6d 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -23,13 +23,13 @@ public struct SignClientFactory { let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.sessions.rawValue))) - let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionToPairingTopic.rawValue) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.proposals.rawValue) let sessionEngine = SessionEngine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) - let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) - let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic, networkInteractor: networkingClient) + let sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionTopicToProposal.rawValue) + let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) + let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, networkInteractor: networkingClient) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) diff --git a/Sources/WalletConnectSign/SignStorageIdentifiers.swift b/Sources/WalletConnectSign/SignStorageIdentifiers.swift index 4da79ec0e..8854dd4e8 100644 --- a/Sources/WalletConnectSign/SignStorageIdentifiers.swift +++ b/Sources/WalletConnectSign/SignStorageIdentifiers.swift @@ -4,5 +4,5 @@ enum SignStorageIdentifiers: String { case pairings = "com.walletconnect.sdk.pairingSequences" case sessions = "com.walletconnect.sdk.sessionSequences" case proposals = "com.walletconnect.sdk.sessionProposals" - case sessionToPairingTopic = "com.walletconnect.sdk.sessionToPairingTopic" + case sessionTopicToProposal = "com.walletconnect.sdk.sessionTopicToProposal" } diff --git a/Sources/WalletConnectSign/Types/SignReasonCode.swift b/Sources/WalletConnectSign/Types/SignReasonCode.swift index 522cbfb4f..683c37ee8 100644 --- a/Sources/WalletConnectSign/Types/SignReasonCode.swift +++ b/Sources/WalletConnectSign/Types/SignReasonCode.swift @@ -37,6 +37,9 @@ enum SignReasonCode: Reason, Codable, Equatable { // 6000 case userDisconnected + // 7000 + case sessionSettlementFailed + var code: Int { switch self { case .invalidMethod: return 1001 @@ -65,7 +68,9 @@ enum SignReasonCode: Reason, Codable, Equatable { case .userDisconnected: return 6000 + case .sessionSettlementFailed: return 7000 case .noSessionForTopic: return 7001 + } } @@ -117,6 +122,8 @@ enum SignReasonCode: Reason, Codable, Equatable { return "Unsupported namespace key" case .userDisconnected: return "User discconnected" + case .sessionSettlementFailed: + return "Session Settlement Failed" } } } diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift index f733358ea..d10924417 100644 --- a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift +++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift @@ -62,7 +62,7 @@ final class AppProposalServiceTests: XCTestCase { approveEngine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: .init(defaults: RuntimeKeyValueStorage(), identifier: ""), - sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), + sessionTopicToProposal: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), pairingRegisterer: pairingRegisterer, metadata: meta, kms: cryptoMock, @@ -117,7 +117,7 @@ final class AppProposalServiceTests: XCTestCase { networkingInteractor.responsePublisherSubject.send((topicA, request, response)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) - let storedPairing = storageMock.getPairing(forTopic: topicA)! + _ = storageMock.getPairing(forTopic: topicA)! wait(for: [exp], timeout: 5) diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 45fc2c1ea..8db9dd112 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -18,7 +18,7 @@ final class ApproveEngineTests: XCTestCase { var sessionStorageMock: WCSessionStorageMock! var pairingRegisterer: PairingRegistererMock! var proposalPayloadsStore: CodableStore>! - var sessionToPairingTopic: CodableStore! + var sessionTopicToProposal: CodableStore! var publishers = Set() @@ -30,11 +30,11 @@ final class ApproveEngineTests: XCTestCase { sessionStorageMock = WCSessionStorageMock() pairingRegisterer = PairingRegistererMock() proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "") - sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") + sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") engine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, - sessionToPairingTopic: sessionToPairingTopic, + sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingRegisterer, metadata: metadata, kms: cryptoMock, @@ -107,8 +107,7 @@ final class ApproveEngineTests: XCTestCase { engine.onSessionSettle = { _ in didCallBackOnSessionApproved = true } - sessionToPairingTopic.set("", forKey: sessionTopic) - engine.settlingProposal = SessionProposal.stub() + sessionTopicToProposal.set(SessionProposal.stub().publicRepresentation(pairingTopic: ""), forKey: sessionTopic) networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle())) usleep(100) From 217de83c6d96950b8318591da03e8b9b623e0fd4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 30 Jan 2023 12:46:04 +0100 Subject: [PATCH 64/74] update clean up service --- Sources/WalletConnectSign/Services/SignCleanupService.swift | 5 ++++- Sources/WalletConnectSign/Sign/SignClientFactory.swift | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectSign/Services/SignCleanupService.swift b/Sources/WalletConnectSign/Services/SignCleanupService.swift index 34d37a23e..abee34063 100644 --- a/Sources/WalletConnectSign/Services/SignCleanupService.swift +++ b/Sources/WalletConnectSign/Services/SignCleanupService.swift @@ -5,11 +5,13 @@ final class SignCleanupService { private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage private let kms: KeyManagementServiceProtocol + private let sessionTopicToProposal: CodableStore private let networkInteractor: NetworkInteracting - init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, networkInteractor: NetworkInteracting) { + init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionTopicToProposal: CodableStore, networkInteractor: NetworkInteracting) { self.pairingStore = pairingStore self.sessionStore = sessionStore + self.sessionTopicToProposal = sessionTopicToProposal self.networkInteractor = networkInteractor self.kms = kms } @@ -36,6 +38,7 @@ private extension SignCleanupService { func cleanupStorages() throws { pairingStore.deleteAll() sessionStore.deleteAll() + sessionTopicToProposal.deleteAll() try kms.deleteAll() } } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 868853a6d..9be0e50a8 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -29,7 +29,7 @@ public struct SignClientFactory { let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionTopicToProposal.rawValue) let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) - let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, networkInteractor: networkingClient) + let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionTopicToProposal: sessionTopicToProposal, networkInteractor: networkingClient) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) From 41f0b65bf6d2b69f712452dfed62dbb8e59ac4d5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 30 Jan 2023 12:50:11 +0100 Subject: [PATCH 65/74] fix after merge issue --- .../WCSessionTests.swift | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/Tests/WalletConnectSignTests/WCSessionTests.swift b/Tests/WalletConnectSignTests/WCSessionTests.swift index a10f4cf1d..097dd8883 100644 --- a/Tests/WalletConnectSignTests/WCSessionTests.swift +++ b/Tests/WalletConnectSignTests/WCSessionTests.swift @@ -148,67 +148,4 @@ final class WCSessionTests: XCTestCase { var session = WCSession.stub(requiredNamespaces: stubRequiredNamespaces()) XCTAssertThrowsError(try session.updateNamespaces(invalid)) } - - func testUpdateLessThanRequiredExtension() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: nil)] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionAccounts() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount], - methods: ["method-2", "newMethod-2"], - events: ["event-2", "newEvent-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionMethods() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount, polyAccount], - methods: ["method-2"], - events: ["event-2", "newEvent-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testUpdateLessThanRequiredExtensionEvents() { - let invalid = [ - "eip155": SessionNamespace( - accounts: [ethAccount, polyAccount], - methods: ["method", "method-2"], - events: ["event", "event-2"], - extensions: [ - SessionNamespace.Extension( - accounts: [ethAccount, polyAccount], - methods: ["method-2", "newMethod-2"], - events: ["event-2"])])] - var session = WCSession.stub(requiredNamespaces: stubRequiredNamespacesWithExtension()) - XCTAssertThrowsError(try session.updateNamespaces(invalid)) - } - - func testEncodeDecode() { - let session = WCSession.stub() - let encodedSession = try! JSONEncoder().encode(session) - let decodedSession = try! JSONDecoder().decode(WCSession.self, from: encodedSession) - XCTAssertEqual(session, decodedSession) - } } From 0b66343ccd4903934d6f380cc71be10a7b68b485 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 30 Jan 2023 12:52:52 +0100 Subject: [PATCH 66/74] savepoint --- Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 90333da1b..a00b62c00 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -19,7 +19,7 @@ final class ApproveEngine { private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage private let proposalPayloadsStore: CodableStore> - private var sessionTopicToProposal: CodableStore + private let sessionTopicToProposal: CodableStore private let pairingRegisterer: PairingRegisterer private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol From f44646705a4d26ee268d9ac717027c9d45e053d4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 31 Jan 2023 08:26:44 +0100 Subject: [PATCH 67/74] fix echo registry status --- Sources/WalletConnectEcho/EchoClientFactory.swift | 2 +- .../WalletConnectEcho/Register/EchoRegisterService.swift | 6 ++++-- Sources/WalletConnectEcho/Register/EcoResponse.swift | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectEcho/EchoClientFactory.swift b/Sources/WalletConnectEcho/EchoClientFactory.swift index f1515d5a8..ff7b8cbb6 100644 --- a/Sources/WalletConnectEcho/EchoClientFactory.swift +++ b/Sources/WalletConnectEcho/EchoClientFactory.swift @@ -16,7 +16,7 @@ public struct EchoClientFactory { clientId: String, httpClient: HTTPClient) -> EchoClient { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(loggingLevel: .off) let registerService = EchoRegisterService(httpClient: httpClient, projectId: projectId, clientId: clientId, logger: logger) return EchoClient( diff --git a/Sources/WalletConnectEcho/Register/EchoRegisterService.swift b/Sources/WalletConnectEcho/Register/EchoRegisterService.swift index c3e507af2..51fbd7560 100644 --- a/Sources/WalletConnectEcho/Register/EchoRegisterService.swift +++ b/Sources/WalletConnectEcho/Register/EchoRegisterService.swift @@ -33,9 +33,10 @@ actor EchoRegisterService { EchoResponse.self, at: EchoAPI.register(clientId: clientIdMutlibase, token: token, projectId: projectId) ) - guard response.status == .ok else { + guard response.status == .success else { throw Errors.registrationFailed } + logger.debug("Successfully registered at Echo Server") } #if DEBUG @@ -44,9 +45,10 @@ actor EchoRegisterService { EchoResponse.self, at: EchoAPI.register(clientId: clientIdMutlibase, token: deviceToken, projectId: projectId) ) - guard response.status == .ok else { + guard response.status == .success else { throw Errors.registrationFailed } + logger.debug("Successfully registered at Echo Server") } #endif } diff --git a/Sources/WalletConnectEcho/Register/EcoResponse.swift b/Sources/WalletConnectEcho/Register/EcoResponse.swift index 4c50053a0..f480c510c 100644 --- a/Sources/WalletConnectEcho/Register/EcoResponse.swift +++ b/Sources/WalletConnectEcho/Register/EcoResponse.swift @@ -2,7 +2,7 @@ import Foundation struct EchoResponse: Codable { enum Status: String, Codable { - case ok = "OK" + case success = "SUCCESS" case failed = "FAILED" } From a6dffbcd64aa57af4f15178687c88104e13a2988 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 27 Jan 2023 16:04:17 +0530 Subject: [PATCH 68/74] unit_tests lane --- .gitignore | 13 ++- .../xcshareddata/xcschemes/AuthTests.xcscheme | 52 ---------- .../WalletConnectUtilsTests.xcscheme | 52 ---------- .../xcschemes/PushDecryptionService.xcscheme | 97 +++++++++++++++++++ .../xcschemes/WalletConnect.xcscheme | 66 ++++++++----- .../xcschemes/WalletConnectAuth.xcscheme | 6 +- .../xcschemes/WalletConnectChat.xcscheme | 6 +- .../xcschemes/WalletConnectEcho.xcscheme | 37 ++++--- .../WalletConnectNetworking.xcscheme | 67 +++++++++++++ .../xcschemes/WalletConnectPairing.xcscheme | 67 +++++++++++++ .../xcschemes/WalletConnectPush.xcscheme | 37 ++++--- .../xcschemes/WalletConnectRouter.xcscheme | 37 ++++--- .../xcschemes/WalletConnectVerify.xcscheme | 4 +- .../xcshareddata/xcschemes/Web3Inbox.xcscheme | 35 +++++-- .../xcschemes/Web3Wallet.xcscheme | 77 +++++++++++++++ Makefile | 15 ++- .../Mocks/SignClientMock.swift | 5 +- fastlane/Fastfile | 41 ++++++++ 18 files changed, 527 insertions(+), 187 deletions(-) delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme create mode 100644 Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme rename {.swiftpm/xcode => Example/ExampleApp.xcodeproj}/xcshareddata/xcschemes/WalletConnect.xcscheme (84%) rename {.swiftpm/xcode => Example/ExampleApp.xcodeproj}/xcshareddata/xcschemes/WalletConnectAuth.xcscheme (94%) rename {.swiftpm/xcode => Example/ExampleApp.xcodeproj}/xcshareddata/xcschemes/WalletConnectChat.xcscheme (94%) rename .swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme => Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme (62%) create mode 100644 Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme create mode 100644 Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme rename .swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme => Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme (62%) rename .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme => Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme (61%) rename {.swiftpm/xcode => Example/ExampleApp.xcodeproj}/xcshareddata/xcschemes/WalletConnectVerify.xcscheme (95%) rename .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme => Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme (64%) create mode 100644 Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme create mode 100644 fastlane/Fastfile diff --git a/.gitignore b/.gitignore index dad29299f..31cddd747 100644 --- a/.gitignore +++ b/.gitignore @@ -14,12 +14,15 @@ Package.resolved /*.xcodeproj # Fastlane -*/fastlane/report.xml -*/fastlane/Preview.html -*/fastlane/screenshots/**/*.png -*/fastlane/test_output -*/fastlane/README.md +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output +fastlane/README.md # Configuration Configuration.xcconfig +# Cache +SourcePackagesCache +DerivedDataCache \ No newline at end of file diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme deleted file mode 100644 index b6a508a85..000000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/AuthTests.xcscheme +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme deleted file mode 100644 index 3cf5ef95b..000000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectUtilsTests.xcscheme +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme new file mode 100644 index 000000000..59e2134d6 --- /dev/null +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnect.xcscheme similarity index 84% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnect.xcscheme index ff9119893..4ab880a22 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnect.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "WalletConnect" BuildableName = "WalletConnect" BlueprintName = "WalletConnect" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> @@ -188,7 +188,7 @@ BlueprintIdentifier = "WalletConnect" BuildableName = "WalletConnect" BlueprintName = "WalletConnect" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -199,7 +199,7 @@ BlueprintIdentifier = "RelayerTests" BuildableName = "RelayerTests" BlueprintName = "RelayerTests" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + ReferencedContainer = "container:.."> + + + + + + + + @@ -307,7 +327,7 @@ BlueprintIdentifier = "WalletConnect" BuildableName = "WalletConnect" BlueprintName = "WalletConnect" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme similarity index 94% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme index 10b74b10a..3a5aff949 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectAuth.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "WalletConnectAuth" BuildableName = "WalletConnectAuth" BlueprintName = "WalletConnectAuth" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -35,7 +35,7 @@ BlueprintIdentifier = "AuthTests" BuildableName = "AuthTests" BlueprintName = "AuthTests" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -63,7 +63,7 @@ BlueprintIdentifier = "WalletConnectAuth" BuildableName = "WalletConnectAuth" BlueprintName = "WalletConnectAuth" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme similarity index 94% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme index c04003b78..ae2c82e99 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectChat.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectChat.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "WalletConnectChat" BuildableName = "WalletConnectChat" BlueprintName = "WalletConnectChat" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -35,7 +35,7 @@ BlueprintIdentifier = "ChatTests" BuildableName = "ChatTests" BlueprintName = "ChatTests" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -63,7 +63,7 @@ BlueprintIdentifier = "WalletConnectChat" BuildableName = "WalletConnectChat" BlueprintName = "WalletConnectChat" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme similarity index 62% rename from .swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme index a7cb31007..341890963 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/ChatTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectEcho.xcscheme @@ -1,10 +1,26 @@ + + + + + + - - - - + + + + diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme new file mode 100644 index 000000000..4d9ac47b7 --- /dev/null +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectNetworking.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme new file mode 100644 index 000000000..dc1c7488b --- /dev/null +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPairing.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme similarity index 62% rename from .swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme index a887ff162..48c7aad62 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/RelayerTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectPush.xcscheme @@ -1,10 +1,26 @@ + + + + + + - - - - + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme similarity index 61% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme index d88c50634..a7c2791a6 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectKMSTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectRouter.xcscheme @@ -1,10 +1,26 @@ + + + + + + - - - - + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme similarity index 95% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme index b5ef0e1c9..5b4aa4030 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectVerify.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "WalletConnectVerify" BuildableName = "WalletConnectVerify" BlueprintName = "WalletConnectVerify" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> @@ -53,7 +53,7 @@ BlueprintIdentifier = "WalletConnectVerify" BuildableName = "WalletConnectVerify" BlueprintName = "WalletConnectVerify" - ReferencedContainer = "container:"> + ReferencedContainer = "container:.."> diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme similarity index 64% rename from .swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme index 2b307dc3b..ae55238ff 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectSignTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Inbox.xcscheme @@ -5,6 +5,22 @@ + + + + + + - - - - + + + + diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme new file mode 100644 index 000000000..8776c0962 --- /dev/null +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Web3Wallet.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Makefile b/Makefile index 3ba7ee113..1bd579e75 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,20 @@ XCODE_USER_TEMPLATES_DIR=/Applications/Xcode.app/Contents/Developer/Library/Xcod TEMPLATE_NAME=VIPER TEMPLATES_DIR=Example/Templates/VIPER +EXISTS_FASTLANE = $(shell command -v fastlane 2> /dev/null) + install_templates: mkdir -p $(XCODE_USER_TEMPLATES_DIR) rm -fR $(XCODE_USER_TEMPLATES_DIR)/$(TEMPLATE_NAME) - cp -R $(TEMPLATES_DIR) $(XCODE_USER_TEMPLATES_DIR) \ No newline at end of file + cp -R $(TEMPLATES_DIR) $(XCODE_USER_TEMPLATES_DIR) + +install_env: +ifeq "${EXISTS_FASTLANE}" "" + @echo Installing fastlane + sudo gem install fastlane --no-document +endif + @echo "All dependencies was installed" + + +unit_tests: + fastlane unit_tests \ No newline at end of file diff --git a/Tests/Web3WalletTests/Mocks/SignClientMock.swift b/Tests/Web3WalletTests/Mocks/SignClientMock.swift index 72ad8f006..d112422ef 100644 --- a/Tests/Web3WalletTests/Mocks/SignClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/SignClientMock.swift @@ -16,7 +16,7 @@ final class SignClientMock: SignClientProtocol { var disconnectCalled = false private let metadata = AppMetadata(name: "", description: "", url: "", icons: []) - private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!) + private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!, expiry: nil) var sessionProposalPublisher: AnyPublisher { let proposer = Participant(publicKey: "", metadata: metadata) @@ -24,8 +24,7 @@ final class SignClientMock: SignClientProtocol { relays: [], proposer: proposer, requiredNamespaces: [:] - ) - .publicRepresentation() + ).publicRepresentation(pairingTopic: "") return Result.Publisher(sessionProposal) .eraseToAnyPublisher() diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..82b18368b --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,41 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +# ENV['FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD'] = "" + +default_platform(:ios) + +platform :ios do + + lane :unit_tests do + run_tests( + project: 'Example/ExampleApp.xcodeproj', + scheme: 'WalletConnect', + cloned_source_packages_path: 'SourcePackagesCache', + destination: 'platform=iOS Simulator,name=iPhone 13', + derived_data_path: 'DerivedDataCache' + ) + end + + # "xcodebuild \ + # -project Example/ExampleApp.xcodeproj \ + # -scheme WalletConnect \ + # -clonedSourcePackagesDirPath SourcePackagesCache \ + # -destination 'platform=iOS Simulator,name=iPhone 13' \ + # -derivedDataPath DerivedDataCache \ + # test" + + +end \ No newline at end of file From 7e46da30c7869710637b61df2dd79f70721cf83f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 30 Jan 2023 14:21:57 +0530 Subject: [PATCH 69/74] Fastlane CI --- .env.Showcase | 3 + .env.WalletApp | 3 + .github/actions/ci/action.yml | 39 +---- .github/workflows/ci.yml | 30 +--- .gitignore | 5 +- Example/DApp/Info.plist | 8 +- Example/ExampleApp.xcodeproj/project.pbxproj | 44 +++--- .../xcschemes/IntegrationTests.xcscheme | 25 ++++ .../xcshareddata/xcschemes/UITests.xcscheme | 16 ++ .../xcschemes/WalletConnect.xcscheme | 138 ++++++++---------- Example/ExampleApp/Info.plist | 10 +- Example/PushDecryptionService/Info.plist | 4 + Example/Showcase/Other/Info.plist | 4 + Example/WalletApp/Other/Info.plist | 4 + Makefile | 22 ++- fastlane/Appfile | 3 + fastlane/Fastfile | 65 +++++++-- 17 files changed, 241 insertions(+), 182 deletions(-) create mode 100644 .env.Showcase create mode 100644 .env.WalletApp create mode 100644 fastlane/Appfile diff --git a/.env.Showcase b/.env.Showcase new file mode 100644 index 000000000..c08277921 --- /dev/null +++ b/.env.Showcase @@ -0,0 +1,3 @@ +SCHEME = "Showcase" +APP_IDENTIFIER = "com.walletconnect.chat" +APPLE_ID = "1634760092" \ No newline at end of file diff --git a/.env.WalletApp b/.env.WalletApp new file mode 100644 index 000000000..5ac0d15e7 --- /dev/null +++ b/.env.WalletApp @@ -0,0 +1,3 @@ +SCHEME = "WalletApp" +APP_IDENTIFIER = "com.walletconnect.walletapp" +APPLE_ID = "1667351690" \ No newline at end of file diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml index e3f1b5e5f..1c105f783 100644 --- a/.github/actions/ci/action.yml +++ b/.github/actions/ci/action.yml @@ -19,13 +19,7 @@ runs: - name: Run tests if: inputs.type == 'unit-tests' shell: bash - run: "xcodebuild \ - -project Example/ExampleApp.xcodeproj \ - -scheme WalletConnect \ - -clonedSourcePackagesDirPath SourcePackagesCache \ - -destination 'platform=iOS Simulator,name=iPhone 13' \ - -derivedDataPath DerivedDataCache \ - test" + run: make unit_tests # Integration tests - name: Run integration tests @@ -34,46 +28,23 @@ runs: env: RELAY_ENDPOINT: ${{ inputs.relay-endpoint }} PROJECT_ID: ${{ inputs.project-id }} - run: "xcodebuild \ - -project Example/ExampleApp.xcodeproj \ - -scheme IntegrationTests \ - -clonedSourcePackagesDirPath SourcePackagesCache \ - -destination 'platform=iOS Simulator,name=iPhone 13' \ - -derivedDataPath DerivedDataCache \ - RELAY_HOST='$RELAY_ENDPOINT' \ - PROJECT_ID='$PROJECT_ID' \ - test" + run: make integration_tests RELAY_HOST=$RELAY_ENDPOINT PROJECT_ID=$PROJECT_ID # Wallet build - name: Build Example Wallet if: inputs.type == 'build-example-wallet' shell: bash - run: "xcodebuild \ - -project Example/ExampleApp.xcodeproj \ - -scheme Wallet \ - -clonedSourcePackagesDirPath SourcePackagesCache \ - -destination 'platform=iOS Simulator,name=iPhone 13' \ - -derivedDataPath DerivedDataCache" + run: make build_wallet # DApp build - name: Build Example Dapp if: inputs.type == 'build-example-dapp' shell: bash - run: "xcodebuild \ - -project Example/ExampleApp.xcodeproj \ - -scheme DApp \ - -clonedSourcePackagesDirPath SourcePackagesCache \ - -destination 'platform=iOS Simulator,name=iPhone 13' \ - -derivedDataPath DerivedDataCache" + run: make build_dapp # UI tests - name: UI Tests if: inputs.type == 'ui-tests' shell: bash - run: "xcodebuild \ - -project Example/ExampleApp.xcodeproj \ - -scheme UITests \ - -derivedDataPath DerivedDataCache \ - -clonedSourcePackagesDirPath SourcePackagesCache \ - -destination 'platform=iOS Simulator,name=iPhone 13' test" + run: make ui_tests continue-on-error: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a71b4ce7e..e1846f591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,37 +42,9 @@ jobs: - name: Resolve Dependencies shell: bash - run: " - xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme DApp -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache -destination 'platform=iOS Simulator,name=iPhone 13'; \ - xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme WalletConnect -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache -destination 'platform=iOS Simulator,name=iPhone 13'" + run: make resolve_packages - uses: ./.github/actions/ci with: type: ${{ matrix.test-type }} project-id: ${{ secrets.PROJECT_ID }} - - test-ui: - if: github.ref == 'refs/heads/main' - runs-on: macos-latest - strategy: - matrix: - test-type: [ui-tests] - - steps: - - uses: actions/checkout@v2 - - - name: Setup Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - - - uses: actions/cache@v2 - with: - path: | - .build - SourcePackagesCache - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - restore-keys: | - ${{ runner.os }}-spm- - - - uses: ./.github/actions/ci - with: - type: ${{ matrix.test-type }} diff --git a/.gitignore b/.gitignore index 31cddd747..64e06f6b1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,7 @@ Configuration.xcconfig # Cache SourcePackagesCache -DerivedDataCache \ No newline at end of file +DerivedDataCache + +# Artifacts +*.ipa \ No newline at end of file diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist index 07ba0e705..ec622dae1 100644 --- a/Example/DApp/Info.plist +++ b/Example/DApp/Info.plist @@ -2,10 +2,14 @@ - PROJECT_ID - $(PROJECT_ID) + CFBundleVersion + 7 + CFBundleShortVersionString + $(MARKETING_VERSION) ITSAppUsesNonExemptEncryption + PROJECT_ID + $(PROJECT_ID) UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 1c486bd81..dd044d0bc 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -2334,6 +2334,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 7; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -2358,6 +2359,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -2396,6 +2398,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 7; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -2415,6 +2418,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -2427,7 +2431,7 @@ CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -2435,7 +2439,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example; PRODUCT_NAME = "WalletConnect Wallet"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2453,7 +2457,7 @@ CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -2461,7 +2465,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example; PRODUCT_NAME = "WalletConnect Wallet"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2477,7 +2481,7 @@ CODE_SIGN_ENTITLEMENTS = PushDecryptionService/PushDecryptionService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PushDecryptionService/Info.plist; @@ -2506,7 +2510,7 @@ CODE_SIGN_ENTITLEMENTS = PushDecryptionService/PushDecryptionService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PushDecryptionService/Info.plist; @@ -2536,7 +2540,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; @@ -2552,7 +2556,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2570,7 +2574,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; @@ -2586,7 +2590,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2661,7 +2665,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Showcase/Other/Info.plist; @@ -2691,7 +2695,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Showcase/Other/Info.plist; @@ -2719,11 +2723,10 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.UITests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -2738,11 +2741,10 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.UITests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -2757,7 +2759,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -2766,7 +2768,6 @@ ); GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -2780,11 +2781,10 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -2802,7 +2802,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2835,7 +2835,7 @@ CODE_SIGN_ENTITLEMENTS = WalletApp/WalletAppRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme index 6c42dec52..177cd0cec 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme @@ -5,6 +5,22 @@ + + + + + + + + + + diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme index a8ccba84c..e454d658f 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme @@ -5,6 +5,22 @@ + + + + + + @@ -42,9 +42,9 @@ buildForAnalyzing = "YES"> @@ -56,9 +56,9 @@ buildForAnalyzing = "YES"> @@ -70,9 +70,9 @@ buildForAnalyzing = "YES"> @@ -84,9 +84,9 @@ buildForAnalyzing = "YES"> @@ -98,9 +98,9 @@ buildForAnalyzing = "YES"> @@ -112,23 +112,9 @@ buildForAnalyzing = "YES"> - - - - @@ -140,9 +126,9 @@ buildForAnalyzing = "YES"> @@ -154,9 +140,9 @@ buildForAnalyzing = "YES"> @@ -168,9 +154,9 @@ buildForAnalyzing = "YES"> @@ -196,9 +182,9 @@ skipped = "NO"> @@ -206,9 +192,9 @@ skipped = "NO"> @@ -216,9 +202,9 @@ skipped = "NO"> @@ -226,9 +212,9 @@ skipped = "NO"> @@ -236,9 +222,9 @@ skipped = "NO"> @@ -246,9 +232,9 @@ skipped = "NO"> @@ -256,9 +242,9 @@ skipped = "NO"> @@ -266,9 +252,9 @@ skipped = "NO"> @@ -276,9 +262,9 @@ skipped = "NO"> @@ -286,9 +272,9 @@ skipped = "NO"> diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index 97535e087..c1f94161e 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -2,10 +2,6 @@ - PROJECT_ID - $(PROJECT_ID) - SIMULATOR_IDENTIFIER - $(SIMULATOR_IDENTIFIER) CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -36,13 +32,17 @@ CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 7 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS NSCameraUsageDescription Allow the app to scan for QR codes + PROJECT_ID + $(PROJECT_ID) + SIMULATOR_IDENTIFIER + $(SIMULATOR_IDENTIFIER) UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Example/PushDecryptionService/Info.plist b/Example/PushDecryptionService/Info.plist index 57421ebf9..38ada2364 100644 --- a/Example/PushDecryptionService/Info.plist +++ b/Example/PushDecryptionService/Info.plist @@ -2,6 +2,10 @@ + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 7 NSExtension NSExtensionPointIdentifier diff --git a/Example/Showcase/Other/Info.plist b/Example/Showcase/Other/Info.plist index 136371e1c..f4245dd27 100644 --- a/Example/Showcase/Other/Info.plist +++ b/Example/Showcase/Other/Info.plist @@ -2,6 +2,10 @@ + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 7 PROJECT_ID $(PROJECT_ID) RELAY_HOST diff --git a/Example/WalletApp/Other/Info.plist b/Example/WalletApp/Other/Info.plist index ef4c6367a..08395f3e9 100644 --- a/Example/WalletApp/Other/Info.plist +++ b/Example/WalletApp/Other/Info.plist @@ -2,6 +2,10 @@ + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 7 CFBundleIconName AppIcon CFBundleURLTypes diff --git a/Makefile b/Makefile index 1bd579e75..218b88441 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,26 @@ ifeq "${EXISTS_FASTLANE}" "" endif @echo "All dependencies was installed" +build_dapp: + fastlane build scheme:DApp + +build_wallet: + fastlane build scheme:WalletApp + +ui_tests: + echo "UI Tests disabled" unit_tests: - fastlane unit_tests \ No newline at end of file + fastlane tests scheme:WalletConnect + +integration_tests: + fastlane tests scheme:IntegrationTests relay_host:$(RELAY_HOST) project_id:$(PROJECT_ID) + +resolve_packages: + fastlane resolve scheme:WalletApp + +release_wallet: + fastlane release_testflight username:$(APPLE_ID) --env WalletApp + +release_showcase: + fastlane release_testflight username:$(APPLE_ID) --env Showcase \ No newline at end of file diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 000000000..9fae697da --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,3 @@ +itc_team_id("123564616") +team_id("W5R8AG9K22") +git_url("https://github.com/WalletConnect/match-swift") diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 82b18368b..ba33be662 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -13,29 +13,70 @@ # Uncomment the line if you want fastlane to automatically update itself # update_fastlane -# ENV['FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD'] = "" +ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120" default_platform(:ios) platform :ios do - - lane :unit_tests do + + lane :tests do |options| run_tests( project: 'Example/ExampleApp.xcodeproj', - scheme: 'WalletConnect', + scheme: options[:scheme], cloned_source_packages_path: 'SourcePackagesCache', destination: 'platform=iOS Simulator,name=iPhone 13', - derived_data_path: 'DerivedDataCache' + derived_data_path: 'DerivedDataCache', + skip_package_dependencies_resolution: true, + skip_build: true, + xcargs: "RELAY_HOST='#{options[:relay_host]}' PROJECT_ID='#{options[:project_id]}'" + ) + end + + lane :build do |options| + xcodebuild( + project: 'Example/ExampleApp.xcodeproj', + scheme: options[:scheme], + destination: 'platform=iOS Simulator,name=iPhone 13', + xcargs: "-clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache" ) end - # "xcodebuild \ - # -project Example/ExampleApp.xcodeproj \ - # -scheme WalletConnect \ - # -clonedSourcePackagesDirPath SourcePackagesCache \ - # -destination 'platform=iOS Simulator,name=iPhone 13' \ - # -derivedDataPath DerivedDataCache \ - # test" + lane :resolve do |options| + xcodebuild( + project: 'Example/ExampleApp.xcodeproj', + scheme: options[:scheme], + destination: 'platform=iOS Simulator,name=iPhone 13', + xcargs: "-resolvePackageDependencies -clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache" + ) + end + lane :release_testflight do |options| + match( + readonly: false, + type: "appstore", + app_identifier: ENV["APP_IDENTIFIER"], + git_url: "https://github.com/WalletConnect/match-swift.git", + username: options[:username], + ) + number = latest_testflight_build_number( + app_identifier: ENV["APP_IDENTIFIER"], + username: options[:username], + ) + increment_build_number( + build_number: number + 1, + xcodeproj: "Example/ExampleApp.xcodeproj" + ) + build_app( + project: "Example/ExampleApp.xcodeproj", + scheme: ENV["SCHEME"] + ) + upload_to_testflight( + app_identifier: ENV["APP_IDENTIFIER"], + username: options[:username], + apple_id: ENV["APPLE_ID"], + skip_waiting_for_build_processing: true, + ) + clean_build_artifacts() + end end \ No newline at end of file From d4abe18c55d1f1c68d958da8351ba83aa05b37e9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 31 Jan 2023 16:45:11 +0530 Subject: [PATCH 70/74] SignClientMock fix --- Tests/Web3WalletTests/Mocks/SignClientMock.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Web3WalletTests/Mocks/SignClientMock.swift b/Tests/Web3WalletTests/Mocks/SignClientMock.swift index d112422ef..ab03aca18 100644 --- a/Tests/Web3WalletTests/Mocks/SignClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/SignClientMock.swift @@ -36,7 +36,7 @@ final class SignClientMock: SignClientProtocol { } var sessionsPublisher: AnyPublisher<[WalletConnectSign.Session], Never> { - return Result.Publisher([WalletConnectSign.Session(topic: "", peer: metadata, namespaces: [:], expiryDate: Date())]) + return Result.Publisher([WalletConnectSign.Session(topic: "", pairingTopic: "", peer: metadata, namespaces: [:], expiryDate: Date())]) .eraseToAnyPublisher() } @@ -73,7 +73,7 @@ final class SignClientMock: SignClientProtocol { } func getSessions() -> [WalletConnectSign.Session] { - return [WalletConnectSign.Session(topic: "", peer: metadata, namespaces: [:], expiryDate: Date())] + return [WalletConnectSign.Session(topic: "", pairingTopic: "", peer: metadata, namespaces: [:], expiryDate: Date())] } func getPendingRequests(topic: String?) -> [WalletConnectSign.Request] { From ccfa71f593bc65ea79720231fc95f2057799594c Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Thu, 2 Feb 2023 22:32:10 +0100 Subject: [PATCH 71/74] Make test topic value compliant with new relay topic policies --- Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index 60ab35252..79e91d5f2 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -33,7 +33,7 @@ final class RelayClientEndToEndTests: XCTestCase { subscribeExpectation.assertForOverFulfill = true relayClient.socketConnectionStatusPublisher.sink { status in if status == .connected { - relayClient.subscribe(topic: "qwerty") { error in + relayClient.subscribe(topic: "ecb78f2df880c43d3418ddbf871092b847801932e21765b250cc50b9e96a9131") { error in XCTAssertNil(error) subscribeExpectation.fulfill() } From ae642172a3c583faef553f34b00ce3cbf2c05c07 Mon Sep 17 00:00:00 2001 From: llbartekll Date: Fri, 3 Feb 2023 07:24:54 +0000 Subject: [PATCH 72/74] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index c712e32f1..1663b3678 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.3.0"} +{"version": "1.3.1"} From f8e1514f9a8b78a3b22f5f0e34f5c74a35804951 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 3 Feb 2023 15:22:43 +0530 Subject: [PATCH 73/74] Update Router.m for compatibility with MacOS --- Sources/WalletConnectRouter/Router.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/WalletConnectRouter/Router.m b/Sources/WalletConnectRouter/Router.m index 9010aa7e6..0648d6eb1 100644 --- a/Sources/WalletConnectRouter/Router.m +++ b/Sources/WalletConnectRouter/Router.m @@ -1,6 +1,8 @@ #import #import "Router.h" +#if __has_include() + @import UIKit; @import ObjectiveC.runtime; @@ -24,3 +26,5 @@ + (void)goBack { @end +#endif + From 4d6da86a255b4aa2674b74bef4eebc945cbb6952 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 3 Feb 2023 18:19:23 +0530 Subject: [PATCH 74/74] Old keyserver tests disabled --- Example/IntegrationTests/Chat/RegistryTests.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Example/IntegrationTests/Chat/RegistryTests.swift b/Example/IntegrationTests/Chat/RegistryTests.swift index ab665f828..ea21c9375 100644 --- a/Example/IntegrationTests/Chat/RegistryTests.swift +++ b/Example/IntegrationTests/Chat/RegistryTests.swift @@ -6,13 +6,4 @@ import WalletConnectUtils final class RegistryTests: XCTestCase { - func testRegistry() async throws { - let client = HTTPNetworkClient(host: "keys.walletconnect.com") - let registry = KeyserverRegistryProvider(client: client) - let account = Account("eip155:1:" + Data.randomBytes(count: 16).toHexString())! - let pubKey = SigningPrivateKey().publicKey.hexRepresentation - try await registry.register(account: account, pubKey: pubKey) - let resolvedKey = try await registry.resolve(account: account) - XCTAssertEqual(resolvedKey, pubKey) - } }