From 1c65fe21f307daa8f9a8b6cd3c974be322c96c11 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 14 Jan 2022 09:56:35 +0100 Subject: [PATCH 01/16] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 4 +- Example/ExampleApp/Info.plist | 4 +- .../Responder/ResponderViewController.swift | 10 +--- .../SessionProposal/SessionInfo.swift | 25 ---------- Example/ExampleApp/SceneDelegate.swift | 2 +- .../SessionDetails/SessionDetailsView.swift | 15 ++++++ .../SessionDetailsViewController.swift | 46 +++++++++++++++++-- 7 files changed, 65 insertions(+), 41 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 6db21cbfb..7a51e3286 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -469,7 +469,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 6; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -493,7 +493,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 6; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index a1328e4b8..1fa4cd501 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -20,12 +20,12 @@ $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) + ITSAppUsesNonExemptEncryption + LSRequiresIPhoneOS NSCameraUsageDescription Allow the app to scan for QR codes - ITSAppUsesNonExemptEncryption - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index 48c7d5463..da8f28cb4 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -16,7 +16,7 @@ final class ResponderViewController: UIViewController { metadata: metadata, projectId: "52af113ee0c1e1a20f4995730196c13e", isController: true, - relayHost: "relay.dev.walletconnect.com", + relayHost: "relay.dev.walletconnect.com", //use with dapp at https://canary.react-app.walletconnect.com/ clientName: "responder" ) }() @@ -69,13 +69,7 @@ final class ResponderViewController: UIViewController { } private func showSessionDetailsViewController(_ session: Session) { - let sessionInfo = SessionInfo(name: session.peer.name ?? "", - descriptionText: session.peer.description ?? "", - dappURL: session.peer.description ?? "", - iconURL: session.peer.icons?.first ?? "", - chains: Array(session.permissions.blockchains), - methods: Array(session.permissions.methods)) - let vc = SessionDetailsViewController(sessionInfo) + let vc = SessionDetailsViewController(session, client) navigationController?.pushViewController(vc, animated: true) } diff --git a/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift b/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift index 49e54f897..d0a8cff33 100644 --- a/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift +++ b/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift @@ -6,28 +6,3 @@ struct SessionInfo { let chains: [String] let methods: [String] } - -extension SessionInfo { - - static func mock() -> SessionInfo { - SessionInfo( - name: "Dapp Name", - descriptionText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at eleifend est, vel porta enim. Praesent non placerat orci. Curabitur orci sem, molestie feugiat enim eu, tincidunt tincidunt est.", - dappURL: "decentralized.finance", - iconURL: "https://s2.coinmarketcap.com/static/img/coins/64x64/1.png", - chains: ["Ethereum Kovan", "BSC Mainnet", "Fantom Opera"], - methods: ["personal_sign", "eth_sendTransaction", "eth_signTypedData"] - ) - } - - static func mockPancakeSwap() -> SessionInfo { - SessionInfo( - name: "🥞 PancakeSwap", - descriptionText: "Cheaper and faster than Uniswap? Discover PancakeSwap, the leading DEX on Binance Smart Chain (BSC) with the best farms in DeFi and a lottery for CAKE.", - dappURL: "pancakeswap.finance", - iconURL: "https://pancakeswap.finance/logo.png", - chains: ["Binance Smart Chain (BSC)"], - methods: ["personal_sign"] - ) - } -} diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index de221ae10..3cdd41306 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -20,7 +20,7 @@ extension UITabBarController { let proposerController = UINavigationController(rootViewController: ProposerViewController()) proposerController.tabBarItem = UITabBarItem(title: "Dapp", image: UIImage(systemName: "appclip"), selectedImage: nil) let tabBarController = UITabBarController() - tabBarController.viewControllers = [responderController, proposerController] + tabBarController.viewControllers = [responderController] return tabBarController } } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsView.swift b/Example/ExampleApp/SessionDetails/SessionDetailsView.swift index f7565a8e4..766c2b3f6 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsView.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsView.swift @@ -56,6 +56,15 @@ final class SessionDetailsView: UIView { return stackView }() + let pingButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Ping", for: .normal) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 8 + return button + }() + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground @@ -67,6 +76,7 @@ final class SessionDetailsView: UIView { headerStackView.addArrangedSubview(nameLabel) headerStackView.addArrangedSubview(urlLabel) headerStackView.addArrangedSubview(descriptionLabel) + addSubview(pingButton) subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -87,6 +97,11 @@ final class SessionDetailsView: UIView { methodsStackView.topAnchor.constraint(equalTo: chainsStackView.bottomAnchor, constant: 24), methodsStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), methodsStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), + + pingButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -16), + pingButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), + pingButton.heightAnchor.constraint(equalToConstant: 44), + pingButton.widthAnchor.constraint(equalToConstant: 64), ]) } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index 061fa882c..e051bc5f9 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -1,4 +1,5 @@ import UIKit +import WalletConnect final class SessionDetailsViewController: UIViewController { @@ -6,9 +7,17 @@ final class SessionDetailsViewController: UIViewController { SessionDetailsView() }() private let sessionInfo: SessionInfo - - init(_ sessionInfo: SessionInfo) { - self.sessionInfo = sessionInfo + private let client: WalletConnectClient + private let session: Session + init(_ session: Session, _ client: WalletConnectClient) { + self.sessionInfo = SessionInfo(name: session.peer.name ?? "", + descriptionText: session.peer.description ?? "", + dappURL: session.peer.description ?? "", + iconURL: session.peer.icons?.first ?? "", + chains: Array(session.permissions.blockchains), + methods: Array(session.permissions.methods)) + self.client = client + self.session = session super.init(nibName: nil, bundle: nil) } @@ -19,6 +28,7 @@ final class SessionDetailsViewController: UIViewController { override func viewDidLoad() { show(sessionInfo) super.viewDidLoad() + sessiondetailsView.pingButton.addTarget(self, action: #selector(ping), for: .touchUpInside) } override func loadView() { @@ -33,4 +43,34 @@ final class SessionDetailsViewController: UIViewController { sessiondetailsView.list(chains: sessionInfo.chains) sessiondetailsView.list(methods: sessionInfo.methods) } + + @objc + private func ping() { + client.ping(topic: session.topic) { result in + switch result { + case .success(): + print("received ping response") + case .failure(let error): + print(error) + } + } + } } + + +//class SessionDetailsViewControlle: UITableViewController { +// +// override func numberOfSections(in tableView: UITableView) -> Int { +// return 3 +// } +// +// override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { +// if section == 0 { +// return "Chains" +// } else if section == 1 { +// return "Methods" +// } else { +// return "Pending Requests" +// } +// } +//} From 4d81217f78092084b7d2603ad88b6fee736f4df1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 14 Jan 2022 10:30:07 +0100 Subject: [PATCH 02/16] add tableview to SessionDetailsViewController --- .../SessionDetails/SessionDetailsView.swift | 56 +++-------------- .../SessionDetailsViewController.swift | 63 ++++++++++++------- 2 files changed, 52 insertions(+), 67 deletions(-) diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsView.swift b/Example/ExampleApp/SessionDetails/SessionDetailsView.swift index 766c2b3f6..10b7b7f6f 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsView.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsView.swift @@ -40,22 +40,7 @@ final class SessionDetailsView: UIView { return stackView }() - let chainsStackView: UIStackView = { - let stackView = UIStackView() - stackView.axis = .vertical - stackView.spacing = 10 - stackView.alignment = .leading - return stackView - }() - - let methodsStackView: UIStackView = { - let stackView = UIStackView() - stackView.axis = .vertical - stackView.spacing = 10 - stackView.alignment = .leading - return stackView - }() - + let pingButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Ping", for: .normal) @@ -65,14 +50,16 @@ final class SessionDetailsView: UIView { return button }() + let tableView = UITableView() + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground addSubview(iconView) addSubview(headerStackView) - addSubview(chainsStackView) - addSubview(methodsStackView) + addSubview(tableView) + headerStackView.addArrangedSubview(nameLabel) headerStackView.addArrangedSubview(urlLabel) headerStackView.addArrangedSubview(descriptionLabel) @@ -90,14 +77,11 @@ final class SessionDetailsView: UIView { headerStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), headerStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), - chainsStackView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 24), - chainsStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), - chainsStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), - - methodsStackView.topAnchor.constraint(equalTo: chainsStackView.bottomAnchor, constant: 24), - methodsStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), - methodsStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), - + tableView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 0), + tableView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0), + tableView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0), + tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), + pingButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -16), pingButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), pingButton.heightAnchor.constraint(equalToConstant: 44), @@ -116,26 +100,6 @@ final class SessionDetailsView: UIView { } } - func list(chains: [String]) { - let label = UILabel() - label.text = "Chains" - label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) - chainsStackView.addArrangedSubview(label) - chains.forEach { - chainsStackView.addArrangedSubview(ListItem(text: $0)) - } - } - - func list(methods: [String]) { - let label = UILabel() - label.text = "Methods" - label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) - methodsStackView.addArrangedSubview(label) - methods.forEach { - methodsStackView.addArrangedSubview(ListItem(text: $0)) - } - } - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index e051bc5f9..f3eda030c 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -1,7 +1,7 @@ import UIKit import WalletConnect -final class SessionDetailsViewController: UIViewController { +final class SessionDetailsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { private let sessiondetailsView = { SessionDetailsView() @@ -29,6 +29,9 @@ final class SessionDetailsViewController: UIViewController { show(sessionInfo) super.viewDidLoad() sessiondetailsView.pingButton.addTarget(self, action: #selector(ping), for: .touchUpInside) + sessiondetailsView.tableView.delegate = self + sessiondetailsView.tableView.dataSource = self + sessiondetailsView.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") } override func loadView() { @@ -40,8 +43,6 @@ final class SessionDetailsViewController: UIViewController { sessiondetailsView.descriptionLabel.text = sessionInfo.descriptionText sessiondetailsView.urlLabel.text = sessionInfo.dappURL sessiondetailsView.loadImage(at: sessionInfo.iconURL) - sessiondetailsView.list(chains: sessionInfo.chains) - sessiondetailsView.list(methods: sessionInfo.methods) } @objc @@ -55,22 +56,42 @@ final class SessionDetailsViewController: UIViewController { } } } + + //MARK: - Table View + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if section == 0 { + return sessionInfo.chains.count + } else if section == 1 { + return sessionInfo.methods.count + } else { + return 0 + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + if indexPath.section == 0 { + cell.textLabel?.text = sessionInfo.chains[indexPath.row] + } else if indexPath.section == 1 { + cell.textLabel?.text = sessionInfo.methods[indexPath.row] + } else { + // cell.textLabel?.text = sessionInfo.pendingRequests[indexPath.row] + } + return cell + } + + func numberOfSections(in tableView: UITableView) -> Int { + return 3 + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + if section == 0 { + return "Chains" + } else if section == 1 { + return "Methods" + } else { + return "Pending Requests" + } + } } - - -//class SessionDetailsViewControlle: UITableViewController { -// -// override func numberOfSections(in tableView: UITableView) -> Int { -// return 3 -// } -// -// override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { -// if section == 0 { -// return "Chains" -// } else if section == 1 { -// return "Methods" -// } else { -// return "Pending Requests" -// } -// } -//} From 46c965a1512b915bcc56edb6303cf5d75bc62c4e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 14 Jan 2022 15:34:15 +0100 Subject: [PATCH 03/16] #14 expose json rpc history --- .../Responder/ResponderViewController.swift | 6 ++--- .../SessionProposal/SessionInfo.swift | 1 + .../SessionDetailsViewController.swift | 16 +++++++----- .../WalletConnect/Engine/SessionEngine.swift | 12 ++++++++- .../JsonRpcHistory/JsonRpcHistory.swift | 4 +-- Sources/WalletConnect/Request.swift | 2 +- .../WalletConnect/WalletConnectClient.swift | 22 +++++++++++++--- .../WalletConnectUtils/JsonRpcHistory.swift | 26 +++++++++---------- .../WalletConnectUtils/JsonRpcRecord.swift | 16 ++++++------ .../WalletConnectUtils/KeyValueStore.swift | 15 ++++++++--- 10 files changed, 77 insertions(+), 43 deletions(-) diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index da8f28cb4..629938d40 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -77,11 +77,11 @@ final class ResponderViewController: UIViewController { let requestVC = RequestViewController(sessionRequest) requestVC.onSign = { [unowned self] in let result = signEth(request: sessionRequest) - let response = JSONRPCResponse(id: sessionRequest.id, result: result) + let response = JSONRPCResponse(id: sessionRequest.id!, result: result) client.respond(topic: sessionRequest.topic, response: .response(response)) } requestVC.onReject = { [weak self] in - self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) } present(requestVC, animated: true) } @@ -167,7 +167,7 @@ extension ResponderViewController: WalletConnectClientDelegate { dappURL: appMetadata.url ?? "", iconURL: appMetadata.icons?.first ?? "", chains: Array(sessionProposal.permissions.blockchains), - methods: Array(sessionProposal.permissions.methods)) + methods: Array(sessionProposal.permissions.methods), pendingRequests: []) currentProposal = sessionProposal DispatchQueue.main.async { // FIXME: Delegate being called from background thread self.showSessionProposal(info) diff --git a/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift b/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift index d0a8cff33..218d7834d 100644 --- a/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift +++ b/Example/ExampleApp/Responder/SessionProposal/SessionInfo.swift @@ -5,4 +5,5 @@ struct SessionInfo { let iconURL: String let chains: [String] let methods: [String] + let pendingRequests: [String] } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index f3eda030c..013a81e97 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -10,12 +10,14 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, private let client: WalletConnectClient private let session: Session init(_ session: Session, _ client: WalletConnectClient) { + let pendingRequests = client.getPendingRequests().map{$0.method} self.sessionInfo = SessionInfo(name: session.peer.name ?? "", - descriptionText: session.peer.description ?? "", - dappURL: session.peer.description ?? "", - iconURL: session.peer.icons?.first ?? "", - chains: Array(session.permissions.blockchains), - methods: Array(session.permissions.methods)) + descriptionText: session.peer.description ?? "", + dappURL: session.peer.description ?? "", + iconURL: session.peer.icons?.first ?? "", + chains: Array(session.permissions.blockchains), + methods: Array(session.permissions.methods), + pendingRequests: pendingRequests) self.client = client self.session = session super.init(nibName: nil, bundle: nil) @@ -65,7 +67,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, } else if section == 1 { return sessionInfo.methods.count } else { - return 0 + return sessionInfo.pendingRequests.count } } @@ -76,7 +78,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, } else if indexPath.section == 1 { cell.textLabel?.text = sessionInfo.methods[indexPath.row] } else { - // cell.textLabel?.text = sessionInfo.pendingRequests[indexPath.row] + cell.textLabel?.text = sessionInfo.pendingRequests[indexPath.row] } return cell } diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index a627bad9d..d1545de91 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -22,6 +22,7 @@ final class SessionEngine { private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let topicInitializer: () -> String? + let sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory init(relay: WalletConnectRelaying, crypto: CryptoStorageProtocol, @@ -30,7 +31,8 @@ final class SessionEngine { isController: Bool, metadata: AppMetadata, logger: ConsoleLogging, - topicGenerator: @escaping () -> String? = String.generateTopic) { + topicGenerator: @escaping () -> String? = String.generateTopic, + sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory) { self.relayer = relay self.crypto = crypto self.metadata = metadata @@ -39,6 +41,7 @@ final class SessionEngine { self.isController = isController self.logger = logger self.topicInitializer = topicGenerator + self.sessionPayloadsJsonRpcHistory = sessionPayloadsJsonRpcHistory setUpWCRequestHandling() setupExpirationHandling() restoreSubscriptions() @@ -179,6 +182,11 @@ final class SessionEngine { let request = SessionType.PayloadParams.Request(method: params.method, params: params.params) let sessionPayloadParams = SessionType.PayloadParams(request: request, chainId: params.chainId) let sessionPayloadRequest = WCRequest(method: .sessionPayload, params: .sessionPayload(sessionPayloadParams)) + + + let recordRequest = JSONRPCRequest(id: sessionPayloadRequest.id, method: params.method, params: params.params) + try? sessionPayloadsJsonRpcHistory.set(topic: params.topic, request: recordRequest, chainId: params.chainId) + relayer.request(topic: params.topic, payload: sessionPayloadRequest) { [weak self] result in switch result { case .success(let response): @@ -419,6 +427,8 @@ final class SessionEngine { method: jsonRpcRequest.method, params: jsonRpcRequest.params, chainId: payloadParams.chainId) + + try? sessionPayloadsJsonRpcHistory.set(topic: topic, request: jsonRpcRequest, chainId: payloadParams.chainId) do { try validatePayload(request) onSessionPayloadRequest?(request) diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index 1aa802bbd..8620a67ad 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -9,7 +9,7 @@ protocol JsonRpcHistoryRecording { func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord func exist(id: Int64) -> Bool } - +//TODO -remove and use jsonrpc history only from utils class JsonRpcHistory: JsonRpcHistoryRecording { let storage: KeyValueStore let logger: ConsoleLogging @@ -17,7 +17,7 @@ class JsonRpcHistory: JsonRpcHistoryRecording { init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, uniqueIdentifier: String? = nil) { self.logger = logger - self.storage = KeyValueStore(defaults: keyValueStorage) + self.storage = KeyValueStore(defaults: keyValueStorage, identifier: "") self.identifier = "com.walletconnect.sdk.\(uniqueIdentifier ?? "")" } diff --git a/Sources/WalletConnect/Request.swift b/Sources/WalletConnect/Request.swift index cffcdd128..d70bcd752 100644 --- a/Sources/WalletConnect/Request.swift +++ b/Sources/WalletConnect/Request.swift @@ -2,7 +2,7 @@ import Foundation import WalletConnectUtils public struct Request: Codable, Equatable { - public let id: Int64 + public internal(set) var id: Int64? public let topic: String public let method: String public let params: AnyCodable diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 5bfa920ef..d61ae84b1 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -6,6 +6,14 @@ import WalletConnectUtils import UIKit #endif + + +enum StorageDomainIdentifiers { + static func sessionPayloadsJsonRpcHistory(clientName: String) -> String { + return "com.walletconnect.sdk.\(clientName).jsonRpcHistory.sessionPayloads" + } +} + /// An Object that expose public API to provide interactions with WalletConnect SDK /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialise it. Usually only one instance of a client is required in the application. @@ -30,7 +38,7 @@ public final class WalletConnectClient { private let crypto: Crypto private let secureStorage: SecureStorage private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated) - + // MARK: - Initializers /// Initializes and returns newly created WalletConnect Client Instance. Establishes a network connection with the relay @@ -62,7 +70,9 @@ public final class WalletConnectClient { let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) - self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) + let sessionPayloadsJsonRpcHistoryIdentifier = StorageDomainIdentifiers.sessionPayloadsJsonRpcHistory(clientName: clientName ?? "") + let sessionPayloadsJsonRpcHistory = WalletConnectUtils.JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, identifier: sessionPayloadsJsonRpcHistoryIdentifier) + self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger, sessionPayloadsJsonRpcHistory: sessionPayloadsJsonRpcHistory) setUpEnginesCallbacks() subscribeNotificationCenter() } @@ -221,7 +231,13 @@ public final class WalletConnectClient { pairingEngine.getSettledPairings() } - //MARK: - Private + public func getPendingRequests() -> [Request] { + sessionEngine.sessionPayloadsJsonRpcHistory.getPending().compactMap { + return Request(id: $0.id, topic: $0.topic, method: $0.request.method, params: $0.request.params, chainId: $0.chainId) + } + } + + // MARK: - Private private func setUpEnginesCallbacks() { pairingEngine.onSessionProposal = { [unowned self] proposal in diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift index aacc2c291..33a1214ba 100644 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ b/Sources/WalletConnectUtils/JsonRpcHistory.swift @@ -7,50 +7,48 @@ public class JsonRpcHistory where T: Codable&Equatable { } private let storage: KeyValueStore private let logger: ConsoleLogging - private let identifier: String - + public init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, identifier: String) { self.logger = logger - self.storage = KeyValueStore(defaults: keyValueStorage) - self.identifier = identifier + self.storage = KeyValueStore(defaults: keyValueStorage, identifier: identifier) } public func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: getKey(for: id)) + try? storage.get(key: "\(id)") } - public func set(topic: String, request: JSONRPCRequest) throws { + public func set(topic: String, request: JSONRPCRequest, chainId: String? = nil) throws { guard !exist(id: request.id) else { throw RecordingError.jsonRpcDuplicateDetected } logger.debug("Setting JSON-RPC request history record") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil) - try storage.set(record, forKey: getKey(for: request.id)) + let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil, chainId: chainId) + try storage.set(record, forKey: "\(request.id)") } public func delete(topic: String) { storage.getAll().forEach { record in if record.topic == topic { - storage.delete(forKey: getKey(for: record.id)) + storage.delete(forKey: "\(record.id)") } } } public func resolve(response: JsonRpcResponseTypes) throws { - guard var record = try? storage.get(key: getKey(for: response.id)) else { return } + guard var record = try? storage.get(key: "\(response.id)") else { return } if record.response != nil { throw RecordingError.jsonRpcDuplicateDetected } else { record.response = response - try storage.set(record, forKey: getKey(for: record.id)) + try storage.set(record, forKey: "\(record.id)") } } public func exist(id: Int64) -> Bool { - return (try? storage.get(key: getKey(for: id))) != nil + return (try? storage.get(key: "\(id)")) != nil } - private func getKey(for id: Int64) -> String { - return "com.walletconnect.sdk.\(identifier).\(id)" + public func getPending() -> [JsonRpcRecord] { + storage.getAll().filter{$0.response == nil} } } diff --git a/Sources/WalletConnectUtils/JsonRpcRecord.swift b/Sources/WalletConnectUtils/JsonRpcRecord.swift index 12ec84905..225cca1ac 100644 --- a/Sources/WalletConnectUtils/JsonRpcRecord.swift +++ b/Sources/WalletConnectUtils/JsonRpcRecord.swift @@ -1,16 +1,16 @@ import Foundation -import WalletConnectUtils public struct JsonRpcRecord: Codable { - let id: Int64 - let topic: String - let request: Request - var response: JsonRpcResponseTypes? + public let id: Int64 + public let topic: String + public let request: Request + public var response: JsonRpcResponseTypes? + public let chainId: String? - struct Request: Codable { - let method: String - let params: AnyCodable + public struct Request: Codable { + public let method: String + public let params: AnyCodable } } diff --git a/Sources/WalletConnectUtils/KeyValueStore.swift b/Sources/WalletConnectUtils/KeyValueStore.swift index 71a48e392..eb6b2bd22 100644 --- a/Sources/WalletConnectUtils/KeyValueStore.swift +++ b/Sources/WalletConnectUtils/KeyValueStore.swift @@ -3,24 +3,27 @@ import Foundation public final class KeyValueStore where T: Codable { private let defaults: KeyValueStorage + private let prefix: String - public init(defaults: KeyValueStorage) { + public init(defaults: KeyValueStorage, identifier: String) { self.defaults = defaults + self.prefix = identifier } public func set(_ item: T, forKey key: String) throws { let encoded = try JSONEncoder().encode(item) - defaults.set(encoded, forKey: key) + defaults.set(encoded, forKey: getContextPrefixKey(for: key)) } public func get(key: String) throws -> T? { - guard let data = defaults.object(forKey: key) as? Data else { return nil } + guard let data = defaults.object(forKey: getContextPrefixKey(for: key)) as? Data else { return nil } let item = try JSONDecoder().decode(T.self, from: data) return item } public func getAll() -> [T] { return defaults.dictionaryRepresentation().compactMap { + guard $0.key.hasPrefix(prefix) else {return nil} if let data = $0.value as? Data, let item = try? JSONDecoder().decode(T.self, from: data) { return item @@ -30,6 +33,10 @@ public final class KeyValueStore where T: Codable { } public func delete(forKey key: String) { - defaults.removeObject(forKey: key) + defaults.removeObject(forKey: getContextPrefixKey(for: key)) + } + + private func getContextPrefixKey(for key: String) -> String { + return "\(prefix).\(key)" } } From 0f034ea645aabef4456853e5421fbce89e3dca09 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Jan 2022 10:21:17 +0100 Subject: [PATCH 04/16] savepoint --- .../WalletConnect/Engine/SessionEngine.swift | 12 +----------- .../JsonRpcHistory/JsonRpcHistory.swift | 10 +++++++--- .../JsonRpcHistory/JsonRpcRecord.swift | 3 ++- .../Relay/WalletConnectRelay.swift | 4 ++-- .../WalletConnect/WalletConnectClient.swift | 19 +++++++++++-------- .../WalletConnectUtils/JsonRpcHistory.swift | 13 +++++++++---- .../WalletConnectUtils/KeyValueStore.swift | 8 ++++---- 7 files changed, 36 insertions(+), 33 deletions(-) diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index d1545de91..a627bad9d 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -22,7 +22,6 @@ final class SessionEngine { private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let topicInitializer: () -> String? - let sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory init(relay: WalletConnectRelaying, crypto: CryptoStorageProtocol, @@ -31,8 +30,7 @@ final class SessionEngine { isController: Bool, metadata: AppMetadata, logger: ConsoleLogging, - topicGenerator: @escaping () -> String? = String.generateTopic, - sessionPayloadsJsonRpcHistory: WalletConnectUtils.JsonRpcHistory) { + topicGenerator: @escaping () -> String? = String.generateTopic) { self.relayer = relay self.crypto = crypto self.metadata = metadata @@ -41,7 +39,6 @@ final class SessionEngine { self.isController = isController self.logger = logger self.topicInitializer = topicGenerator - self.sessionPayloadsJsonRpcHistory = sessionPayloadsJsonRpcHistory setUpWCRequestHandling() setupExpirationHandling() restoreSubscriptions() @@ -182,11 +179,6 @@ final class SessionEngine { let request = SessionType.PayloadParams.Request(method: params.method, params: params.params) let sessionPayloadParams = SessionType.PayloadParams(request: request, chainId: params.chainId) let sessionPayloadRequest = WCRequest(method: .sessionPayload, params: .sessionPayload(sessionPayloadParams)) - - - let recordRequest = JSONRPCRequest(id: sessionPayloadRequest.id, method: params.method, params: params.params) - try? sessionPayloadsJsonRpcHistory.set(topic: params.topic, request: recordRequest, chainId: params.chainId) - relayer.request(topic: params.topic, payload: sessionPayloadRequest) { [weak self] result in switch result { case .success(let response): @@ -427,8 +419,6 @@ final class SessionEngine { method: jsonRpcRequest.method, params: jsonRpcRequest.params, chainId: payloadParams.chainId) - - try? sessionPayloadsJsonRpcHistory.set(topic: topic, request: jsonRpcRequest, chainId: payloadParams.chainId) do { try validatePayload(request) onSessionPayloadRequest?(request) diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index 8620a67ad..8fc1be5c9 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -4,7 +4,7 @@ import WalletConnectUtils protocol JsonRpcHistoryRecording { func get(id: Int64) -> JsonRpcRecord? - func set(topic: String, request: WCRequest) throws + func set(topic: String, request: WCRequest, chainId: String) throws func delete(topic: String) func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord func exist(id: Int64) -> Bool @@ -25,12 +25,12 @@ class JsonRpcHistory: JsonRpcHistoryRecording { try? storage.get(key: getKey(for: id)) } - func set(topic: String, request: WCRequest) throws { + func set(topic: String, request: WCRequest, chainId: String) throws { guard !exist(id: request.id) else { throw WalletConnectError.internal(.jsonRpcDuplicateDetected) } logger.debug("Setting JSON-RPC request history record - ID: \(request.id)") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: request.params), response: nil) + let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: request.params), response: nil, chainId: chainId) try storage.set(record, forKey: getKey(for: request.id)) } @@ -64,4 +64,8 @@ class JsonRpcHistory: JsonRpcHistoryRecording { let prefix = "\(identifier).wc_json_rpc_record." return "\(prefix)\(id)" } + + public func getPending() -> [JsonRpcRecord] { + storage.getAll().filter{$0.response == nil} + } } diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift index 4bac1f787..f1461c5be 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift @@ -8,7 +8,8 @@ struct JsonRpcRecord: Codable { let topic: String let request: Request var response: JsonRpcResponseTypes? - + let chainId: String? + struct Request: Codable { let method: WCRequest.Method let params: WCRequest.Params diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 16eaee5da..886802593 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -60,7 +60,7 @@ class WalletConnectRelay: WalletConnectRelaying { func request(topic: String, payload: WCRequest, completion: @escaping ((Result, JSONRPCErrorResponse>)->())) { do { - try jsonRpcHistory.set(topic: topic, request: payload) + try jsonRpcHistory.set(topic: topic, request: payload, chainId: <#String#>) let message = try jsonRpcSerialiser.serialise(topic: topic, encodable: payload) networkRelayer.publish(topic: topic, payload: message) { [weak self] error in guard let self = self else {return} @@ -158,7 +158,7 @@ class WalletConnectRelay: WalletConnectRelaying { private func handleWCRequest(topic: String, request: WCRequest) { do { - try jsonRpcHistory.set(topic: topic, request: request) + try jsonRpcHistory.set(topic: topic, request: request, chainId: <#String#>) let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) wcRequestPublisherSubject.send(payload) } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index d61ae84b1..c09a9ee63 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -7,7 +7,6 @@ import UIKit #endif - enum StorageDomainIdentifiers { static func sessionPayloadsJsonRpcHistory(clientName: String) -> String { return "com.walletconnect.sdk.\(clientName).jsonRpcHistory.sessionPayloads" @@ -38,6 +37,7 @@ public final class WalletConnectClient { private let crypto: Crypto private let secureStorage: SecureStorage private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated) + private let history: JsonRpcHistory // MARK: - Initializers @@ -66,13 +66,13 @@ public final class WalletConnectClient { let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStore, uniqueIdentifier: clientName ?? "") let serialiser = JSONRPCSerialiser(crypto: crypto) - self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, uniqueIdentifier: clientName)) + self.history = JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, identifier: clientName) + self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: history) let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) - let sessionPayloadsJsonRpcHistoryIdentifier = StorageDomainIdentifiers.sessionPayloadsJsonRpcHistory(clientName: clientName ?? "") - let sessionPayloadsJsonRpcHistory = WalletConnectUtils.JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, identifier: sessionPayloadsJsonRpcHistoryIdentifier) - self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger, sessionPayloadsJsonRpcHistory: sessionPayloadsJsonRpcHistory) + + self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) setUpEnginesCallbacks() subscribeNotificationCenter() } @@ -232,9 +232,12 @@ public final class WalletConnectClient { } public func getPendingRequests() -> [Request] { - sessionEngine.sessionPayloadsJsonRpcHistory.getPending().compactMap { - return Request(id: $0.id, topic: $0.topic, method: $0.request.method, params: $0.request.params, chainId: $0.chainId) - } + history.getPending() + .filter{$0.request.method == "sessionPayload"} + .compactMap { + let payloadParams = try! $0.request.params.get(SessionType.PayloadParams.self) + return Request(id: $0.id, topic: $0.topic, method: payloadParams.request.method, params: payloadParams.request.params, chainId: payloadParams.chainId) + } } // MARK: - Private diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift index 33a1214ba..b0addc521 100644 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ b/Sources/WalletConnectUtils/JsonRpcHistory.swift @@ -4,13 +4,14 @@ import Foundation public class JsonRpcHistory where T: Codable&Equatable { enum RecordingError: Error { case jsonRpcDuplicateDetected + case noJsonRpcRequestMatchingResponse } private let storage: KeyValueStore private let logger: ConsoleLogging - public init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, identifier: String) { + public init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, identifier: String?) { self.logger = logger - self.storage = KeyValueStore(defaults: keyValueStorage, identifier: identifier) + self.storage = KeyValueStore(defaults: keyValueStorage, identifier: identifier ?? "") } public func get(id: Int64) -> JsonRpcRecord? { @@ -34,13 +35,17 @@ public class JsonRpcHistory where T: Codable&Equatable { } } - public func resolve(response: JsonRpcResponseTypes) throws { - guard var record = try? storage.get(key: "\(response.id)") else { return } + public func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord { + logger.debug("Resolving JSON-RPC response - ID: \(response.id)") + guard var record = try? storage.get(key: "\(response.id)") else { + throw RecordingError.noJsonRpcRequestMatchingResponse + } if record.response != nil { throw RecordingError.jsonRpcDuplicateDetected } else { record.response = response try storage.set(record, forKey: "\(record.id)") + return record } } diff --git a/Sources/WalletConnectUtils/KeyValueStore.swift b/Sources/WalletConnectUtils/KeyValueStore.swift index eb6b2bd22..cccbd1da5 100644 --- a/Sources/WalletConnectUtils/KeyValueStore.swift +++ b/Sources/WalletConnectUtils/KeyValueStore.swift @@ -12,11 +12,11 @@ public final class KeyValueStore where T: Codable { public func set(_ item: T, forKey key: String) throws { let encoded = try JSONEncoder().encode(item) - defaults.set(encoded, forKey: getContextPrefixKey(for: key)) + defaults.set(encoded, forKey: getContextPrefixedKey(for: key)) } public func get(key: String) throws -> T? { - guard let data = defaults.object(forKey: getContextPrefixKey(for: key)) as? Data else { return nil } + guard let data = defaults.object(forKey: getContextPrefixedKey(for: key)) as? Data else { return nil } let item = try JSONDecoder().decode(T.self, from: data) return item } @@ -33,10 +33,10 @@ public final class KeyValueStore where T: Codable { } public func delete(forKey key: String) { - defaults.removeObject(forKey: getContextPrefixKey(for: key)) + defaults.removeObject(forKey: getContextPrefixedKey(for: key)) } - private func getContextPrefixKey(for key: String) -> String { + private func getContextPrefixedKey(for key: String) -> String { return "\(prefix).\(key)" } } From f58a276da2ee937dc6ba2d60ec450bb03e91a590 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Jan 2022 11:03:07 +0100 Subject: [PATCH 05/16] remove session payloads history --- .../WalletConnect/JsonRpcHistory/JsonRpcHistory.swift | 4 ++-- Sources/WalletConnect/Relay/WalletConnectRelay.swift | 9 +++++++-- Sources/WalletConnect/WalletConnectClient.swift | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index 8fc1be5c9..83670d6b1 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -4,7 +4,7 @@ import WalletConnectUtils protocol JsonRpcHistoryRecording { func get(id: Int64) -> JsonRpcRecord? - func set(topic: String, request: WCRequest, chainId: String) throws + func set(topic: String, request: WCRequest, chainId: String?) throws func delete(topic: String) func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord func exist(id: Int64) -> Bool @@ -25,7 +25,7 @@ class JsonRpcHistory: JsonRpcHistoryRecording { try? storage.get(key: getKey(for: id)) } - func set(topic: String, request: WCRequest, chainId: String) throws { + func set(topic: String, request: WCRequest, chainId: String?) throws { guard !exist(id: request.id) else { throw WalletConnectError.internal(.jsonRpcDuplicateDetected) } diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 886802593..05325370a 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -60,7 +60,7 @@ class WalletConnectRelay: WalletConnectRelaying { func request(topic: String, payload: WCRequest, completion: @escaping ((Result, JSONRPCErrorResponse>)->())) { do { - try jsonRpcHistory.set(topic: topic, request: payload, chainId: <#String#>) + try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) let message = try jsonRpcSerialiser.serialise(topic: topic, encodable: payload) networkRelayer.publish(topic: topic, payload: message) { [weak self] error in guard let self = self else {return} @@ -158,7 +158,7 @@ class WalletConnectRelay: WalletConnectRelaying { private func handleWCRequest(topic: String, request: WCRequest) { do { - try jsonRpcHistory.set(topic: topic, request: request, chainId: <#String#>) + try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) wcRequestPublisherSubject.send(payload) } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { @@ -197,4 +197,9 @@ class WalletConnectRelay: WalletConnectRelaying { logger.info("Info: \(error.localizedDescription)") } } + + func getChainId(_ request: WCRequest) -> String? { + guard case let .sessionPayload(payload) = request.params else {return nil} + return payload.chainId + } } diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index c09a9ee63..beb97c794 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -66,7 +66,7 @@ public final class WalletConnectClient { let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStore, uniqueIdentifier: clientName ?? "") let serialiser = JSONRPCSerialiser(crypto: crypto) - self.history = JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, identifier: clientName) + self.history = JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, uniqueIdentifier: clientName) self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: history) let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) @@ -233,10 +233,10 @@ public final class WalletConnectClient { public func getPendingRequests() -> [Request] { history.getPending() - .filter{$0.request.method == "sessionPayload"} + .filter{$0.request.method == .sessionPayload} .compactMap { - let payloadParams = try! $0.request.params.get(SessionType.PayloadParams.self) - return Request(id: $0.id, topic: $0.topic, method: payloadParams.request.method, params: payloadParams.request.params, chainId: payloadParams.chainId) + guard case let .sessionPayload(payloadRequest) = $0.request.params else {return nil} + return Request(id: $0.id, topic: $0.topic, method: payloadRequest.request.method, params: payloadRequest.request.params, chainId: payloadRequest.chainId) } } From f7513609e5cdec89d05a020f200c3150a02e4fdc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Jan 2022 11:59:15 +0100 Subject: [PATCH 06/16] add pending requests handling in example app --- Example/ExampleApp.xcodeproj/project.pbxproj | 8 +++ .../Responder/EthereumTransaction.swift | 17 +++++ .../Responder/ResponderViewController.swift | 71 ++++--------------- Example/ExampleApp/Responder/Signer.swift | 48 +++++++++++++ .../SessionDetailsViewController.swift | 38 +++++++++- 5 files changed, 122 insertions(+), 60 deletions(-) create mode 100644 Example/ExampleApp/Responder/EthereumTransaction.swift create mode 100644 Example/ExampleApp/Responder/Signer.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 7a51e3286..905a712dc 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -28,6 +28,8 @@ 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 8460DD002750D6F50081F94C /* SessionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */; }; 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DD012750D7020081F94C /* SessionDetailsView.swift */; }; + 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; + 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,6 +68,8 @@ 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsViewController.swift; sourceTree = ""; }; 8460DD012750D7020081F94C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -92,6 +96,8 @@ isa = PBXGroup; children = ( 761C649B26FB7B7F004239D1 /* ResponderViewController.swift */, + 84F568C32795832A00D0A289 /* EthereumTransaction.swift */, + 84F568C1279582D200D0A289 /* Signer.swift */, 84494387278D9C1B00CC26BB /* UIAlertController.swift */, 76744CF426FDFB6B00B77ED9 /* ResponderView.swift */, 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */, @@ -309,10 +315,12 @@ 761C649E26FB7FD7004239D1 /* SessionViewController.swift in Sources */, 76744CF726FE4D5400B77ED9 /* ActiveSessionItem.swift in Sources */, 764E1D4226F8D3FC00A1FB15 /* SceneDelegate.swift in Sources */, + 84F568C2279582D200D0A289 /* Signer.swift in Sources */, 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */, 7603D74D2703429A00DD27A2 /* ProposerView.swift in Sources */, 764E1D5A26F8DF1B00A1FB15 /* ScannerViewController.swift in Sources */, 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */, + 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/ExampleApp/Responder/EthereumTransaction.swift b/Example/ExampleApp/Responder/EthereumTransaction.swift new file mode 100644 index 000000000..a19bbaae6 --- /dev/null +++ b/Example/ExampleApp/Responder/EthereumTransaction.swift @@ -0,0 +1,17 @@ + +import Foundation +import Web3 + +extension EthereumTransaction { +var description: String { + return """ + from: \(String(describing: from!.hex(eip55: true))) + to: \(String(describing: to!.hex(eip55: true))), + value: \(String(describing: value!.hex())), + gasPrice: \(String(describing: gasPrice?.hex())), + gas: \(String(describing: gas?.hex())), + data: \(data.hex()), + nonce: \(String(describing: nonce?.hex())) + """ +} +} diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index 629938d40..f96343387 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -20,10 +20,9 @@ final class ResponderViewController: UIViewController { clientName: "responder" ) }() - lazy var account = privateKey.address.hex(eip55: true) + lazy var account = Signer.privateKey.address.hex(eip55: true) var sessionItems: [ActiveSessionItem] = [] var currentProposal: Session.Proposal? - let privateKey: EthereumPrivateKey = try! EthereumPrivateKey(hexPrivateKey: "0xe56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") private let responderView: ResponderView = { ResponderView() @@ -76,16 +75,25 @@ final class ResponderViewController: UIViewController { private func showSessionRequest(_ sessionRequest: Request) { let requestVC = RequestViewController(sessionRequest) requestVC.onSign = { [unowned self] in - let result = signEth(request: sessionRequest) + let result = Signer.signEth(request: sessionRequest) let response = JSONRPCResponse(id: sessionRequest.id!, result: result) client.respond(topic: sessionRequest.topic, response: .response(response)) + reloadSessionDetailsIfNeeded() } - requestVC.onReject = { [weak self] in - self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + requestVC.onReject = { [unowned self] in + client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + reloadSessionDetailsIfNeeded() } + reloadSessionDetailsIfNeeded() present(requestVC, animated: true) } + func reloadSessionDetailsIfNeeded() { + if let sessionDetailsViewController = navigationController?.viewControllers.first(where: {$0 is SessionDetailsViewController}) as? SessionDetailsViewController { + sessionDetailsViewController.reloadTable() + } + } + private func pairClient(uri: String) { print("[RESPONDER] Pairing to: \(uri)") do { @@ -221,57 +229,4 @@ extension ResponderViewController: WalletConnectClientDelegate { self.responderView.tableView.reloadData() } } - - func signEth(request: Request) -> AnyCodable { - let method = request.method - if method == "personal_sign" { - let params = try! request.params.get([String].self) - let messageToSign = params[0] - let signHash = signHash(messageToSign) - let (v, r, s) = try! self.privateKey.sign(hash: signHash) - let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) - return AnyCodable(result) - } else if method == "eth_signTypedData" { - let params = try! request.params.get([String].self) - print(params) - let messageToSign = params[1] - let signHash = signHash(messageToSign) - let (v, r, s) = try! self.privateKey.sign(hash: signHash) - let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) - return AnyCodable(result) - } else if method == "eth_sendTransaction" { - let params = try! request.params.get([EthereumTransaction].self) - var transaction = params[0] - transaction.gas = EthereumQuantity(quantity: BigUInt("1234")) - print(transaction.description) - let signedTx = try! transaction.sign(with: self.privateKey, chainId: 4) - let (r, s, v) = (signedTx.r, signedTx.s, signedTx.v) - let result = r.hex() + s.hex().dropFirst(2) + String(v.quantity, radix: 16) - return AnyCodable(result) - } - fatalError("not implemented") - } - - func signHash(_ message: String) -> Bytes { - let prefix = "\u{19}Ethereum Signed Message:\n" - let messageData = Data(hex: message) - let prefixData = (prefix + String(messageData.count)).data(using: .utf8)! - let prefixedMessageData = prefixData + messageData - let dataToHash: Bytes = .init(hex: prefixedMessageData.toHexString()) - return SHA3(variant: .keccak256).calculate(for: dataToHash) - } -} - -extension EthereumTransaction { - var description: String { - return """ - from: \(String(describing: from!.hex(eip55: true))) - to: \(String(describing: to!.hex(eip55: true))), - value: \(String(describing: value!.hex())), - gasPrice: \(String(describing: gasPrice?.hex())), - gas: \(String(describing: gas?.hex())), - data: \(data.hex()), - nonce: \(String(describing: nonce?.hex())) - """ - } } diff --git a/Example/ExampleApp/Responder/Signer.swift b/Example/ExampleApp/Responder/Signer.swift new file mode 100644 index 000000000..1e53bc75d --- /dev/null +++ b/Example/ExampleApp/Responder/Signer.swift @@ -0,0 +1,48 @@ +import Web3 +import Foundation +import WalletConnectUtils +import WalletConnect +import CryptoSwift + +class Signer { + static let privateKey: EthereumPrivateKey = try! EthereumPrivateKey(hexPrivateKey: "0xe56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") + private init(){} + static func signEth(request: Request) -> AnyCodable { + let method = request.method + if method == "personal_sign" { + let params = try! request.params.get([String].self) + let messageToSign = params[0] + let signHash = signHash(messageToSign) + let (v, r, s) = try! self.privateKey.sign(hash: signHash) + let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) + return AnyCodable(result) + } else if method == "eth_signTypedData" { + let params = try! request.params.get([String].self) + print(params) + let messageToSign = params[1] + let signHash = signHash(messageToSign) + let (v, r, s) = try! self.privateKey.sign(hash: signHash) + let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) + return AnyCodable(result) + } else if method == "eth_sendTransaction" { + let params = try! request.params.get([EthereumTransaction].self) + var transaction = params[0] + transaction.gas = EthereumQuantity(quantity: BigUInt("1234")) + print(transaction.description) + let signedTx = try! transaction.sign(with: self.privateKey, chainId: 4) + let (r, s, v) = (signedTx.r, signedTx.s, signedTx.v) + let result = r.hex() + s.hex().dropFirst(2) + String(v.quantity, radix: 16) + return AnyCodable(result) + } + fatalError("not implemented") + } + + private static func signHash(_ message: String) -> Bytes { + let prefix = "\u{19}Ethereum Signed Message:\n" + let messageData = Data(hex: message) + let prefixData = (prefix + String(messageData.count)).data(using: .utf8)! + let prefixedMessageData = prefixData + messageData + let dataToHash: Bytes = .init(hex: prefixedMessageData.toHexString()) + return SHA3(variant: .keccak256).calculate(for: dataToHash) + } +} diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index 013a81e97..d05311aec 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -1,12 +1,12 @@ import UIKit import WalletConnect +import WalletConnectUtils final class SessionDetailsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { - private let sessiondetailsView = { SessionDetailsView() }() - private let sessionInfo: SessionInfo + private var sessionInfo: SessionInfo private let client: WalletConnectClient private let session: Session init(_ session: Session, _ client: WalletConnectClient) { @@ -96,4 +96,38 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, return "Pending Requests" } } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if indexPath.section == 2 { + let pendingRequests = client.getPendingRequests() + showSessionRequest(pendingRequests[indexPath.row]) + } + } + + private func showSessionRequest(_ sessionRequest: Request) { + let requestVC = RequestViewController(sessionRequest) + requestVC.onSign = { [unowned self] in + let result = Signer.signEth(request: sessionRequest) + let response = JSONRPCResponse(id: sessionRequest.id!, result: result) + client.respond(topic: sessionRequest.topic, response: .response(response)) + reloadTable() + } + requestVC.onReject = { [unowned self] in + client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + reloadTable() + } + present(requestVC, animated: true) + } + + func reloadTable() { + let pendingRequests = client.getPendingRequests().map{$0.method} + self.sessionInfo = SessionInfo(name: session.peer.name ?? "", + descriptionText: session.peer.description ?? "", + dappURL: session.peer.description ?? "", + iconURL: session.peer.icons?.first ?? "", + chains: Array(session.permissions.blockchains), + methods: Array(session.permissions.methods), + pendingRequests: pendingRequests) + sessiondetailsView.tableView.reloadData() + } } From e4c26dd3da3fde0f9f200e778226e8cc87a44ad0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Jan 2022 13:17:22 +0100 Subject: [PATCH 07/16] Fix - not passing request id when transforming models --- .../Responder/ResponderViewController.swift | 4 ++-- .../SessionDetailsViewController.swift | 4 ++-- .../WalletConnect/Engine/SessionEngine.swift | 2 +- .../JsonRpcHistory/JsonRpcHistory.swift | 2 +- Sources/WalletConnect/Request.swift | 22 ++++++++++++++++++- .../JsonRpcHistoryTests.swift | 4 ++-- 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index f96343387..c5049becd 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -76,12 +76,12 @@ final class ResponderViewController: UIViewController { let requestVC = RequestViewController(sessionRequest) requestVC.onSign = { [unowned self] in let result = Signer.signEth(request: sessionRequest) - let response = JSONRPCResponse(id: sessionRequest.id!, result: result) + let response = JSONRPCResponse(id: sessionRequest.id, result: result) client.respond(topic: sessionRequest.topic, response: .response(response)) reloadSessionDetailsIfNeeded() } requestVC.onReject = { [unowned self] in - client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) reloadSessionDetailsIfNeeded() } reloadSessionDetailsIfNeeded() diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index d05311aec..688cc3c87 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -108,12 +108,12 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, let requestVC = RequestViewController(sessionRequest) requestVC.onSign = { [unowned self] in let result = Signer.signEth(request: sessionRequest) - let response = JSONRPCResponse(id: sessionRequest.id!, result: result) + let response = JSONRPCResponse(id: sessionRequest.id, result: result) client.respond(topic: sessionRequest.topic, response: .response(response)) reloadTable() } requestVC.onReject = { [unowned self] in - client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id!, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) + client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) reloadTable() } present(requestVC, animated: true) diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index a627bad9d..9dc0c4f14 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -178,7 +178,7 @@ final class SessionEngine { } let request = SessionType.PayloadParams.Request(method: params.method, params: params.params) let sessionPayloadParams = SessionType.PayloadParams(request: request, chainId: params.chainId) - let sessionPayloadRequest = WCRequest(method: .sessionPayload, params: .sessionPayload(sessionPayloadParams)) + let sessionPayloadRequest = WCRequest(id: params.id, method: .sessionPayload, params: .sessionPayload(sessionPayloadParams)) relayer.request(topic: params.topic, payload: sessionPayloadRequest) { [weak self] result in switch result { case .success(let response): diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index 83670d6b1..d074e340e 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -25,7 +25,7 @@ class JsonRpcHistory: JsonRpcHistoryRecording { try? storage.get(key: getKey(for: id)) } - func set(topic: String, request: WCRequest, chainId: String?) throws { + func set(topic: String, request: WCRequest, chainId: String? = nil) throws { guard !exist(id: request.id) else { throw WalletConnectError.internal(.jsonRpcDuplicateDetected) } diff --git a/Sources/WalletConnect/Request.swift b/Sources/WalletConnect/Request.swift index d70bcd752..e55b13497 100644 --- a/Sources/WalletConnect/Request.swift +++ b/Sources/WalletConnect/Request.swift @@ -2,9 +2,29 @@ import Foundation import WalletConnectUtils public struct Request: Codable, Equatable { - public internal(set) var id: Int64? + public let id: Int64 public let topic: String public let method: String public let params: AnyCodable public let chainId: String? + + internal init(id: Int64, topic: String, method: String, params: AnyCodable, chainId: String?) { + self.id = id + self.topic = topic + self.method = method + self.params = params + self.chainId = chainId + } + + public init(topic: String, method: String, params: AnyCodable, chainId: String?) { + self.id = Self.generateId() + self.topic = topic + self.method = method + self.params = params + self.chainId = chainId + } + + public static func generateId() -> Int64 { + return Int64(Date().timeIntervalSince1970 * 1000)*1000 + Int64.random(in: 0..<1000) + } } diff --git a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift index 0a5798077..4657259d3 100644 --- a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift +++ b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift @@ -37,7 +37,7 @@ final class JsonRpcHistoryTests: XCTestCase { XCTAssertNil(sut.get(id: recordinput.request.id)?.response) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) let response = JsonRpcResponseTypes.response(jsonRpcResponse) - try! sut.resolve(response: response) + _ = try! sut.resolve(response: response) XCTAssertNotNil(sut.get(id: jsonRpcResponse.id)?.response) } @@ -46,7 +46,7 @@ final class JsonRpcHistoryTests: XCTestCase { try! sut.set(topic: recordinput.topic, request: recordinput.request) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) let response = JsonRpcResponseTypes.response(jsonRpcResponse) - try! sut.resolve(response: response) + _ = try! sut.resolve(response: response) XCTAssertThrowsError(try sut.resolve(response: response)) } From 21a3fd8571cdeb891139d0f8bf35563ab1250e75 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Jan 2022 14:14:01 +0100 Subject: [PATCH 08/16] update history tests --- .../JsonRpcHistoryTests.swift | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift index 4657259d3..6f0e282cd 100644 --- a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift +++ b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift @@ -18,21 +18,21 @@ final class JsonRpcHistoryTests: XCTestCase { } func testSetRecord() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() XCTAssertFalse(sut.exist(id: recordinput.request.id)) try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertTrue(sut.exist(id: recordinput.request.id)) } func testGetRecord() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() XCTAssertNil(sut.get(id: recordinput.request.id)) try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertNotNil(sut.get(id: recordinput.request.id)) } func testResolve() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertNil(sut.get(id: recordinput.request.id)?.response) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) @@ -42,7 +42,7 @@ final class JsonRpcHistoryTests: XCTestCase { } func testThrowsOnResolveDuplicate() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() try! sut.set(topic: recordinput.topic, request: recordinput.request) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) let response = JsonRpcResponseTypes.response(jsonRpcResponse) @@ -51,21 +51,44 @@ final class JsonRpcHistoryTests: XCTestCase { } func testThrowsOnSetDuplicate() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertThrowsError(try sut.set(topic: recordinput.topic, request: recordinput.request)) } func testDelete() { - let recordinput = testJsonRpcRecordInput + let recordinput = getTestJsonRpcRecordInput() try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertNotNil(sut.get(id: recordinput.request.id)) sut.delete(topic: testTopic) XCTAssertNil(sut.get(id: recordinput.request.id)) } + + func testGetPending() { + let recordinput1 = getTestJsonRpcRecordInput(id: 1) + let recordinput2 = getTestJsonRpcRecordInput(id: 2) + try! sut.set(topic: recordinput1.topic, request: recordinput1.request) + try! sut.set(topic: recordinput2.topic, request: recordinput2.request) + XCTAssertEqual(sut.getPending().count, 2) + let jsonRpcResponse = JSONRPCResponse(id: recordinput1.request.id, result: AnyCodable("")) + let response = JsonRpcResponseTypes.response(jsonRpcResponse) + _ = try! sut.resolve(response: response) + XCTAssertEqual(sut.getPending().count, 1) + } } private let testTopic = "test_topic" -private var testJsonRpcRecordInput: (topic: String, request: WCRequest) { - return (topic: testTopic, request: SerialiserTestData.pairingApproveJSONRPCRequest) +private func getTestJsonRpcRecordInput(id: Int64 = 0) -> (topic: String, request: WCRequest) { + let request = WCRequest(id: id, + jsonrpc: "2.0", + method: WCRequest.Method.pairingApprove, + params: WCRequest.Params.pairingApprove( + PairingType.ApprovalParams(relay: RelayProtocolOptions(protocol: "waku", + params: nil), responder: PairingParticipant(publicKey: "be9225978b6287a02d259ee0d9d1bcb683082d8386b7fb14b58ac95b93b2ef43"), + expiry: 1632742217, + state: PairingState(metadata: AppMetadata(name: "iOS", + description: nil, + url: nil, + icons: nil))))) + return (topic: testTopic, request: request) } From 1986190260e439b6e4b04f41dab5bb5467a01d3e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 11:03:06 +0100 Subject: [PATCH 09/16] add storage domains --- Sources/Relayer/WakuNetworkRelay.swift | 4 ++-- .../JsonRpcHistory/JsonRpcHistory.swift | 23 +++++++------------ .../WalletConnect/Storage/SequenceStore.swift | 4 ++-- .../StorageDomainIdentifiers.swift | 14 +++++++++++ .../WalletConnect/WalletConnectClient.swift | 19 +++++---------- .../WalletConnectUtils/JsonRpcHistory.swift | 4 ++-- 6 files changed, 34 insertions(+), 34 deletions(-) create mode 100644 Sources/WalletConnect/StorageDomainIdentifiers.swift diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 41fec1102..8e1eb99fc 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -36,8 +36,8 @@ public final class WakuNetworkRelay { uniqueIdentifier: String) { self.logger = logger self.dispatcher = dispatcher - let historyIdentifier = "\(uniqueIdentifier).relayer.subscription_json_rpc_record" - self.jsonRpcSubscriptionsHistory = JsonRpcHistory(logger: logger, keyValueStorage: keyValueStorage, identifier: historyIdentifier) + let historyIdentifier = "com.walletconnect.sdk.\(uniqueIdentifier).relayer.subscription_json_rpc_record" + self.jsonRpcSubscriptionsHistory = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: historyIdentifier)) setUpBindings() } diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index d074e340e..9909dccd2 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -13,16 +13,14 @@ protocol JsonRpcHistoryRecording { class JsonRpcHistory: JsonRpcHistoryRecording { let storage: KeyValueStore let logger: ConsoleLogging - let identifier: String - init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, uniqueIdentifier: String? = nil) { + init(logger: ConsoleLogging, keyValueStore: KeyValueStore) { self.logger = logger - self.storage = KeyValueStore(defaults: keyValueStorage, identifier: "") - self.identifier = "com.walletconnect.sdk.\(uniqueIdentifier ?? "")" + self.storage = keyValueStore } func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: getKey(for: id)) + try? storage.get(key: "\(id)") } func set(topic: String, request: WCRequest, chainId: String? = nil) throws { @@ -31,38 +29,33 @@ class JsonRpcHistory: JsonRpcHistoryRecording { } logger.debug("Setting JSON-RPC request history record - ID: \(request.id)") let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: request.params), response: nil, chainId: chainId) - try storage.set(record, forKey: getKey(for: request.id)) + try storage.set(record, forKey: "\(request.id)") } func delete(topic: String) { storage.getAll().forEach { record in if record.topic == topic { - storage.delete(forKey: getKey(for: record.id)) + storage.delete(forKey: "\(record.id)") } } } func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord { logger.debug("Resolving JSON-RPC response - ID: \(response.id)") - guard var record = try? storage.get(key: getKey(for: response.id)) else { + guard var record = try? storage.get(key: "\(response.id)") else { throw WalletConnectError.internal(.noJsonRpcRequestMatchingResponse) } if record.response != nil { throw WalletConnectError.internal(.jsonRpcDuplicateDetected) } else { record.response = response - try storage.set(record, forKey: getKey(for: record.id)) + try storage.set(record, forKey: "\(record.id)") return record } } func exist(id: Int64) -> Bool { - return (try? storage.get(key: getKey(for: id))) != nil - } - - private func getKey(for id: Int64) -> String { - let prefix = "\(identifier).wc_json_rpc_record." - return "\(prefix)\(id)" + return (try? storage.get(key: "\(id)")) != nil } public func getPending() -> [JsonRpcRecord] { diff --git a/Sources/WalletConnect/Storage/SequenceStore.swift b/Sources/WalletConnect/Storage/SequenceStore.swift index c8eeec60c..d4ff6fc39 100644 --- a/Sources/WalletConnect/Storage/SequenceStore.swift +++ b/Sources/WalletConnect/Storage/SequenceStore.swift @@ -18,10 +18,10 @@ final class SequenceStore where T: ExpirableSequence { private let dateInitializer: () -> Date private let identifier: String - init(storage: KeyValueStorage, uniqueIdentifier: String? = nil, dateInitializer: @escaping () -> Date = Date.init) { + init(storage: KeyValueStorage, identifier: String, dateInitializer: @escaping () -> Date = Date.init) { self.storage = storage self.dateInitializer = dateInitializer - self.identifier = "com.walletconnect.sdk.\(uniqueIdentifier ?? "")" + self.identifier = identifier } func hasSequence(forTopic topic: String) -> Bool { diff --git a/Sources/WalletConnect/StorageDomainIdentifiers.swift b/Sources/WalletConnect/StorageDomainIdentifiers.swift new file mode 100644 index 000000000..fa2bd7ecc --- /dev/null +++ b/Sources/WalletConnect/StorageDomainIdentifiers.swift @@ -0,0 +1,14 @@ + +import Foundation + +enum StorageDomainIdentifiers { + static func jsonRpcHistory(clientName: String) -> String { + return "com.walletconnect.sdk.\(clientName).wc_jsonRpcHistoryRecord" + } + static func pairings(clientName: String) -> String { + return "com.walletconnect.sdk.\(clientName).pairingSequences" + } + static func sessions(clientName: String) -> String { + return "com.walletconnect.sdk.\(clientName).sessionSequences" + } +} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index beb97c794..9038789ad 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -6,13 +6,6 @@ import WalletConnectUtils import UIKit #endif - -enum StorageDomainIdentifiers { - static func sessionPayloadsJsonRpcHistory(clientName: String) -> String { - return "com.walletconnect.sdk.\(clientName).jsonRpcHistory.sessionPayloads" - } -} - /// An Object that expose public API to provide interactions with WalletConnect SDK /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialise it. Usually only one instance of a client is required in the application. @@ -53,10 +46,10 @@ public final class WalletConnectClient { /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialise it. Usually only one instance of a client is required in the application. public convenience init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, keyValueStorage: KeyValueStorage = UserDefaults.standard, clientName: String? = nil) { - self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStore: keyValueStorage, clientName: clientName) + self.init(metadata: metadata, projectId: projectId, isController: isController, relayHost: relayHost, logger: ConsoleLogger(loggingLevel: .off), keychain: KeychainStorage(uniqueIdentifier: clientName), keyValueStorage: keyValueStorage, clientName: clientName) } - init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStore: KeyValueStorage, clientName: String? = nil) { + init(metadata: AppMetadata, projectId: String, isController: Bool, relayHost: String, logger: ConsoleLogging, keychain: KeychainStorage, keyValueStorage: KeyValueStorage, clientName: String? = nil) { self.metadata = metadata self.isController = isController self.logger = logger @@ -64,12 +57,12 @@ public final class WalletConnectClient { self.crypto = Crypto(keychain: keychain) self.secureStorage = SecureStorage(keychain: keychain) let relayUrl = WakuNetworkRelay.makeRelayUrl(host: relayHost, projectId: projectId) - self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStore, uniqueIdentifier: clientName ?? "") + self.wakuRelay = WakuNetworkRelay(logger: logger, url: relayUrl, keyValueStorage: keyValueStorage, uniqueIdentifier: clientName ?? "") let serialiser = JSONRPCSerialiser(crypto: crypto) - self.history = JsonRpcHistory(logger: logger, keyValueStorage: keyValueStore, uniqueIdentifier: clientName) + self.history = JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory(clientName: clientName ?? "_"))) self.relay = WalletConnectRelay(networkRelayer: wakuRelay, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: history) - let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) - let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStore, uniqueIdentifier: clientName)) + let pairingSequencesStore = PairingStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.pairings(clientName: clientName ?? "_"))) + let sessionSequencesStore = SessionStorage(storage: SequenceStore(storage: keyValueStorage, identifier: StorageDomainIdentifiers.sessions(clientName: clientName ?? "_"))) self.pairingEngine = PairingEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: pairingSequencesStore, isController: isController, metadata: metadata, logger: logger) self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift index b0addc521..bca7270a0 100644 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ b/Sources/WalletConnectUtils/JsonRpcHistory.swift @@ -9,9 +9,9 @@ public class JsonRpcHistory where T: Codable&Equatable { private let storage: KeyValueStore private let logger: ConsoleLogging - public init(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, identifier: String?) { + public init(logger: ConsoleLogging, keyValueStore: KeyValueStore) { self.logger = logger - self.storage = KeyValueStore(defaults: keyValueStorage, identifier: identifier ?? "") + self.storage = keyValueStore } public func get(id: Int64) -> JsonRpcRecord? { From 80b158a61515921aef04c04334659c68fa1ea0cf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 11:09:34 +0100 Subject: [PATCH 10/16] update swift.yml --- .github/workflows/swift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 9d9c23300..d47aa4129 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -2,9 +2,9 @@ name: Swift on: push: - branches: [ main ] + branches: [ main, develop ] pull_request: - branches: [ main ] + branches: [ main, develop ] jobs: build: From 9c1c450e766c079667bc5864182465b4d2e3a5d0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 11:49:16 +0100 Subject: [PATCH 11/16] fix tests --- Example/ExampleApp.xcodeproj/project.pbxproj | 4 ++-- Tests/IntegrationTests/ClientTest.swift | 2 +- Tests/WalletConnectTests/JsonRpcHistoryTests.swift | 2 +- Tests/WalletConnectTests/SequenceStoreTests.swift | 2 +- Tests/WalletConnectTests/WCRelayTests.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 905a712dc..dc0b3c745 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -477,7 +477,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -501,7 +501,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index 7a6cb0c7f..934212c98 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -34,7 +34,7 @@ final class ClientTests: XCTestCase { relayHost: relayHost, logger: logger, keychain: KeychainStorage(keychainService: KeychainServiceFake()), - keyValueStore: RuntimeKeyValueStorage()) + keyValueStorage: RuntimeKeyValueStorage()) return ClientDelegate(client: client) } diff --git a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift index 6f0e282cd..c807c1b46 100644 --- a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift +++ b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift @@ -10,7 +10,7 @@ final class JsonRpcHistoryTests: XCTestCase { var sut: WalletConnect.JsonRpcHistory! override func setUp() { - sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStorage: RuntimeKeyValueStorage()) + sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStore: KeyValueStore(defaults: RuntimeKeyValueStorage(), identifier: "")) } override func tearDown() { diff --git a/Tests/WalletConnectTests/SequenceStoreTests.swift b/Tests/WalletConnectTests/SequenceStoreTests.swift index 996e5e7ed..e37a2cf4e 100644 --- a/Tests/WalletConnectTests/SequenceStoreTests.swift +++ b/Tests/WalletConnectTests/SequenceStoreTests.swift @@ -21,7 +21,7 @@ final class SequenceStoreTests: XCTestCase { override func setUp() { timeTraveler = TimeTraveler() storageFake = RuntimeKeyValueStorage() - sut = SequenceStore(storage: storageFake, dateInitializer: timeTraveler.generateDate) + sut = SequenceStore(storage: storageFake, identifier: "", dateInitializer: timeTraveler.generateDate) sut.onSequenceExpiration = { _, _ in XCTFail("Unexpected expiration call") } diff --git a/Tests/WalletConnectTests/WCRelayTests.swift b/Tests/WalletConnectTests/WCRelayTests.swift index 4e266b44c..37ead33db 100644 --- a/Tests/WalletConnectTests/WCRelayTests.swift +++ b/Tests/WalletConnectTests/WCRelayTests.swift @@ -18,7 +18,7 @@ class WalletConnectRelayTests: XCTestCase { let logger = ConsoleLoggerMock() serialiser = MockedJSONRPCSerialiser() networkRelayer = MockedNetworkRelayer() - wcRelay = WalletConnectRelay(networkRelayer: networkRelayer, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: JsonRpcHistory(logger: logger, keyValueStorage: RuntimeKeyValueStorage())) + wcRelay = WalletConnectRelay(networkRelayer: networkRelayer, jsonRpcSerialiser: serialiser, logger: logger, jsonRpcHistory: JsonRpcHistory(logger: logger, keyValueStore: KeyValueStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) } override func tearDown() { From 2beea4268cf29611078561cf40a28d78b4e9511b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 13:31:25 +0100 Subject: [PATCH 12/16] update failing test to new client interface --- Tests/IntegrationTests/ClientTest.swift | 59 ++++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index 934212c98..cb56eff3c 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -78,37 +78,34 @@ final class ClientTests: XCTestCase { waitForExpectations(timeout: defaultTimeout, handler: nil) } - // FIXME: Broken test!! -// func testNewSessionOnExistingPairing() { -// let proposerSettlesSessionExpectation = expectation(description: "Proposer settles session") -// proposerSettlesSessionExpectation.expectedFulfillmentCount = 2 -// let responderSettlesSessionExpectation = expectation(description: "Responder settles session") -// responderSettlesSessionExpectation.expectedFulfillmentCount = 2 -// var pairingTopic: String! -// var initiatedSecondSession = false -// let permissions = SessionType.Permissions(blockchain: SessionType.Blockchain(chains: []), jsonrpc: SessionType.JSONRPC(methods: [])) -// let connectParams = ConnectParams(permissions: permissions) -// let uri = try! proposer.client.connect(params: connectParams)! -// try! responder.client.pair(uri: uri) -// proposer.onPairingSettled = { pairing in -// pairingTopic = pairing.topic -// } -// responder.onSessionProposal = { [unowned self] proposal in -// self.responder.client.approve(proposal: proposal, accounts: []){_ in} -// } -// responder.onSessionSettled = { sessionSettled in -// responderSettlesSessionExpectation.fulfill() -// } -// proposer.onSessionSettled = { [unowned self] sessionSettled in -// proposerSettlesSessionExpectation.fulfill() -// if !initiatedSecondSession { -// let params = ConnectParams(permissions: SessionType.Permissions(blockchain: SessionType.Blockchain(chains: []), jsonrpc: SessionType.JSONRPC(methods: [])), topic: pairingTopic) -// let _ = try! proposer.client.connect(params: params) -// initiatedSecondSession = true -// } -// } -// waitForExpectations(timeout: defaultTimeout, handler: nil) -// } + func testNewSessionOnExistingPairing() { + let proposerSettlesSessionExpectation = expectation(description: "Proposer settles session") + proposerSettlesSessionExpectation.expectedFulfillmentCount = 2 + let responderSettlesSessionExpectation = expectation(description: "Responder settles session") + responderSettlesSessionExpectation.expectedFulfillmentCount = 2 + var pairingTopic: String! + var initiatedSecondSession = false + let permissions = Session.Permissions.stub() + let uri = try! proposer.client.connect(sessionPermissions: permissions, topic: nil)! + try! responder.client.pair(uri: uri) + proposer.onPairingSettled = { pairing in + pairingTopic = pairing.topic + } + responder.onSessionProposal = { [unowned self] proposal in + responder.client.approve(proposal: proposal, accounts: []) + } + responder.onSessionSettled = { sessionSettled in + responderSettlesSessionExpectation.fulfill() + } + proposer.onSessionSettled = { [unowned self] sessionSettled in + proposerSettlesSessionExpectation.fulfill() + if !initiatedSecondSession { + let _ = try! proposer.client.connect(sessionPermissions: permissions, topic: pairingTopic) + initiatedSecondSession = true + } + } + waitForExpectations(timeout: defaultTimeout, handler: nil) + } func testResponderRejectsSession() { let sessionRejectExpectation = expectation(description: "Proposer is notified on session rejection") From 6694755e3cce166964333148bcd88c616466653a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 14:41:08 +0100 Subject: [PATCH 13/16] fix for failing propose on pairing test --- Sources/Relayer/WakuNetworkRelay.swift | 6 +++--- Sources/WalletConnect/Engine/SessionEngine.swift | 2 +- Sources/WalletConnect/Relay/WalletConnectRelay.swift | 4 ++++ Tests/IntegrationTests/ClientTest.swift | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 8e1eb99fc..6d2975c98 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -120,7 +120,7 @@ public final class WakuNetworkRelay { let request = JSONRPCRequest(method: RelayJSONRPC.Method.unsubscribe.rawValue, params: params) let requestJson = try! request.json() var cancellable: AnyCancellable? - jsonRpcSubscriptionsHistory.delete(topic: topic) +// jsonRpcSubscriptionsHistory.delete(topic: topic) dispatcher.send(requestJson) { [weak self] error in if let error = error { self?.logger.debug("Failed to Unsubscribe on Topic") @@ -155,7 +155,7 @@ public final class WakuNetworkRelay { if let request = tryDecode(SubscriptionRequest.self, from: payload), request.method == RelayJSONRPC.Method.subscription.rawValue { do { - try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) +// try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) onMessage?(request.params.data.topic, request.params.data.message) acknowledgeSubscription(requestId: request.id) } catch { @@ -184,7 +184,7 @@ public final class WakuNetworkRelay { private func acknowledgeSubscription(requestId: Int64) { let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) let responseJson = try! response.json() - try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResponseTypes.response(response)) +// try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResponseTypes.response(response)) dispatcher.send(responseJson) { [weak self] error in if let error = error { self?.logger.debug("Failed to Respond for request id: \(requestId), error: \(error)") diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 9dc0c4f14..5fd91a115 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -489,10 +489,10 @@ final class SessionEngine { permissions: Session.Permissions( blockchains: pendingSession.proposal.permissions.blockchain.chains, methods: pendingSession.proposal.permissions.jsonrpc.methods)) - onSessionApproved?(approvedSession) let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + onSessionApproved?(approvedSession) if let error = error { logger.error(error) } diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 05325370a..3bccd3d0e 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -62,6 +62,7 @@ class WalletConnectRelay: WalletConnectRelaying { do { try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) let message = try jsonRpcSerialiser.serialise(topic: topic, encodable: payload) + logger.info("‼️‼️request id: \(payload.id) method: \(payload.method)") networkRelayer.publish(topic: topic, payload: message) { [weak self] error in guard let self = self else {return} if let error = error { @@ -180,6 +181,7 @@ class WalletConnectRelay: WalletConnectRelaying { onResponse?(wcResponse) } catch { logger.info("Info: \(error.localizedDescription)") + logger.info("‼️‼️response \(response.id)") } } @@ -195,6 +197,8 @@ class WalletConnectRelay: WalletConnectRelaying { onResponse?(wcResponse) } catch { logger.info("Info: \(error.localizedDescription)") + logger.info("‼️‼️errror \(response.id)") + } } diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index cb56eff3c..757c8ce6d 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -95,9 +95,11 @@ final class ClientTests: XCTestCase { responder.client.approve(proposal: proposal, accounts: []) } responder.onSessionSettled = { sessionSettled in + print("GGGGGGGGGGGG") responderSettlesSessionExpectation.fulfill() } proposer.onSessionSettled = { [unowned self] sessionSettled in + print("UUUUUUUUUUUU") proposerSettlesSessionExpectation.fulfill() if !initiatedSecondSession { let _ = try! proposer.client.connect(sessionPermissions: permissions, topic: pairingTopic) From 3b4e8da1ab1668d2949b2c5b562548132790ae8e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 14:45:00 +0100 Subject: [PATCH 14/16] clean up --- Sources/WalletConnect/Engine/SessionEngine.swift | 2 +- Sources/WalletConnect/Relay/WalletConnectRelay.swift | 3 --- Tests/IntegrationTests/ClientTest.swift | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 5fd91a115..ef566bae8 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -492,11 +492,11 @@ final class SessionEngine { let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in - onSessionApproved?(approvedSession) if let error = error { logger.error(error) } } + onSessionApproved?(approvedSession) } private func setupExpirationHandling() { diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 3bccd3d0e..f4a57edfc 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -181,7 +181,6 @@ class WalletConnectRelay: WalletConnectRelaying { onResponse?(wcResponse) } catch { logger.info("Info: \(error.localizedDescription)") - logger.info("‼️‼️response \(response.id)") } } @@ -197,8 +196,6 @@ class WalletConnectRelay: WalletConnectRelaying { onResponse?(wcResponse) } catch { logger.info("Info: \(error.localizedDescription)") - logger.info("‼️‼️errror \(response.id)") - } } diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index 757c8ce6d..cb56eff3c 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -95,11 +95,9 @@ final class ClientTests: XCTestCase { responder.client.approve(proposal: proposal, accounts: []) } responder.onSessionSettled = { sessionSettled in - print("GGGGGGGGGGGG") responderSettlesSessionExpectation.fulfill() } proposer.onSessionSettled = { [unowned self] sessionSettled in - print("UUUUUUUUUUUU") proposerSettlesSessionExpectation.fulfill() if !initiatedSecondSession { let _ = try! proposer.client.connect(sessionPermissions: permissions, topic: pairingTopic) From 7017de60202ef47e1cc2600d05aeee13fb12666d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Jan 2022 14:49:17 +0100 Subject: [PATCH 15/16] uncomment code in waku --- Sources/Relayer/WakuNetworkRelay.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index 6d2975c98..8e1eb99fc 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -120,7 +120,7 @@ public final class WakuNetworkRelay { let request = JSONRPCRequest(method: RelayJSONRPC.Method.unsubscribe.rawValue, params: params) let requestJson = try! request.json() var cancellable: AnyCancellable? -// jsonRpcSubscriptionsHistory.delete(topic: topic) + jsonRpcSubscriptionsHistory.delete(topic: topic) dispatcher.send(requestJson) { [weak self] error in if let error = error { self?.logger.debug("Failed to Unsubscribe on Topic") @@ -155,7 +155,7 @@ public final class WakuNetworkRelay { if let request = tryDecode(SubscriptionRequest.self, from: payload), request.method == RelayJSONRPC.Method.subscription.rawValue { do { -// try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) + try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) onMessage?(request.params.data.topic, request.params.data.message) acknowledgeSubscription(requestId: request.id) } catch { @@ -184,7 +184,7 @@ public final class WakuNetworkRelay { private func acknowledgeSubscription(requestId: Int64) { let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) let responseJson = try! response.json() -// try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResponseTypes.response(response)) + try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResponseTypes.response(response)) dispatcher.send(responseJson) { [weak self] error in if let error = error { self?.logger.debug("Failed to Respond for request id: \(requestId), error: \(error)") From 7083b190e841c5f17bf366d72648312a7d844ee3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 19 Jan 2022 09:01:01 +0100 Subject: [PATCH 16/16] fix build --- Sources/WalletConnect/Relay/WalletConnectRelay.swift | 1 + Sources/WalletConnect/Types/WCRequest.swift | 4 ++-- Tests/WalletConnectTests/TestsData/SerialiserTestData.swift | 6 +++--- Tests/WalletConnectTests/WCRelayTests.swift | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 0771d73aa..6e3c2c926 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -16,6 +16,7 @@ protocol WalletConnectRelaying: AnyObject { var transportConnectionPublisher: AnyPublisher {get} var wcRequestPublisher: AnyPublisher {get} func request(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>)->())?) + func request(topic: String, payload: WCRequest, completion: ((Result, JSONRPCErrorResponse>)->())?) func respond(topic: String, response: JsonRpcResponseTypes, completion: @escaping ((Error?)->())) func subscribe(topic: String) func unsubscribe(topic: String) diff --git a/Sources/WalletConnect/Types/WCRequest.swift b/Sources/WalletConnect/Types/WCRequest.swift index 50c7f9278..b4b6c20cd 100644 --- a/Sources/WalletConnect/Types/WCRequest.swift +++ b/Sources/WalletConnect/Types/WCRequest.swift @@ -12,8 +12,8 @@ struct WCRequest: Codable { case method case params } - - internal init(method: Method, params: Params, id: Int64 = generateId(), jsonrpc: String = "2.0") { + + internal init(id: Int64 = generateId(), jsonrpc: String = "2.0", method: Method, params: Params) { self.id = id self.jsonrpc = jsonrpc self.method = method diff --git a/Tests/WalletConnectTests/TestsData/SerialiserTestData.swift b/Tests/WalletConnectTests/TestsData/SerialiserTestData.swift index 58b8eaff8..f2f4d91e1 100644 --- a/Tests/WalletConnectTests/TestsData/SerialiserTestData.swift +++ b/Tests/WalletConnectTests/TestsData/SerialiserTestData.swift @@ -12,6 +12,8 @@ enum SerialiserTestData { Data(hex: "14aa7f6034dd0213be5901b472f461769855ac1e2f6bec6a8ed1157a9da3b2df08802cbd6e0d030d86ff99011040cfc831eec3636c1d46bfc22cbe055560fea3") static let serialisedMessage = "f0d00d4274a7e9711e4e0f21820b887745c59ad0c053925072f4503a39fe579ca8b7b8fa6bf0c7297e6db8f6585ee77ffc6d3106fa827043279f9db08cd2e29a988c7272fa3cfdb739163bb9606822c714aa7f6034dd0213be5901b472f461769855ac1e2f6bec6a8ed1157a9da3b2df08802cbd6e0d030d86ff99011040cfc831eec3636c1d46bfc22cbe055560fea3" static let pairingApproveJSONRPCRequest = WCRequest( + id: 0, + jsonrpc: "2.0", method: WCRequest.Method.pairingApprove, params: WCRequest.Params.pairingApprove( PairingType.ApprovalParams( @@ -24,9 +26,7 @@ enum SerialiserTestData { name: "iOS", description: nil, url: nil, - icons: nil)))), - id: 0, - jsonrpc: "2.0" + icons: nil)))) ) static let pairingApproveJSON = """ diff --git a/Tests/WalletConnectTests/WCRelayTests.swift b/Tests/WalletConnectTests/WCRelayTests.swift index 6c2b5ab87..37ead33db 100644 --- a/Tests/WalletConnectTests/WCRelayTests.swift +++ b/Tests/WalletConnectTests/WCRelayTests.swift @@ -87,7 +87,7 @@ extension WalletConnectRelayTests { let wcRequestId: Int64 = 123456 let sessionPayloadParams = SessionType.PayloadParams(request: SessionType.PayloadParams.Request(method: "method", params: AnyCodable("params")), chainId: "") let params = WCRequest.Params.sessionPayload(sessionPayloadParams) - let wcRequest = WCRequest(method: WCRequest.Method.sessionPayload, params: params, id: wcRequestId) + let wcRequest = WCRequest(id: wcRequestId, method: WCRequest.Method.sessionPayload, params: params) return wcRequest } }