diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index f4ed47f2c..448c3bd66 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -125,7 +125,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { imageUrl: "https://avatars.githubusercontent.com/u/37784886?s=200&v=4", order: 1, mobileLink: "rn-web3wallet://", - linkMode: "https://lab.web3modal.com/walletkit_rn" + linkMode: "https://lab.web3modal.com/rn_walletkit" ), .init( id: "flutter-sample", @@ -136,15 +136,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { mobileLink: "wcflutterwallet://", linkMode: "https://lab.web3modal.com/walletkit_flutter" ), - .init( - id: "flutter-sample-internal", - name: "Flutter Sample Wallet Internal", - homepage: "https://walletconnect.com/", - imageUrl: "https://avatars.githubusercontent.com/u/37784886?s=200&v=4", - order: 1, - mobileLink: "wcflutterwallet-internal://", - linkMode: "https://lab.web3modal.com/walletkit_flutter_internal" - ), ] ) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 02e8b077f..f7829209b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -57,7 +57,6 @@ 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; 84CE642827981DF000142511 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642727981DF000142511 /* Assets.xcassets */; }; 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642927981DF000142511 /* LaunchScreen.storyboard */; }; - 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; }; 84D093EB2B4EA6CB005B1925 /* ActivityIndicatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D093EA2B4EA6CB005B1925 /* ActivityIndicatorManager.swift */; }; 84DB38F32983CDAE00BFEE37 /* PushRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */; }; 84E6B84A29787A8000428BAF /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E6B84929787A8000428BAF /* NotificationService.swift */; }; @@ -315,7 +314,6 @@ C5DD5BE1294E09E3008FD3A4 /* Web3Wallet in Frameworks */ = {isa = PBXBuildFile; productRef = C5DD5BE0294E09E3008FD3A4 /* Web3Wallet */; }; C5F32A2C2954814200A6476E /* ConnectionDetailsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A2B2954814200A6476E /* ConnectionDetailsModule.swift */; }; C5F32A2E2954814A00A6476E /* ConnectionDetailsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A2D2954814A00A6476E /* ConnectionDetailsRouter.swift */; }; - C5F32A302954816100A6476E /* ConnectionDetailsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A2F2954816100A6476E /* ConnectionDetailsInteractor.swift */; }; C5F32A322954816C00A6476E /* ConnectionDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */; }; C5F32A342954817600A6476E /* ConnectionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A332954817600A6476E /* ConnectionDetailsView.swift */; }; C5F32A362954FE3C00A6476E /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C5F32A352954FE3C00A6476E /* Colors.xcassets */; }; @@ -438,7 +436,6 @@ 84CE642A27981DF000142511 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 84CE642C27981DF000142511 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; - 84CEC64528D89D6B00D081A8 /* PairingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTests.swift; sourceTree = ""; }; 84D093EA2B4EA6CB005B1925 /* ActivityIndicatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorManager.swift; sourceTree = ""; }; 84D72FC62B4692770057EAF3 /* DApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DApp.entitlements; sourceTree = ""; }; 84DB38F029828A7C00BFEE37 /* WalletApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletApp.entitlements; sourceTree = ""; }; @@ -656,7 +653,6 @@ C5BE021A2AF79B960064FC88 /* SessionAccountModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionAccountModule.swift; sourceTree = ""; }; C5F32A2B2954814200A6476E /* ConnectionDetailsModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsModule.swift; sourceTree = ""; }; C5F32A2D2954814A00A6476E /* ConnectionDetailsRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsRouter.swift; sourceTree = ""; }; - C5F32A2F2954816100A6476E /* ConnectionDetailsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsInteractor.swift; sourceTree = ""; }; C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsPresenter.swift; sourceTree = ""; }; C5F32A332954817600A6476E /* ConnectionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsView.swift; sourceTree = ""; }; C5F32A352954FE3C00A6476E /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; @@ -977,14 +973,6 @@ path = DApp; sourceTree = ""; }; - 84CEC64728D8A98900D081A8 /* Pairing */ = { - isa = PBXGroup; - children = ( - 84CEC64528D89D6B00D081A8 /* PairingTests.swift */, - ); - path = Pairing; - sourceTree = ""; - }; 84D2A66728A4F5260088AE09 /* Auth */ = { isa = PBXGroup; children = ( @@ -1457,7 +1445,6 @@ children = ( 847F07FE2A25DBC700B2A5A4 /* XPlatform */, 849D7A91292E2115006A2BD4 /* Push */, - 84CEC64728D8A98900D081A8 /* Pairing */, A5E03E0A28646A8A00888481 /* Stubs */, A5E03E0928646A8100888481 /* Sign */, 84D2A66728A4F5260088AE09 /* Auth */, @@ -1816,7 +1803,6 @@ children = ( C5F32A2B2954814200A6476E /* ConnectionDetailsModule.swift */, C5F32A2D2954814A00A6476E /* ConnectionDetailsRouter.swift */, - C5F32A2F2954816100A6476E /* ConnectionDetailsInteractor.swift */, C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */, C5F32A332954817600A6476E /* ConnectionDetailsView.swift */, ); @@ -2396,7 +2382,6 @@ files = ( A51606F92A2F47BD00CACB92 /* DefaultBIP44Provider.swift in Sources */, A5A0843E29D2F624000B9B17 /* DefaultCryptoProvider.swift in Sources */, - 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */, 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, 8439CB89293F658E00F2F2E2 /* PushMessage.swift in Sources */, A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */, @@ -2545,7 +2530,6 @@ C55D34AF2965FB750004314A /* SessionProposalPresenter.swift in Sources */, A5D610D22AB35B1100C20083 /* Listings.swift in Sources */, A5B4F7C82ABB21190099AF7C /* CacheAsyncImage.swift in Sources */, - C5F32A302954816100A6476E /* ConnectionDetailsInteractor.swift in Sources */, 847BD1E8298A806800076C90 /* NotificationsView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3277,8 +3261,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WalletConnect/web3modal-swift"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.0.0; + branch = "pairing-expiry"; + kind = branch; }; }; A5434021291E6A270068F706 /* XCRemoteSwiftPackageReference "solana-swift" */ = { diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 53859947e..34a63bc5b 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/attaswift/BigInt.git", "state": { "branch": null, - "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", - "version": "5.3.0" + "revision": "793a7fac0bfc318e85994bf6900652e827aef33e", + "version": "5.4.1" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", "state": { "branch": null, - "revision": "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c", - "version": "1.8.1" + "revision": "678d442c6f7828def400a70ae15968aef67ef52d", + "version": "1.8.3" } }, { @@ -33,7 +33,7 @@ "repositoryURL": "https://github.com/mixpanel/mixpanel-swift", "state": { "branch": "master", - "revision": "9438b4a5f76bf2108b09f8fed1abbad8f353e1a2", + "revision": "61ce9b40817466fb1334db1d7a582fbaf616ab4c", "version": null } }, @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/getsentry/sentry-cocoa.git", "state": { "branch": null, - "revision": "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version": "8.21.0" + "revision": "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version": "8.34.0" } }, { @@ -185,8 +185,8 @@ "package": "swift-web3modal", "repositoryURL": "https://github.com/WalletConnect/web3modal-swift", "state": { - "branch": null, - "revision": "25abd7e5471f21d662400f9763948fbf97c60c97", + "branch": "pairing-expiry", + "revision": "3088a08a43c490c24f1dfb1b858a67de089feef6", "version": null } } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift deleted file mode 100644 index 80c9e5f0a..000000000 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ /dev/null @@ -1,86 +0,0 @@ -import Foundation -import XCTest -@testable import WalletConnectUtils -@testable import WalletConnectKMS -import WalletConnectRelay -import Combine -import WalletConnectNetworking -@testable import WalletConnectPairing -import WalletConnectSign - -final class PairingTests: XCTestCase { - - var appPairingClient: PairingClient! - var walletPairingClient: PairingClient! - - var pairingStorage: PairingStorage! - - private var publishers = [AnyCancellable]() - - func makeClient(prefix: String, includeSign: Bool = true) -> PairingClient { - let keychain = KeychainStorageMock() - let keyValueStorage = RuntimeKeyValueStorage() - - let logger = ConsoleLogger(prefix: prefix, loggingLevel: .debug) - - let relayClient = RelayClientFactory.create( - relayHost: InputConfig.relayHost, - projectId: InputConfig.projectId, - keyValueStorage: RuntimeKeyValueStorage(), - keychainStorage: keychain, - socketFactory: DefaultSocketFactory(), - networkMonitor: NetworkMonitor(), - logger: logger) - - let networkingClient = NetworkingClientFactory.create( - relayClient: relayClient, - logger: logger, - keychainStorage: keychain, - keyValueStorage: keyValueStorage) - - let pairingClient = PairingClientFactory.create( - logger: logger, - keyValueStorage: keyValueStorage, - keychainStorage: keychain, - networkingClient: networkingClient, - eventsClient: MockEventsClient()) - - - return pairingClient - } - - override func setUp() { - appPairingClient = makeClient(prefix: "🤖 Dapp: ") - walletPairingClient = makeClient(prefix: "🐶 Wallet: ", includeSign: false) - } - - func testPing() async { - let expectation = expectation(description: "expects ping response") - let uri = try! await appPairingClient.create() - try? await walletPairingClient.pair(uri: uri) - try! await walletPairingClient.ping(topic: uri.topic) - walletPairingClient.pingResponsePublisher - .sink { topic in - XCTAssertEqual(topic, uri.topic) - expectation.fulfill() - }.store(in: &publishers) - wait(for: [expectation], timeout: InputConfig.defaultTimeout) - } - - func testDisconnect() async { - - let expectation = expectation(description: "wallet disconnected pairing") - - - walletPairingClient.pairingDeletePublisher.sink { _ in - expectation.fulfill() - }.store(in: &publishers) - - let uri = try! await appPairingClient.create() - - try? await walletPairingClient.pair(uri: uri) - - try! await appPairingClient.disconnect(topic: uri.topic) - wait(for: [expectation], timeout: InputConfig.defaultTimeout) - } -} diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 31f09a9e3..4575da2db 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -110,8 +110,7 @@ final class SignClientTests: XCTestCase { walletSettlementExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) try await walletPairingClient.pair(uri: uri) await fulfillment(of: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } @@ -124,8 +123,7 @@ final class SignClientTests: XCTestCase { let store = Store() let semaphore = DispatchSemaphore(value: 0) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) try await walletPairingClient.pair(uri: uri) wallet.sessionProposalPublisher.sink { [unowned self] (proposal, _) in @@ -164,8 +162,7 @@ final class SignClientTests: XCTestCase { sessionDeleteExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) try await walletPairingClient.pair(uri: uri) await fulfillment(of: [sessionDeleteExpectation], timeout: InputConfig.defaultTimeout) } @@ -194,8 +191,8 @@ final class SignClientTests: XCTestCase { expectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) @@ -247,8 +244,8 @@ final class SignClientTests: XCTestCase { responseExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [requestExpectation, responseExpectation], timeout: InputConfig.defaultTimeout) } @@ -292,48 +289,10 @@ final class SignClientTests: XCTestCase { expectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) - try await walletPairingClient.pair(uri: uri) - await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) - } - - func testNewSessionOnExistingPairing() async throws { - let dappSettlementExpectation = expectation(description: "Dapp settles session") - dappSettlementExpectation.expectedFulfillmentCount = 2 - let walletSettlementExpectation = expectation(description: "Wallet settles session") - walletSettlementExpectation.expectedFulfillmentCount = 2 - let requiredNamespaces = ProposalNamespace.stubRequired() - let sessionNamespaces = SessionNamespace.make(toRespond: requiredNamespaces) - var initiatedSecondSession = false - - wallet.sessionProposalPublisher.sink { [unowned self] (proposal, _) in - Task(priority: .high) { - do { - _ = try await wallet.approve(proposalId: proposal.id, namespaces: sessionNamespaces) - } catch { - XCTFail("\(error)") - } - } - }.store(in: &publishers) - dapp.sessionSettlePublisher.sink { [unowned self] _ in - dappSettlementExpectation.fulfill() - let pairingTopic = dappPairingClient.getPairings().first!.topic - if !initiatedSecondSession { - Task(priority: .high) { - _ = try! await dapp.connect(requiredNamespaces: requiredNamespaces, topic: pairingTopic) - } - initiatedSecondSession = true - } - }.store(in: &publishers) - wallet.sessionSettlePublisher.sink { _ in - walletSettlementExpectation.fulfill() - }.store(in: &publishers) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) try await walletPairingClient.pair(uri: uri) - await fulfillment(of: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) + await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) } func testSuccessfulSessionUpdateNamespaces() async throws { @@ -358,8 +317,8 @@ final class SignClientTests: XCTestCase { XCTAssertEqual(namespace.values.first?.accounts.count, 2) expectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) } @@ -386,8 +345,8 @@ final class SignClientTests: XCTestCase { } }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) @@ -417,8 +376,8 @@ final class SignClientTests: XCTestCase { } }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) @@ -445,8 +404,8 @@ final class SignClientTests: XCTestCase { } }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) @@ -524,8 +483,7 @@ final class SignClientTests: XCTestCase { walletSettlementExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces) try await walletPairingClient.pair(uri: uri) await fulfillment(of: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } @@ -593,8 +551,8 @@ final class SignClientTests: XCTestCase { walletSettlementExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } @@ -652,8 +610,8 @@ final class SignClientTests: XCTestCase { walletSettlementExpectation.fulfill() }.store(in: &publishers) - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } @@ -715,8 +673,8 @@ final class SignClientTests: XCTestCase { settlementFailedExpectation.fulfill() } - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [settlementFailedExpectation], timeout: InputConfig.defaultTimeout) } @@ -781,8 +739,8 @@ final class SignClientTests: XCTestCase { settlementFailedExpectation.fulfill() } - let uri = try! await dappPairingClient.create() - try await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces, topic: uri.topic) + let uri = try! await dapp.connect(requiredNamespaces: requiredNamespaces, optionalNamespaces: optionalNamespaces) + try await walletPairingClient.pair(uri: uri) await fulfillment(of: [settlementFailedExpectation], timeout: 1) } diff --git a/Example/WalletApp/ApplicationLayer/ConfigurationService.swift b/Example/WalletApp/ApplicationLayer/ConfigurationService.swift index 970353746..7cd72d9db 100644 --- a/Example/WalletApp/ApplicationLayer/ConfigurationService.swift +++ b/Example/WalletApp/ApplicationLayer/ConfigurationService.swift @@ -67,7 +67,6 @@ final class ConfigurationService { Web3Wallet.instance.pairingExpirationPublisher .receive(on: DispatchQueue.main) .sink { pairing in - guard !pairing.active else { return } AlertPresenter.present(message: "Pairing has expired", type: .warning) }.store(in: &publishers) diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift deleted file mode 100644 index aafd41678..000000000 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Combine - -import Web3Wallet - -final class ConnectionDetailsInteractor { - func disconnectSession(session: Session) async throws { - try await Web3Wallet.instance.disconnect(topic: session.topic) - } -} diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsModule.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsModule.swift index d42807d98..860400ab0 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsModule.swift @@ -6,9 +6,7 @@ final class ConnectionDetailsModule { @discardableResult static func create(app: Application, session: Session) -> UIViewController { let router = ConnectionDetailsRouter(app: app) - let interactor = ConnectionDetailsInteractor() let presenter = ConnectionDetailsPresenter( - interactor: interactor, router: router, session: session ) diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsPresenter.swift index 50d6f1d59..9ca9a33aa 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsPresenter.swift @@ -4,7 +4,6 @@ import Combine import Web3Wallet final class ConnectionDetailsPresenter: ObservableObject { - private let interactor: ConnectionDetailsInteractor private let router: ConnectionDetailsRouter let session: Session @@ -12,11 +11,9 @@ final class ConnectionDetailsPresenter: ObservableObject { private var disposeBag = Set() init( - interactor: ConnectionDetailsInteractor, router: ConnectionDetailsRouter, session: Session ) { - self.interactor = interactor self.router = router self.session = session } @@ -25,7 +22,7 @@ final class ConnectionDetailsPresenter: ObservableObject { Task { do { ActivityIndicatorManager.shared.start() - try await interactor.disconnectSession(session: session) + try await Web3Wallet.instance.disconnect(topic: session.topic) ActivityIndicatorManager.shared.stop() DispatchQueue.main.async { self.router.dismiss() @@ -36,10 +33,26 @@ final class ConnectionDetailsPresenter: ObservableObject { } } } - + + func accountReferences(namespace: String) -> [String] { session.namespaces[namespace]?.accounts.map { "\($0.namespace):\(($0.reference))" } ?? [] } + + func changeForMainnet() { + Task { + do { + ActivityIndicatorManager.shared.start() + + try await Web3Wallet.instance.emit(topic: session.topic, event: Session.Event(name: "chainChanged", data: AnyCodable("1")), chainId: Blockchain("eip155:1")!) + + ActivityIndicatorManager.shared.stop() + } catch { + ActivityIndicatorManager.shared.stop() + print(error) + } + } + } } // MARK: - Private functions diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsView.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsView.swift index 5174bc80e..86fcf1020 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsView.swift @@ -184,6 +184,15 @@ struct ConnectionDetailsView: View { .padding(.horizontal, 20) .padding(.top, 30) + Button { + presenter.changeForMainnet() + } label: { + Text("Change for Mainnet") + .foregroundColor(.grey50) + .font(.system(size: 20, weight: .semibold, design: .rounded)) + } + .padding(.top, 20) + Button { presenter.onDelete() } label: { diff --git a/Package.swift b/Package.swift index 797d5622c..d674fea3c 100644 --- a/Package.swift +++ b/Package.swift @@ -13,9 +13,6 @@ let package = Package( .library( name: "WalletConnect", targets: ["WalletConnectSign"]), - .library( - name: "WalletConnectAuth", - targets: ["Auth"]), .library( name: "Web3Wallet", targets: ["Web3Wallet"]), @@ -54,10 +51,6 @@ let package = Package( dependencies: ["WalletConnectPairing", "WalletConnectVerify", "WalletConnectSigner", "Events"], path: "Sources/WalletConnectSign", resources: [.process("Resources/PrivacyInfo.xcprivacy")]), - .target( - name: "Auth", - dependencies: ["WalletConnectPairing", "WalletConnectSigner", "WalletConnectVerify"], - path: "Sources/Auth"), .target( name: "Web3Wallet", dependencies: ["WalletConnectSign", "WalletConnectPush", "WalletConnectVerify"], diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift deleted file mode 100644 index 044e92bd8..000000000 --- a/Sources/Auth/Auth.swift +++ /dev/null @@ -1,46 +0,0 @@ -import Foundation -import Combine - -#if SWIFT_PACKAGE -public typealias VerifyContext = WalletConnectVerify.VerifyContext -#endif - -/// Auth instatnce wrapper -/// -/// ```Swift -/// let metadata = AppMetadata( -/// name: "Swift wallet", -/// description: "wallet", -/// url: "wallet.connect", -/// icons: ["https://my_icon.com/1"]) -/// Auth.configure(metadata: metadata, account: account) -/// try await Auth.instance.pair(uri: uri) -/// ``` -public class Auth { - - /// Auth client instance - public static var instance: AuthClient = { - guard let config = Auth.config else { - fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.") - } - return AuthClientFactory.create( - metadata: Pair.metadata, - projectId: Networking.projectId, - crypto: config.crypto, - networkingClient: Networking.interactor, - pairingRegisterer: Pair.registerer, - groupIdentifier: Networking.groupIdentifier - ) - }() - - private static var config: Config? - - private init() { } - - /// Auth instance wallet config method. For DApp usage - /// - Parameters: - /// - signerFactory: Auth signers factory - static public func configure(crypto: CryptoProvider) { - Auth.config = Auth.Config(crypto: crypto) - } -} diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift deleted file mode 100644 index d9551bdde..000000000 --- a/Sources/Auth/AuthClient.swift +++ /dev/null @@ -1,118 +0,0 @@ -import Foundation -import Combine - -/// WalletConnect Auth Client -/// -/// Cannot be instantiated outside of the SDK -/// -/// Access via `Auth.instance` -@available(*, deprecated, message: "Use SignClient for dApps and Web3Wallet interface for wallets instead.") -public class AuthClient: AuthClientProtocol { - - // MARK: - Public Properties - - /// Publisher that sends authentication requests - /// - /// Wallet should subscribe on events in order to receive auth requests. - @available(*, deprecated, message: "Use SignClient for dApps and Web3Wallet interface for wallets instead.") - public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { - authRequestPublisherSubject.eraseToAnyPublisher() - } - - /// Publisher that sends authentication responses - /// - /// App should subscribe for events in order to receive CACAO object with a signature matching authentication request. - /// - /// Emited result may be an error. - @available(*, deprecated, message: "Use SignClient for dApps and Web3Wallet interface for wallets instead.") - public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { - authResponsePublisherSubject.eraseToAnyPublisher() - } - - /// Publisher that sends web socket connection status - @available(*, deprecated, message: "Use Web3Wallet interface for managing socket connection status.") - public let socketConnectionStatusPublisher: AnyPublisher - - /// An object that loggs SDK's errors and info messages - public let logger: ConsoleLogging - - // MARK: - Private Properties - - private let pairingRegisterer: PairingRegisterer - - private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - private var authRequestPublisherSubject = PassthroughSubject<(request: AuthRequest, context: VerifyContext?), Never>() - private let appRequestService: AppRequestService - private let appRespondSubscriber: AppRespondSubscriber - private let walletRequestSubscriber: WalletRequestSubscriber - private let walletRespondService: WalletRespondService - private let pendingRequestsProvider: Auth_PendingRequestsProvider - - init(appRequestService: AppRequestService, - appRespondSubscriber: AppRespondSubscriber, - walletRequestSubscriber: WalletRequestSubscriber, - walletRespondService: WalletRespondService, - pendingRequestsProvider: Auth_PendingRequestsProvider, - logger: ConsoleLogging, - socketConnectionStatusPublisher: AnyPublisher, - pairingRegisterer: PairingRegisterer - ) { - self.appRequestService = appRequestService - self.walletRequestSubscriber = walletRequestSubscriber - self.walletRespondService = walletRespondService - self.appRespondSubscriber = appRespondSubscriber - self.pendingRequestsProvider = pendingRequestsProvider - self.logger = logger - self.socketConnectionStatusPublisher = socketConnectionStatusPublisher - self.pairingRegisterer = pairingRegisterer - setUpPublishers() - } - - /// For a dapp to send an authentication request to a wallet - /// - Parameter params: Set of parameters required to request authentication - /// - Parameter topic: Pairing topic that wallet already subscribes for - @available(*, deprecated, message: "Use SignClient for sending authentication requests.") - public func request(_ params: RequestParams, topic: String) async throws { - logger.debug("Requesting Authentication on existing pairing") - try pairingRegisterer.validatePairingExistance(topic) - try await appRequestService.request(params: params, topic: topic) - } - - /// For a wallet to respond on authentication request - /// - Parameters: - /// - requestId: authentication request id - /// - signature: CACAO signature of requested message - @available(*, deprecated, message: "Use Web3Wallet interface for responding to authentication requests.") - public func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws { - try await walletRespondService.respond(requestId: requestId, signature: signature, account: account) - } - - /// For wallet to reject authentication request - /// - Parameter requestId: authentication request id - @available(*, deprecated, message: "Use Web3Wallet interface for rejecting authentication requests.") - public func reject(requestId: RPCID) async throws { - try await walletRespondService.respondError(requestId: requestId) - } - - /// Query pending authentication requests - /// - Returns: Pending authentication requests - @available(*, deprecated, message: "Use SignClient for managing pending authentication requests.") - public func getPendingRequests() throws -> [(AuthRequest, VerifyContext?)] { - return try pendingRequestsProvider.getPendingRequests() - } - - @available(*, deprecated, message: "Use SignClient or Web3Wallet for message formatting.") - public func formatMessage(payload: AuthPayloadStruct, address: String) throws -> String { - return try SIWEFromCacaoPayloadFormatter().formatMessage(from: payload.cacaoPayload(address: address)) - } - - private func setUpPublishers() { - appRespondSubscriber.onResponse = { [unowned self] (id, result) in - authResponsePublisherSubject.send((id, result)) - } - - walletRequestSubscriber.onRequest = { [unowned self] request in - authRequestPublisherSubject.send(request) - } - } -} diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift deleted file mode 100644 index 59b902602..000000000 --- a/Sources/Auth/AuthClientFactory.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Foundation - -public struct AuthClientFactory { - public static func create( - metadata: AppMetadata, - projectId: String, - crypto: CryptoProvider, - networkingClient: NetworkingInteractor, - pairingRegisterer: PairingRegisterer, - groupIdentifier: String - ) -> AuthClient { - let logger = ConsoleLogger(loggingLevel: .off) - guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else { - fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)") - } - let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier) - let iatProvider = DefaultIATProvider() - - return AuthClientFactory.create( - metadata: metadata, - projectId: projectId, - crypto: crypto, - logger: logger, - keyValueStorage: keyValueStorage, - keychainStorage: keychainStorage, - networkingClient: networkingClient, - pairingRegisterer: pairingRegisterer, - iatProvider: iatProvider - ) - } - - static func create( - metadata: AppMetadata, - projectId: String, - crypto: CryptoProvider, - logger: ConsoleLogging, - keyValueStorage: KeyValueStorage, - keychainStorage: KeychainStorageProtocol, - networkingClient: NetworkingInteractor, - pairingRegisterer: PairingRegisterer, - iatProvider: IATProvider - ) -> AuthClient { - let kms = KeyManagementService(keychain: keychainStorage) - let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let messageFormatter = SIWEFromCacaoPayloadFormatter() - let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider) - let verifyClient = VerifyClientFactory.create() - let verifyContextStore = CodableStore(defaults: keyValueStorage, identifier: VerifyStorageIdentifiers.context.rawValue) - let messageVerifierFactory = MessageVerifierFactory(crypto: crypto) - let signatureVerifier = messageVerifierFactory.create(projectId: projectId) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: signatureVerifier, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) - let walletErrorResponder = Auth_WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer, verifyClient: verifyClient, verifyContextStore: verifyContextStore) - let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) - let pendingRequestsProvider = Auth_PendingRequestsProvider(rpcHistory: history, verifyContextStore: verifyContextStore) - - return AuthClient( - appRequestService: appRequestService, - appRespondSubscriber: appRespondSubscriber, - walletRequestSubscriber: walletRequestSubscriber, - walletRespondService: walletRespondService, - pendingRequestsProvider: pendingRequestsProvider, - logger: logger, - socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, - pairingRegisterer: pairingRegisterer - ) - } -} diff --git a/Sources/Auth/AuthClientProtocol.swift b/Sources/Auth/AuthClientProtocol.swift deleted file mode 100644 index d74cdbb7f..000000000 --- a/Sources/Auth/AuthClientProtocol.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation -import Combine - -public protocol AuthClientProtocol { - var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { get } - - func formatMessage(payload: AuthPayloadStruct, address: String) throws -> String - func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws - func reject(requestId: RPCID) async throws - func getPendingRequests() throws -> [(AuthRequest, VerifyContext?)] -} diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift deleted file mode 100644 index 183617159..000000000 --- a/Sources/Auth/AuthConfig.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -extension Auth { - struct Config { - let crypto: CryptoProvider - } -} diff --git a/Sources/Auth/AuthImports.swift b/Sources/Auth/AuthImports.swift deleted file mode 100644 index 91463cad3..000000000 --- a/Sources/Auth/AuthImports.swift +++ /dev/null @@ -1,5 +0,0 @@ -#if !CocoaPods -@_exported import WalletConnectPairing -@_exported import WalletConnectSigner -@_exported import WalletConnectVerify -#endif diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift deleted file mode 100644 index a3a56117f..000000000 --- a/Sources/Auth/AuthProtocolMethod.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -struct AuthRequestProtocolMethod: ProtocolMethod { - let method: String = "wc_authRequest" - - let requestConfig = RelayConfig(tag: 3000, prompt: true, ttl: 86400) - - let responseConfig = RelayConfig(tag: 3001, prompt: false, ttl: 86400) -} - -struct PairingPingProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingPing" - - let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - - let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) -} - -struct PairingDeleteProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingDelete" - - let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - - let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) -} diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift deleted file mode 100644 index 325c515b5..000000000 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation - -actor AppRequestService { - private let networkingInteractor: NetworkInteracting - private let appMetadata: AppMetadata - private let kms: KeyManagementService - private let logger: ConsoleLogging - private let iatProvader: IATProvider - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementService, - appMetadata: AppMetadata, - logger: ConsoleLogging, - iatProvader: IATProvider) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.appMetadata = appMetadata - self.logger = logger - self.iatProvader = iatProvader - } - - func request(params: RequestParams, topic: String) async throws { - let pubKey = try kms.createX25519KeyPair() - let responseTopic = pubKey.rawRepresentation.sha256().toHexString() - let requester = Auth_RequestParams.Requester(publicKey: pubKey.hexRepresentation, metadata: appMetadata) - let payload = AuthPayloadStruct(requestParams: params, iat: iatProvader.iat) - let params = Auth_RequestParams(requester: requester, payloadParams: payload) - let request = RPCRequest(method: "wc_authRequest", params: params) - try kms.setPublicKey(publicKey: pubKey, for: responseTopic) - logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)") - try await networkingInteractor.request(request, topic: topic, protocolMethod: AuthRequestProtocolMethod()) - try await networkingInteractor.subscribe(topic: responseTopic) - } -} diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift deleted file mode 100644 index e7036bf5e..000000000 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ /dev/null @@ -1,75 +0,0 @@ -import Foundation -import Combine - -class AppRespondSubscriber { - private let networkingInteractor: NetworkInteracting - private let logger: ConsoleLogging - private let rpcHistory: RPCHistory - private let signatureVerifier: MessageVerifier - private let messageFormatter: SIWEFromCacaoPayloadFormatter - private let pairingRegisterer: PairingRegisterer - private var publishers = [AnyCancellable]() - - var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - rpcHistory: RPCHistory, - signatureVerifier: MessageVerifier, - pairingRegisterer: PairingRegisterer, - messageFormatter: SIWEFromCacaoPayloadFormatter) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.rpcHistory = rpcHistory - self.signatureVerifier = signatureVerifier - self.messageFormatter = messageFormatter - self.pairingRegisterer = pairingRegisterer - subscribeForResponse() - } - - private func subscribeForResponse() { - networkingInteractor.responseErrorSubscription(on: AuthRequestProtocolMethod()) - .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in - guard let error = AuthErrors(code: payload.error.code) else { return } - onResponse?(payload.id, .failure(error)) - }.store(in: &publishers) - - networkingInteractor.responseSubscription(on: AuthRequestProtocolMethod()) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - - pairingRegisterer.activate(pairingTopic: payload.topic, peerMetadata: nil) - - networkingInteractor.unsubscribe(topic: payload.topic) - - let requestId = payload.id - let cacao = payload.response - let requestPayload = payload.request - - guard - let address = try? DIDPKH(did: cacao.p.iss).account.address, - let message = try? messageFormatter.formatMessage(from: cacao.p) - else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return } - - guard - let recovered = try? messageFormatter.formatMessage( - from: requestPayload.payloadParams.cacaoPayload(address: address) - ), recovered == message - else { self.onResponse?(requestId, .failure(.messageCompromised)); return } - - Task(priority: .high) { - do { - try await signatureVerifier.verify( - signature: cacao.s, - message: message, - address: address, - chainId: requestPayload.payloadParams.chainId - ) - onResponse?(requestId, .success(cacao)) - } catch { - logger.error("Signature verification failed with: \(error.localizedDescription)") - onResponse?(requestId, .failure(.signatureVerificationFailed)) - } - } - }.store(in: &publishers) - } -} diff --git a/Sources/Auth/Services/Wallet/Auth_PendingRequestsProvider.swift b/Sources/Auth/Services/Wallet/Auth_PendingRequestsProvider.swift deleted file mode 100644 index f646835e4..000000000 --- a/Sources/Auth/Services/Wallet/Auth_PendingRequestsProvider.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation - -class Auth_PendingRequestsProvider { - private let rpcHistory: RPCHistory - private let verifyContextStore: CodableStore - - init( - rpcHistory: RPCHistory, - verifyContextStore: CodableStore - ) { - self.rpcHistory = rpcHistory - self.verifyContextStore = verifyContextStore - } - - public func getPendingRequests() throws -> [(AuthRequest, VerifyContext?)] { - let pendingRequests: [AuthRequest] = rpcHistory.getPending() - .filter {$0.request.method == "wc_authRequest"} - .compactMap { - guard let params = try? $0.request.params?.get(Auth_RequestParams.self) else { return nil } - return AuthRequest(id: $0.request.id!, topic: $0.topic, payload: params.payloadParams, requester: params.requester.metadata) - } - - return pendingRequests.map { ($0, try? verifyContextStore.get(key: $0.id.string)) } - } -} diff --git a/Sources/Auth/Services/Wallet/Auth_WalletErrorResponder.swift b/Sources/Auth/Services/Wallet/Auth_WalletErrorResponder.swift deleted file mode 100644 index 895f41d9d..000000000 --- a/Sources/Auth/Services/Wallet/Auth_WalletErrorResponder.swift +++ /dev/null @@ -1,52 +0,0 @@ -import Foundation - -actor Auth_WalletErrorResponder { - enum Errors: Error { - case recordForIdNotFound - case malformedAuthRequestParams - } - - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let rpcHistory: RPCHistory - private let logger: ConsoleLogging - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol, - rpcHistory: RPCHistory) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.kms = kms - self.rpcHistory = rpcHistory - } - - func respondError(_ error: AuthErrors, requestId: RPCID) async throws { - let authRequestParams = try getAuthRequestParams(requestId: requestId) - let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) - - try kms.setAgreementSecret(keys, topic: topic) - - let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation) - try await networkingInteractor.respondError(topic: topic, requestId: requestId, protocolMethod: AuthRequestProtocolMethod(), reason: error, envelopeType: envelopeType) - } - - private func getAuthRequestParams(requestId: RPCID) throws -> Auth_RequestParams { - guard let request = rpcHistory.get(recordId: requestId)?.request - else { throw Errors.recordForIdNotFound } - - guard let authRequestParams = try request.params?.get(Auth_RequestParams.self) - else { throw Errors.malformedAuthRequestParams } - - return authRequestParams - } - - private func generateAgreementKeys(requestParams: Auth_RequestParams) throws -> (topic: String, keys: AgreementKeys) { - let peerPubKey = try AgreementPublicKey(hex: requestParams.requester.publicKey) - let topic = peerPubKey.rawRepresentation.sha256().toHexString() - let selfPubKey = try kms.createX25519KeyPair() - let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) - // TODO - remove keys - return (topic, keys) - } -} diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift deleted file mode 100644 index c8697ec5d..000000000 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ /dev/null @@ -1,60 +0,0 @@ -import Foundation -import Combine - -class WalletRequestSubscriber { - private let networkingInteractor: NetworkInteracting - private let logger: ConsoleLogging - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() - private let walletErrorResponder: Auth_WalletErrorResponder - private let pairingRegisterer: PairingRegisterer - private let verifyClient: VerifyClientProtocol - private let verifyContextStore: CodableStore - - var onRequest: (((request: AuthRequest, context: VerifyContext?)) -> Void)? - - init( - networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol, - walletErrorResponder: Auth_WalletErrorResponder, - pairingRegisterer: PairingRegisterer, - verifyClient: VerifyClientProtocol, - verifyContextStore: CodableStore - ) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.kms = kms - self.walletErrorResponder = walletErrorResponder - self.pairingRegisterer = pairingRegisterer - self.verifyClient = verifyClient - self.verifyContextStore = verifyContextStore - subscribeForRequest() - } - - private func subscribeForRequest() { - pairingRegisterer.register(method: AuthRequestProtocolMethod()) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - logger.debug("WalletRequestSubscriber: Received request") - - pairingRegisterer.setReceived(pairingTopic: payload.topic) - - let request = AuthRequest(id: payload.id, topic: payload.topic, payload: payload.request.payloadParams, requester: payload.request.requester.metadata) - - Task(priority: .high) { - let assertionId = payload.decryptedPayload.sha256().toHexString() - do { - let response = try await verifyClient.verify(.v1(assertionId: assertionId)) - let verifyContext = verifyClient.createVerifyContext(origin: response.origin, domain: payload.request.payloadParams.domain, isScam: response.isScam, isVerified: nil) - verifyContextStore.set(verifyContext, forKey: request.id.string) - onRequest?((request, verifyContext)) - } catch { - let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: payload.request.payloadParams.domain, isScam: nil, isVerified: nil) - verifyContextStore.set(verifyContext, forKey: request.id.string) - onRequest?((request, verifyContext)) - return - } - } - }.store(in: &publishers) - } -} diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift deleted file mode 100644 index 529ef2a79..000000000 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ /dev/null @@ -1,77 +0,0 @@ -import Foundation - -actor WalletRespondService { - enum Errors: Error { - case recordForIdNotFound - case malformedAuthRequestParams - } - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementService - private let rpcHistory: RPCHistory - private let verifyContextStore: CodableStore - private let logger: ConsoleLogging - private let walletErrorResponder: Auth_WalletErrorResponder - private let pairingRegisterer: PairingRegisterer - - init( - networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementService, - rpcHistory: RPCHistory, - verifyContextStore: CodableStore, - walletErrorResponder: Auth_WalletErrorResponder, - pairingRegisterer: PairingRegisterer - ) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.kms = kms - self.rpcHistory = rpcHistory - self.verifyContextStore = verifyContextStore - self.walletErrorResponder = walletErrorResponder - self.pairingRegisterer = pairingRegisterer - } - - func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { - let authRequestParams = try getAuthRequestParams(requestId: requestId) - let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) - - try kms.setAgreementSecret(keys, topic: topic) - - let header = CacaoHeader(t: "eip4361") - let payload = try authRequestParams.payloadParams.cacaoPayload(address: account.address) - let responseParams = AuthResponseParams(h: header, p: payload, s: signature) - - let response = RPCResponse(id: requestId, result: responseParams) - try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) - - pairingRegisterer.activate( - pairingTopic: topic, - peerMetadata: authRequestParams.requester.metadata - ) - - verifyContextStore.delete(forKey: requestId.string) - } - - func respondError(requestId: RPCID) async throws { - try await walletErrorResponder.respondError(AuthErrors.userRejeted, requestId: requestId) - verifyContextStore.delete(forKey: requestId.string) - } - - private func getAuthRequestParams(requestId: RPCID) throws -> Auth_RequestParams { - guard let request = rpcHistory.get(recordId: requestId)?.request - else { throw Errors.recordForIdNotFound } - - guard let authRequestParams = try request.params?.get(Auth_RequestParams.self) - else { throw Errors.malformedAuthRequestParams } - - return authRequestParams - } - - private func generateAgreementKeys(requestParams: Auth_RequestParams) throws -> (topic: String, keys: AgreementKeys) { - let peerPubKey = try AgreementPublicKey(hex: requestParams.requester.publicKey) - let topic = peerPubKey.rawRepresentation.sha256().toHexString() - let selfPubKey = try kms.createX25519KeyPair() - let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) - return (topic, keys) - } -} diff --git a/Sources/Auth/Types/AuthPayloadStruct.swift b/Sources/Auth/Types/AuthPayloadStruct.swift deleted file mode 100644 index b7a1494c4..000000000 --- a/Sources/Auth/Types/AuthPayloadStruct.swift +++ /dev/null @@ -1,59 +0,0 @@ -import Foundation - -public struct AuthPayloadStruct: Codable, Equatable { - public let domain: String - public let aud: String - public let version: String - public let nonce: String - public let chainId: String - public let type: String - public let iat: String - public let nbf: String? - public let exp: String? - public let statement: String? - public let requestId: String? - public let resources: [String]? - - init(requestParams: RequestParams, iat: String) { - self.type = "eip4361" - self.chainId = requestParams.chainId - self.domain = requestParams.domain - self.aud = requestParams.aud - self.version = "1" - self.nonce = requestParams.nonce - self.iat = iat - self.nbf = requestParams.nbf - self.exp = requestParams.exp - self.statement = requestParams.statement - self.requestId = requestParams.requestId - self.resources = requestParams.resources - } - - public func cacaoPayload(address: String) throws -> CacaoPayload { - guard - let blockchain = Blockchain(chainId), - let account = Account(blockchain: blockchain, address: address) else { - throw Errors.invalidChainID - } - return CacaoPayload( - iss: account.did, - domain: domain, - aud: aud, - version: version, - nonce: nonce, - iat: iat, - nbf: nbf, - exp: exp, - statement: statement, - requestId: requestId, - resources: resources - ) - } -} - -private extension AuthPayloadStruct { - - enum Errors: Error { - case invalidChainID - } -} diff --git a/Sources/Auth/Types/AuthRespondParams.swift b/Sources/Auth/Types/AuthRespondParams.swift deleted file mode 100644 index fce3414eb..000000000 --- a/Sources/Auth/Types/AuthRespondParams.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -public struct AuthRespondParams: Equatable { - let id: RPCID - let signature: CacaoSignature - - public init(id: RPCID, signature: CacaoSignature) { - self.id = id - self.signature = signature - } -} diff --git a/Sources/Auth/Types/Errors/AuthErrors.swift b/Sources/Auth/Types/Errors/AuthErrors.swift deleted file mode 100644 index 9cfda6bd1..000000000 --- a/Sources/Auth/Types/Errors/AuthErrors.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Foundation - -/// Authentication error -public enum AuthErrors: Codable, Equatable, Error { - case methodUnsupported - case userDisconnected - case userRejeted - case malformedResponseParams - case malformedRequestParams - case messageCompromised - case signatureVerificationFailed -} - -extension AuthErrors: Reason { - - init?(code: Int) { - switch code { - case Self.methodUnsupported.code: - self = .methodUnsupported - case Self.userRejeted.code: - self = .userRejeted - case Self.malformedResponseParams.code: - self = .malformedResponseParams - case Self.malformedRequestParams.code: - self = .malformedRequestParams - case Self.messageCompromised.code: - self = .messageCompromised - case Self.signatureVerificationFailed.code: - self = .signatureVerificationFailed - default: - return nil - } - } - - public var code: Int { - switch self { - case .methodUnsupported: - return 10001 - case .userDisconnected: - return 6000 - case .userRejeted: - return 14001 - case .malformedResponseParams: - return 12001 - case .malformedRequestParams: - return 12002 - case .messageCompromised: - return 12003 - case .signatureVerificationFailed: - return 12004 - } - } - - public var message: String { - switch self { - case .methodUnsupported: - return "Method Unsupported" - case .userRejeted: - return "Auth request rejected by user" - case .malformedResponseParams: - return "Response params malformed" - case .malformedRequestParams: - return "Request params malformed" - case .messageCompromised: - return "Original message compromised" - case .signatureVerificationFailed: - return "Message verification failed" - case .userDisconnected: - return "User Disconnected" - } - } -} diff --git a/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift b/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift deleted file mode 100644 index e2d097c01..000000000 --- a/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift +++ /dev/null @@ -1,4 +0,0 @@ -import Foundation - -/// wc_authRequest RPC method respond param -typealias AuthResponseParams = Cacao diff --git a/Sources/Auth/Types/ProtocolRPCParams/Auth_RequestParams.swift b/Sources/Auth/Types/ProtocolRPCParams/Auth_RequestParams.swift deleted file mode 100644 index 4a72728ed..000000000 --- a/Sources/Auth/Types/ProtocolRPCParams/Auth_RequestParams.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation - -/// wc_authRequest RPC method request param -struct Auth_RequestParams: Codable, Equatable { - let requester: Requester - let payloadParams: AuthPayloadStruct -} - -extension Auth_RequestParams { - struct Requester: Codable, Equatable { - let publicKey: String - let metadata: AppMetadata - } -} diff --git a/Sources/Auth/Types/Public/AuthRequest.swift b/Sources/Auth/Types/Public/AuthRequest.swift deleted file mode 100644 index 63e553cc5..000000000 --- a/Sources/Auth/Types/Public/AuthRequest.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public struct AuthRequest: Equatable, Codable { - public let id: RPCID - public let topic: String - public let payload: AuthPayloadStruct - public let requester: AppMetadata -} diff --git a/Sources/Auth/Types/RequestParams.swift b/Sources/Auth/Types/RequestParams.swift deleted file mode 100644 index 1f57884a9..000000000 --- a/Sources/Auth/Types/RequestParams.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation - -/// Parameters required to construct authentication request -/// for details read CAIP-74 and EIP-4361 specs -/// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-74.md -/// https://eips.ethereum.org/EIPS/eip-4361 -public struct RequestParams { - public let domain: String - public let chainId: String - public let nonce: String - public let aud: String - public let nbf: String? - public let exp: String? - public let statement: String? - public let requestId: String? - public let resources: [String]? - - public init( - domain: String, - chainId: String, - nonce: String, - aud: String, - nbf: String?, - exp: String?, - statement: String?, - requestId: String?, - resources: [String]? - ) { - self.domain = domain - self.chainId = chainId - self.nonce = nonce - self.aud = aud - self.nbf = nbf - self.exp = exp - self.statement = statement - self.requestId = requestId - self.resources = resources - } -} diff --git a/Sources/Events/Event.swift b/Sources/Events/Event.swift deleted file mode 100644 index cc8c4d3c1..000000000 --- a/Sources/Events/Event.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -struct Event: Codable { - let eventId: String - let bundleId: String - let timestamp: Int64 - let props: Props -} - -struct Props: Codable { - let event: String - let type: String - let properties: Properties? -} - -struct Properties: Codable { - let topic: String? - let trace: [String]? -} diff --git a/Sources/Events/EventStorage.swift b/Sources/Events/EventStorage.swift index 691ef903e..78afe55e2 100644 --- a/Sources/Events/EventStorage.swift +++ b/Sources/Events/EventStorage.swift @@ -2,16 +2,16 @@ import Foundation protocol EventStorage { - func saveErrorEvent(_ event: Event) - func fetchErrorEvents() -> [Event] + func saveErrorEvent(_ event: TraceEvent) + func fetchErrorEvents() -> [TraceEvent] func clearErrorEvents() } -class UserDefaultsEventStorage: EventStorage { +class UserDefaultsTraceEventStorage: EventStorage { private let errorEventsKey = "com.walletconnect.sdk.errorEvents" private let maxEvents = 30 - func saveErrorEvent(_ event: Event) { + func saveErrorEvent(_ event: TraceEvent) { var existingEvents = fetchErrorEvents() existingEvents.append(event) // Ensure we keep only the last 30 events @@ -23,9 +23,9 @@ class UserDefaultsEventStorage: EventStorage { } } - func fetchErrorEvents() -> [Event] { + func fetchErrorEvents() -> [TraceEvent] { if let data = UserDefaults.standard.data(forKey: errorEventsKey), - let events = try? JSONDecoder().decode([Event].self, from: data) { + let events = try? JSONDecoder().decode([TraceEvent].self, from: data) { // Return only the last 30 events return Array(events.suffix(maxEvents)) } @@ -39,13 +39,13 @@ class UserDefaultsEventStorage: EventStorage { #if DEBUG class MockEventStorage: EventStorage { - private(set) var savedEvents: [Event] = [] + private(set) var savedEvents: [TraceEvent] = [] - func saveErrorEvent(_ event: Event) { + func saveErrorEvent(_ event: TraceEvent) { savedEvents.append(event) } - func fetchErrorEvents() -> [Event] { + func fetchErrorEvents() -> [TraceEvent] { return savedEvents } diff --git a/Sources/Events/EventsClient.swift b/Sources/Events/EventsClient.swift index f0bcb32c3..da0dd30bd 100644 --- a/Sources/Events/EventsClient.swift +++ b/Sources/Events/EventsClient.swift @@ -2,9 +2,10 @@ import Foundation public protocol EventsClientProtocol { func startTrace(topic: String) - func saveEvent(_ event: TraceEvent) + func saveTraceEvent(_ event: TraceEventItem) func setTopic(_ topic: String) func setTelemetryEnabled(_ enabled: Bool) + func saveMessageEvent(_ event: MessageEventType) } public class EventsClient: EventsClientProtocol { @@ -12,17 +13,20 @@ public class EventsClient: EventsClientProtocol { private let eventsDispatcher: EventsDispatcher private let logger: ConsoleLogging private var stateStorage: TelemetryStateStorage + private let messageEventsStorage: MessageEventsStorage init( eventsCollector: EventsCollector, eventsDispatcher: EventsDispatcher, logger: ConsoleLogging, - stateStorage: TelemetryStateStorage + stateStorage: TelemetryStateStorage, + messageEventsStorage: MessageEventsStorage ) { self.eventsCollector = eventsCollector self.eventsDispatcher = eventsDispatcher self.logger = logger self.stateStorage = stateStorage + self.messageEventsStorage = messageEventsStorage if stateStorage.telemetryEnabled { Task { await sendStoredEvents() } @@ -48,12 +52,17 @@ public class EventsClient: EventsClientProtocol { } // Public method to save event - public func saveEvent(_ event: TraceEvent) { + public func saveTraceEvent(_ event: TraceEventItem) { guard stateStorage.telemetryEnabled else { return } - logger.debug("Will store an event: \(event)") + logger.debug("Will store a trace event: \(event)") eventsCollector.saveEvent(event) } + public func saveMessageEvent(_ event: MessageEventType) { + logger.debug("Will store a message event: \(event)") + messageEventsStorage.saveMessageEvent(event) + } + // Public method to set telemetry enabled or disabled public func setTelemetryEnabled(_ enabled: Bool) { stateStorage.telemetryEnabled = enabled @@ -66,15 +75,27 @@ public class EventsClient: EventsClientProtocol { private func sendStoredEvents() async { guard stateStorage.telemetryEnabled else { return } - let events = eventsCollector.storage.fetchErrorEvents() - guard !events.isEmpty else { return } - logger.debug("Will send events") + let traceEvents = eventsCollector.storage.fetchErrorEvents() + let messageEvents = messageEventsStorage.fetchMessageEvents() + + guard !traceEvents.isEmpty || !messageEvents.isEmpty else { return } + + var combinedEvents: [AnyCodable] = [] + + // Wrap trace events + combinedEvents.append(contentsOf: traceEvents.map { AnyCodable($0) }) + + // Wrap message events + combinedEvents.append(contentsOf: messageEvents.map { AnyCodable($0) }) + + logger.debug("Will send combined events") do { - let success: Bool = try await eventsDispatcher.executeWithRetry(events: events) + let success: Bool = try await eventsDispatcher.executeWithRetry(events: combinedEvents) if success { - logger.debug("Events sent successfully") + logger.debug("Combined events sent successfully") self.eventsCollector.storage.clearErrorEvents() + self.messageEventsStorage.clearMessageEvents() } } catch { logger.debug("Failed to send events after multiple attempts: \(error)") @@ -96,12 +117,17 @@ public class MockEventsClient: EventsClientProtocol { public func setTopic(_ topic: String) {} - public func saveEvent(_ event: TraceEvent) { + public func saveTraceEvent(_ event: TraceEventItem) { saveEventCalled = true } public func setTelemetryEnabled(_ enabled: Bool) { telemetryEnabled = enabled } + + public func saveMessageEvent(_ event: MessageEventType) { + + } + } #endif diff --git a/Sources/Events/EventsClientFactory.swift b/Sources/Events/EventsClientFactory.swift index 43bff4c76..b45df2f26 100644 --- a/Sources/Events/EventsClientFactory.swift +++ b/Sources/Events/EventsClientFactory.swift @@ -4,7 +4,7 @@ public class EventsClientFactory { static func create( projectId: String, sdkVersion: String, - storage: EventStorage = UserDefaultsEventStorage() + storage: EventStorage = UserDefaultsTraceEventStorage() ) -> EventsClient { let networkingService = NetworkingService( projectId: projectId, @@ -18,7 +18,8 @@ public class EventsClientFactory { eventsCollector: eventsCollector, eventsDispatcher: eventsDispatcher, logger: logger, - stateStorage: UserDefaultsTelemetryStateStorage() + stateStorage: UserDefaultsTelemetryStateStorage(), + messageEventsStorage: UserDefaultsMessageEventsStorage() ) } } diff --git a/Sources/Events/EventsCollector.swift b/Sources/Events/EventsCollector.swift index 2ace98002..92e1d286c 100644 --- a/Sources/Events/EventsCollector.swift +++ b/Sources/Events/EventsCollector.swift @@ -1,12 +1,12 @@ import Foundation // Protocol for TraceEvent -public protocol TraceEvent: CustomStringConvertible { +public protocol TraceEventItem: CustomStringConvertible { var description: String { get } } // Protocol for ErrorEvent -protocol ErrorEvent: TraceEvent {} +protocol ErrorEvent: TraceEventItem {} class EventsCollector { @@ -34,7 +34,7 @@ class EventsCollector { } // Function to save event - func saveEvent(_ event: TraceEvent) { + func saveEvent(_ event: TraceEventItem) { trace.append(event.description) if let errorEvent = event as? ErrorEvent { saveErrorEvent(errorEvent) @@ -51,14 +51,14 @@ class EventsCollector { // Private function to save error event private func saveErrorEvent(_ errorEvent: ErrorEvent) { let bundleId = Bundle.main.bundleIdentifier ?? "Unknown" - let event = Event( + let event = TraceEvent( eventId: UUID().uuidString, bundleId: bundleId, timestamp: Int64(Date().timeIntervalSince1970 * 1000), - props: Props( + props: TraceEvent.Props( event: "ERROR", type: errorEvent.description, - properties: Properties( + properties: TraceEvent.Properties( topic: topic, trace: trace ) diff --git a/Sources/Events/EventsDispatcher.swift b/Sources/Events/EventsDispatcher.swift index 0c347958b..f1ae135e1 100644 --- a/Sources/Events/EventsDispatcher.swift +++ b/Sources/Events/EventsDispatcher.swift @@ -17,7 +17,7 @@ class EventsDispatcher { self.retryPolicy = retryPolicy } - func executeWithRetry(events: [Event]) async throws -> Bool { + func executeWithRetry(events: [T]) async throws -> Bool { var attempts = 0 var delay = retryPolicy.initialDelay diff --git a/Sources/Events/ExecutionTraces/PairingExecutionTraceEvents.swift b/Sources/Events/ExecutionTraces/PairingExecutionTraceEvents.swift index bfb809dd3..4244b1674 100644 --- a/Sources/Events/ExecutionTraces/PairingExecutionTraceEvents.swift +++ b/Sources/Events/ExecutionTraces/PairingExecutionTraceEvents.swift @@ -1,7 +1,7 @@ import Foundation -public enum PairingExecutionTraceEvents: String, TraceEvent { +public enum PairingExecutionTraceEvents: String, TraceEventItem { case pairingUriValidationSuccess = "pairing_uri_validation_success" case pairingStarted = "pairing_started" case noWssConnection = "no_wss_connection" @@ -20,7 +20,6 @@ public enum PairingExecutionTraceEvents: String, TraceEvent { public enum PairingTraceErrorEvents: String, ErrorEvent { case noInternetConnection = "no_internet_connection" case malformedPairingUri = "malformed_pairing_uri" - case activePairingAlreadyExists = "active_pairing_already_exists" case subscribePairingTopicFailure = "subscribe_pairing_topic_failure" case pairingExpired = "pairing_expired" case proposalExpired = "proposal_expired" diff --git a/Sources/Events/ExecutionTraces/SessionApproveExecutionTraceEvents.swift b/Sources/Events/ExecutionTraces/SessionApproveExecutionTraceEvents.swift index 95cc48ca6..6879efa28 100644 --- a/Sources/Events/ExecutionTraces/SessionApproveExecutionTraceEvents.swift +++ b/Sources/Events/ExecutionTraces/SessionApproveExecutionTraceEvents.swift @@ -1,7 +1,7 @@ import Foundation -public enum SessionApproveExecutionTraceEvents: String, TraceEvent { +public enum SessionApproveExecutionTraceEvents: String, TraceEventItem { case approvingSessionProposal = "approving_session_proposal" case sessionNamespacesValidationStarted = "session_namespaces_validation_started" case sessionNamespacesValidationSuccess = "session_namespaces_validation_success" diff --git a/Sources/Events/ExecutionTraces/SessionAuthenticateTraceEvents.swift b/Sources/Events/ExecutionTraces/SessionAuthenticateTraceEvents.swift index 88b2f0696..dd2aa13f9 100644 --- a/Sources/Events/ExecutionTraces/SessionAuthenticateTraceEvents.swift +++ b/Sources/Events/ExecutionTraces/SessionAuthenticateTraceEvents.swift @@ -1,7 +1,7 @@ import Foundation -public enum SessionAuthenticateTraceEvents: String, TraceEvent { +public enum SessionAuthenticateTraceEvents: String, TraceEventItem { case signatureVerificationStarted = "signature_verification_started" case signatureVerificationSuccess = "signature_verification_success" case requestParamsRetrieved = "request_params_retrieved" diff --git a/Sources/Events/MessageEvent.swift b/Sources/Events/MessageEvent.swift new file mode 100644 index 000000000..5c884aee2 --- /dev/null +++ b/Sources/Events/MessageEvent.swift @@ -0,0 +1,33 @@ + +import Foundation + +struct MessageEvent: Codable { + struct Props: Codable { + let event: String = "SUCCESS" + let type: String + let properties: Properties + } + + struct Properties: Codable { + let correlationId: Int64 + let clientId: String + let direction: Direction + + // Custom CodingKeys to map Swift property names to JSON keys + enum CodingKeys: String, CodingKey { + case correlationId = "correlation_id" + case clientId = "client_id" + case direction + } + } + + enum Direction: String, Codable { + case sent + case received + } + + let eventId: String + let bundleId: String + let timestamp: Int64 + let props: Props +} diff --git a/Sources/Events/MessageEventProperties.swift b/Sources/Events/MessageEventProperties.swift new file mode 100644 index 000000000..7bb3aea50 --- /dev/null +++ b/Sources/Events/MessageEventProperties.swift @@ -0,0 +1,62 @@ +import Foundation + +protocol MessageEventsStorage { + func saveMessageEvent(_ eventType: MessageEventType) + func fetchMessageEvents() -> [MessageEvent] + func clearMessageEvents() +} + +class UserDefaultsMessageEventsStorage: MessageEventsStorage { + private let messageEventsKey = "com.walletconnect.sdk.messageEvents" + private let maxEvents = 200 + + func saveMessageEvent(_ eventType: MessageEventType) { + let correlationId = eventType.rpcId.integer + let type = "\(eventType.tag)" + let bundleId = Bundle.main.bundleIdentifier ?? "Unknown" + let clientId = (try? Networking.interactor.getClientId()) ?? "Unknown" + + let props = MessageEvent.Props( + type: type, + properties: MessageEvent.Properties( + correlationId: correlationId, + clientId: clientId, + direction: eventType.direction + ) + ) + + let event = MessageEvent( + eventId: UUID().uuidString, + bundleId: bundleId, + timestamp: Int64(Date().timeIntervalSince1970 * 1000), + props: props + ) + + // Fetch existing events from UserDefaults + var existingEvents = fetchMessageEvents() + existingEvents.append(event) + + // Ensure we keep only the last 200 events + if existingEvents.count > maxEvents { + existingEvents = Array(existingEvents.suffix(maxEvents)) + } + + // Save updated events back to UserDefaults + if let encoded = try? JSONEncoder().encode(existingEvents) { + UserDefaults.standard.set(encoded, forKey: messageEventsKey) + } + } + + func fetchMessageEvents() -> [MessageEvent] { + if let data = UserDefaults.standard.data(forKey: messageEventsKey), + let events = try? JSONDecoder().decode([MessageEvent].self, from: data) { + // Return only the last 200 events + return Array(events.suffix(maxEvents)) + } + return [] + } + + func clearMessageEvents() { + UserDefaults.standard.removeObject(forKey: messageEventsKey) + } +} diff --git a/Sources/Events/MessageEventType.swift b/Sources/Events/MessageEventType.swift new file mode 100644 index 000000000..53aa30d83 --- /dev/null +++ b/Sources/Events/MessageEventType.swift @@ -0,0 +1,63 @@ + +import Foundation + +public enum MessageEventType { + case sessionAuthenticateLinkModeSent(RPCID) + case sessionAuthenticateLinkModeReceived(RPCID) + case sessionAuthenticateLinkModeResponseApproveSent(RPCID) + case sessionAuthenticateLinkModeResponseApproveReceived(RPCID) + case sessionAuthenticateLinkModeResponseRejectSent(RPCID) + case sessionAuthenticateLinkModeResponseRejectReceived(RPCID) + case sessionRequestLinkModeSent(RPCID) + case sessionRequestLinkModeReceived(RPCID) + case sessionRequestLinkModeResponseSent(RPCID) + case sessionRequestLinkModeResponseReceived(RPCID) + + var rpcId: RPCID { + switch self { + case .sessionAuthenticateLinkModeSent(let rpcId), + .sessionAuthenticateLinkModeReceived(let rpcId), + .sessionAuthenticateLinkModeResponseApproveSent(let rpcId), + .sessionAuthenticateLinkModeResponseApproveReceived(let rpcId), + .sessionAuthenticateLinkModeResponseRejectSent(let rpcId), + .sessionAuthenticateLinkModeResponseRejectReceived(let rpcId), + .sessionRequestLinkModeSent(let rpcId), + .sessionRequestLinkModeReceived(let rpcId), + .sessionRequestLinkModeResponseSent(let rpcId), + .sessionRequestLinkModeResponseReceived(let rpcId): + return rpcId + } + } + + var tag: Int { + switch self { + case .sessionAuthenticateLinkModeSent, .sessionAuthenticateLinkModeReceived: + return 1122 + case .sessionAuthenticateLinkModeResponseApproveSent, .sessionAuthenticateLinkModeResponseApproveReceived: + return 1123 + case .sessionAuthenticateLinkModeResponseRejectSent, .sessionAuthenticateLinkModeResponseRejectReceived: + return 1124 + case .sessionRequestLinkModeSent, .sessionRequestLinkModeReceived: + return 1125 + case .sessionRequestLinkModeResponseSent, .sessionRequestLinkModeResponseReceived: + return 1126 + } + } + + var direction: MessageEvent.Direction { + switch self { + case .sessionAuthenticateLinkModeSent, + .sessionAuthenticateLinkModeResponseApproveSent, + .sessionAuthenticateLinkModeResponseRejectSent, + .sessionRequestLinkModeSent, + .sessionRequestLinkModeResponseSent: + return .sent + case .sessionAuthenticateLinkModeReceived, + .sessionAuthenticateLinkModeResponseApproveReceived, + .sessionAuthenticateLinkModeResponseRejectReceived, + .sessionRequestLinkModeReceived, + .sessionRequestLinkModeResponseReceived: + return .received + } + } +} diff --git a/Sources/Events/NetworkingService.swift b/Sources/Events/NetworkingService.swift index a8a03b620..233500a16 100644 --- a/Sources/Events/NetworkingService.swift +++ b/Sources/Events/NetworkingService.swift @@ -1,7 +1,7 @@ import Foundation protocol NetworkingServiceProtocol { - func sendEvents(_ events: [Event]) async throws -> Bool + func sendEvents(_ events: [T]) async throws -> Bool } class NetworkingService: NetworkingServiceProtocol { @@ -16,7 +16,7 @@ class NetworkingService: NetworkingServiceProtocol { self.sdkVersion = sdkVersion } - func sendEvents(_ events: [Event]) async throws -> Bool { + func sendEvents(_ events: [T]) async throws -> Bool { var request = URLRequest(url: apiURL) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") @@ -51,7 +51,7 @@ class MockNetworkingService: NetworkingServiceProtocol { var shouldFail = false var attemptCount = 0 - func sendEvents(_ events: [Event]) async throws -> Bool { + func sendEvents(_ events: [T]) async throws -> Bool where T : Encodable { attemptCount += 1 if shouldFail { throw NSError(domain: "MockError", code: -1, userInfo: nil) diff --git a/Sources/Events/TraceEvent.swift b/Sources/Events/TraceEvent.swift new file mode 100644 index 000000000..5127891d7 --- /dev/null +++ b/Sources/Events/TraceEvent.swift @@ -0,0 +1,19 @@ +import Foundation + +struct TraceEvent: Codable { + struct Props: Codable { + let event: String + let type: String + let properties: Properties? + } + + struct Properties: Codable { + let topic: String? + let trace: [String]? + } + let eventId: String + let bundleId: String + let timestamp: Int64 + let props: Props +} + diff --git a/Sources/WalletConnectModal/Modal/ModalInteractor.swift b/Sources/WalletConnectModal/Modal/ModalInteractor.swift index fe18b4f48..acd41c5ce 100644 --- a/Sources/WalletConnectModal/Modal/ModalInteractor.swift +++ b/Sources/WalletConnectModal/Modal/ModalInteractor.swift @@ -4,7 +4,7 @@ import Foundation protocol ModalSheetInteractor { func getWallets(page: Int, entries: Int) async throws -> (Int, [Wallet]) - func createPairingAndConnect() async throws -> WalletConnectURI? + func createPairingAndConnect() async throws -> WalletConnectURI var sessionSettlePublisher: AnyPublisher { get } var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never> { get } @@ -34,7 +34,7 @@ final class DefaultModalSheetInteractor: ModalSheetInteractor { return (response.count, response.data.compactMap { $0 }) } - func createPairingAndConnect() async throws -> WalletConnectURI? { - try await WalletConnectModal.instance.connect(topic: nil) + func createPairingAndConnect() async throws -> WalletConnectURI { + try await WalletConnectModal.instance.connect() } } diff --git a/Sources/WalletConnectModal/Modal/ModalViewModel.swift b/Sources/WalletConnectModal/Modal/ModalViewModel.swift index e78aa56e1..3a01cba3c 100644 --- a/Sources/WalletConnectModal/Modal/ModalViewModel.swift +++ b/Sources/WalletConnectModal/Modal/ModalViewModel.swift @@ -102,10 +102,7 @@ final class ModalViewModel: ObservableObject { @MainActor func createURI() async { do { - guard let wcUri = try await interactor.createPairingAndConnect() else { - toast = Toast(style: .error, message: "Failed to create pairing") - return - } + let wcUri = try await interactor.createPairingAndConnect() uri = wcUri.absoluteString deeplinkUri = wcUri.deeplinkUri } catch { diff --git a/Sources/WalletConnectModal/WalletConnectModalClient.swift b/Sources/WalletConnectModal/WalletConnectModalClient.swift index b6a8f41be..cb16847b9 100644 --- a/Sources/WalletConnectModal/WalletConnectModalClient.swift +++ b/Sources/WalletConnectModal/WalletConnectModalClient.swift @@ -50,11 +50,11 @@ public class WalletConnectModalClient { // MARK: - Private Properties - private let signClient: SignClientProtocol + private let signClient: SignClient private let pairingClient: PairingClientProtocol & PairingInteracting & PairingRegisterer init( - signClient: SignClientProtocol, + signClient: SignClient, pairingClient: PairingClientProtocol & PairingInteracting & PairingRegisterer ) { self.signClient = signClient @@ -69,61 +69,15 @@ public class WalletConnectModalClient { /// For proposing a session to a wallet. /// Function will propose a session on existing pairing or create new one if not specified /// Namespaces from WalletConnectModal.config will be used - /// - Parameters: - /// - topic: pairing topic - public func connect( - topic: String? - ) async throws -> WalletConnectURI? { - if let topic = topic { - try pairingClient.validatePairingExistance(topic) - try await signClient.connect( - requiredNamespaces: WalletConnectModal.config.sessionParams.requiredNamespaces, - optionalNamespaces: WalletConnectModal.config.sessionParams.optionalNamespaces, - sessionProperties: WalletConnectModal.config.sessionParams.sessionProperties, - topic: topic - ) - return nil - } else { - let pairingURI = try await pairingClient.create() - try await signClient.connect( + public func connect() async throws -> WalletConnectURI { + let pairingURI = try await signClient.connect( requiredNamespaces: WalletConnectModal.config.sessionParams.requiredNamespaces, optionalNamespaces: WalletConnectModal.config.sessionParams.optionalNamespaces, - sessionProperties: WalletConnectModal.config.sessionParams.sessionProperties, - topic: pairingURI.topic + sessionProperties: WalletConnectModal.config.sessionParams.sessionProperties ) return pairingURI } - } - - /// For proposing a session to a wallet. - /// Function will propose a session on existing pairing. - /// - Parameters: - /// - requiredNamespaces: required namespaces for a session - /// - topic: pairing topic - public func connect( - requiredNamespaces: [String: ProposalNamespace], - optionalNamespaces: [String: ProposalNamespace]? = nil, - sessionProperties: [String: String]? = nil, - topic: String - ) async throws { - try await signClient.connect( - requiredNamespaces: requiredNamespaces, - optionalNamespaces: optionalNamespaces, - sessionProperties: sessionProperties, - topic: topic - ) - } - - /// Ping method allows to check if peer client is online and is subscribing for given topic - /// - /// Should Error: - /// - When the session topic is not found - /// - /// - Parameters: - /// - topic: Topic of a session - public func ping(topic: String) async throws { - try await pairingClient.ping(topic: topic) - } + /// For sending JSON-RPC requests to wallet. /// - Parameters: diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 233fcd76b..7443adeee 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -2,9 +2,6 @@ import Foundation import Combine public class PairingClient: PairingRegisterer, PairingInteracting, PairingClientProtocol { - public var pingResponsePublisher: AnyPublisher<(String), Never> { - pingResponsePublisherSubject.eraseToAnyPublisher() - } public var pairingDeletePublisher: AnyPublisher<(code: Int, message: String), Never> { pairingDeleteRequestSubscriber.deletePublisherSubject.eraseToAnyPublisher() } @@ -21,14 +18,10 @@ public class PairingClient: PairingRegisterer, PairingInteracting, PairingClient private let pairingStorage: WCPairingStorage private let walletPairService: WalletPairService private let appPairService: AppPairService - private let appPairActivateService: AppPairActivationService - private var pingResponsePublisherSubject = PassthroughSubject() private let logger: ConsoleLogging - private let pingService: PairingPingService private let networkingInteractor: NetworkInteracting private let pairingRequestsSubscriber: PairingRequestsSubscriber private let pairingsProvider: PairingsProvider - private let pairingDeleteRequester: PairingDeleteRequester private let resubscribeService: PairingResubscribeService private let expirationService: ExpirationService private let pairingDeleteRequestSubscriber: PairingDeleteRequestSubscriber @@ -46,14 +39,11 @@ public class PairingClient: PairingRegisterer, PairingInteracting, PairingClient networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, - pairingDeleteRequester: PairingDeleteRequester, pairingDeleteRequestSubscriber: PairingDeleteRequestSubscriber, resubscribeService: PairingResubscribeService, expirationService: ExpirationService, pairingRequestsSubscriber: PairingRequestsSubscriber, - appPairActivateService: AppPairActivationService, cleanupService: PairingCleanupService, - pingService: PairingPingService, socketConnectionStatusPublisher: AnyPublisher, pairingsProvider: PairingsProvider, pairingStateProvider: PairingStateProvider @@ -64,26 +54,16 @@ public class PairingClient: PairingRegisterer, PairingInteracting, PairingClient self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger - self.pairingDeleteRequester = pairingDeleteRequester self.pairingDeleteRequestSubscriber = pairingDeleteRequestSubscriber - self.appPairActivateService = appPairActivateService self.resubscribeService = resubscribeService self.expirationService = expirationService self.cleanupService = cleanupService - self.pingService = pingService self.pairingRequestsSubscriber = pairingRequestsSubscriber self.pairingsProvider = pairingsProvider self.pairingStateProvider = pairingStateProvider - setUpPublishers() setUpExpiration() } - private func setUpPublishers() { - pingService.onResponse = { [unowned self] topic in - pingResponsePublisherSubject.send(topic) - } - } - private func setUpExpiration() { expirationService.setupExpirationHandling() } @@ -103,10 +83,6 @@ public class PairingClient: PairingRegisterer, PairingInteracting, PairingClient return try await appPairService.create(supportedMethods: methods) } - public func activate(pairingTopic: String, peerMetadata: AppMetadata?) { - appPairActivateService.activate(for: pairingTopic, peerMetadata: peerMetadata) - } - public func setReceived(pairingTopic: String) { guard var pairing = pairingStorage.getPairing(forTopic: pairingTopic) else { return logger.error("Pairing not found for topic: \(pairingTopic)") @@ -124,17 +100,8 @@ public class PairingClient: PairingRegisterer, PairingInteracting, PairingClient try pairingsProvider.getPairing(for: topic) } - public func ping(topic: String) async throws { - try await pingService.ping(topic: topic) - } - - public func disconnect(topic: String) async { - await pairingDeleteRequester.delete(topic: topic) - } - - public func validatePairingExistance(_ topic: String) throws { - _ = try pairingsProvider.getPairing(for: topic) - } + @available(*, deprecated, message: "This method is deprecated. Pairing will disconnect automatically") + public func disconnect(topic: String) async {} public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 26293c3ad..4cc01b94a 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -32,9 +32,6 @@ public struct PairingClientFactory { let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingClient, pairingStorage: pairingStore, logger: logger) let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) let cleanupService = PairingCleanupService(pairingStore: pairingStore, kms: kms) - let pairingDeleteRequester = PairingDeleteRequester(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) - let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) - let appPairActivateService = AppPairActivationService(pairingStorage: pairingStore, logger: logger) let expirationService = ExpirationService(pairingStorage: pairingStore, networkInteractor: networkingClient, kms: kms) let resubscribeService = PairingResubscribeService(networkInteractor: networkingClient, pairingStorage: pairingStore) let pairingDeleteRequestSubscriber = PairingDeleteRequestSubscriber(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) @@ -46,14 +43,11 @@ public struct PairingClientFactory { networkingInteractor: networkingClient, logger: logger, walletPairService: walletPairService, - pairingDeleteRequester: pairingDeleteRequester, pairingDeleteRequestSubscriber: pairingDeleteRequestSubscriber, resubscribeService: resubscribeService, expirationService: expirationService, pairingRequestsSubscriber: pairingRequestsSubscriber, - appPairActivateService: appPairActivateService, cleanupService: cleanupService, - pingService: pingService, socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, pairingsProvider: pairingsProvider, pairingStateProvider: pairingStateProvider diff --git a/Sources/WalletConnectPairing/PairingInteracting.swift b/Sources/WalletConnectPairing/PairingInteracting.swift index 7db1ccd47..6fc1e30d5 100644 --- a/Sources/WalletConnectPairing/PairingInteracting.swift +++ b/Sources/WalletConnectPairing/PairingInteracting.swift @@ -9,8 +9,6 @@ public protocol PairingInteracting { func getPairing(for topic: String) throws -> Pairing - func ping(topic: String) async throws - func disconnect(topic: String) async throws #if DEBUG diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index e09cf9a42..a6de58164 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -6,7 +6,5 @@ public protocol PairingRegisterer { method: ProtocolMethod ) -> AnyPublisher, Never> - func activate(pairingTopic: String, peerMetadata: AppMetadata?) func setReceived(pairingTopic: String) - func validatePairingExistance(_ topic: String) throws } diff --git a/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift b/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift deleted file mode 100644 index 3ce060f22..000000000 --- a/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation -import Combine - -final class AppPairActivationService { - enum Errors: Error { - case pairingNotFound - } - - private let pairingStorage: WCPairingStorage - private let logger: ConsoleLogging - - init(pairingStorage: WCPairingStorage, logger: ConsoleLogging) { - self.pairingStorage = pairingStorage - self.logger = logger - } - - func activate(for topic: String, peerMetadata: AppMetadata?) { - guard var pairing = pairingStorage.getPairing(forTopic: topic) else { - return logger.error("Pairing not found for topic: \(topic)") - } - - if !pairing.active { - pairing.activate() - } else { - try? pairing.updateExpiry() - } - - pairing.updatePeerMetadata(peerMetadata) - pairingStorage.setPairing(pairing) - } -} diff --git a/Sources/WalletConnectPairing/Services/Common/Delete/PairingDeleteRequester.swift b/Sources/WalletConnectPairing/Services/Common/Delete/PairingDeleteRequester.swift deleted file mode 100644 index c8c08ba34..000000000 --- a/Sources/WalletConnectPairing/Services/Common/Delete/PairingDeleteRequester.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Foundation - -class PairingDeleteRequester { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let pairingStorage: WCPairingStorage - private let logger: ConsoleLogging - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStorage: WCPairingStorage, - logger: ConsoleLogging) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.pairingStorage = pairingStorage - self.logger = logger - } - - func delete(topic: String) async { - let reason = PairingReasonCode.userDisconnected - let protocolMethod = PairingProtocolMethod.delete - let pairingDeleteParams = PairingDeleteParams(code: reason.code, message: reason.message) - logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - let request = RPCRequest(method: protocolMethod.method, params: pairingDeleteParams) - try? await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) - pairingStorage.delete(topic: topic) - kms.deleteSymmetricKey(for: topic) - networkingInteractor.unsubscribe(topic: topic) - } -} diff --git a/Sources/WalletConnectPairing/Services/Common/PairingStateProvider.swift b/Sources/WalletConnectPairing/Services/Common/PairingStateProvider.swift index ff917e33d..89ea7db8e 100644 --- a/Sources/WalletConnectPairing/Services/Common/PairingStateProvider.swift +++ b/Sources/WalletConnectPairing/Services/Common/PairingStateProvider.swift @@ -23,7 +23,7 @@ class PairingStateProvider { } private func checkPairingState() { - let pairingStateActive = !pairingStorage.getAll().allSatisfy { $0.active || $0.requestReceived } + let pairingStateActive = !pairingStorage.getAll().allSatisfy { $0.requestReceived } if lastPairingState != pairingStateActive { pairingStatePublisherSubject.send(pairingStateActive) diff --git a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift deleted file mode 100644 index e1250bd5a..000000000 --- a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - -public class PairingPingService { - private let pairingStorage: WCPairingStorage - private let pingRequester: PingRequester - private let pingResponder: PingResponder - private let pingResponseSubscriber: PingResponseSubscriber - - public var onResponse: ((String) -> Void)? { - get { - return pingResponseSubscriber.onResponse - } - set { - pingResponseSubscriber.onResponse = newValue - } - } - - public init( - pairingStorage: WCPairingStorage, - networkingInteractor: NetworkInteracting, - logger: ConsoleLogging) { - let protocolMethod = PairingProtocolMethod.ping - self.pairingStorage = pairingStorage - self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: protocolMethod) - self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) - self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) - } - - public func ping(topic: String) async throws { - guard pairingStorage.hasPairing(forTopic: topic) else { return } - try await pingRequester.ping(topic: topic) - } -} diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index c5607aa59..a025e49e1 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -2,7 +2,6 @@ import Foundation actor WalletPairService { enum Errors: Error { - case pairingAlreadyExist(topic: String) case networkNotConnected } @@ -31,35 +30,35 @@ actor WalletPairService { func pair(_ uri: WalletConnectURI) async throws { eventsClient.startTrace(topic: uri.topic) - eventsClient.saveEvent(PairingExecutionTraceEvents.pairingStarted) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.pairingStarted) logger.debug("Pairing with uri: \(uri)") guard try !pairingHasPendingRequest(for: uri.topic) else { - eventsClient.saveEvent(PairingExecutionTraceEvents.pairingHasPendingRequest) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.pairingHasPendingRequest) logger.debug("Pairing with topic (\(uri.topic)) has pending request") return } if !networkingInteractor.isSocketConnected { - eventsClient.saveEvent(PairingExecutionTraceEvents.noWssConnection) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.noWssConnection) } let pairing = WCPairing(uri: uri) let symKey = try SymmetricKey(hex: uri.symKey) try kms.setSymmetricKey(symKey, for: pairing.topic) pairingStorage.setPairing(pairing) - eventsClient.saveEvent(PairingExecutionTraceEvents.storeNewPairing) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.storeNewPairing) let networkConnectionStatus = await resolveNetworkConnectionStatus() guard networkConnectionStatus == .connected else { logger.debug("Pairing failed - Network is not connected") - eventsClient.saveEvent(PairingTraceErrorEvents.noInternetConnection) + eventsClient.saveTraceEvent(PairingTraceErrorEvents.noInternetConnection) throw Errors.networkNotConnected } - eventsClient.saveEvent(PairingExecutionTraceEvents.subscribingPairingTopic) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.subscribingPairingTopic) do { try await networkingInteractor.subscribe(topic: pairing.topic) } catch { logger.debug("Failed to subscribe to topic: \(pairing.topic)") - eventsClient.saveEvent(PairingTraceErrorEvents.subscribePairingTopicFailure) + eventsClient.saveTraceEvent(PairingTraceErrorEvents.subscribePairingTopicFailure) throw error } } @@ -71,21 +70,15 @@ extension WalletPairService { guard let pairing = pairingStorage.getPairing(forTopic: topic), pairing.requestReceived else { return false } - - if pairing.active { - eventsClient.saveEvent(PairingTraceErrorEvents.activePairingAlreadyExists) - throw Errors.pairingAlreadyExist(topic: topic) - } - + let pendingRequests = history.getPending() .compactMap { record -> RPCRequest? in (record.topic == pairing.topic) ? record.request : nil } - guard !pendingRequests.isEmpty else { return false } pendingRequests.forEach { request in - eventsClient.saveEvent(PairingExecutionTraceEvents.emitSessionProposal) + eventsClient.saveTraceEvent(PairingExecutionTraceEvents.emitSessionProposal) networkingInteractor.handleHistoryRequest(topic: topic, request: request) } return true @@ -110,7 +103,6 @@ extension WalletPairService { extension WalletPairService.Errors: LocalizedError { var errorDescription: String? { switch self { - case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) is already active" case .networkNotConnected: return "Pairing failed. You seem to be offline" } } diff --git a/Sources/WalletConnectPairing/Types/Pairing.swift b/Sources/WalletConnectPairing/Types/Pairing.swift index 9edd37941..b7b783364 100644 --- a/Sources/WalletConnectPairing/Types/Pairing.swift +++ b/Sources/WalletConnectPairing/Types/Pairing.swift @@ -4,15 +4,11 @@ import Foundation */ public struct Pairing { public let topic: String - public let peer: AppMetadata? public let expiryDate: Date - public let active: Bool init(_ pairing: WCPairing) { self.topic = pairing.topic - self.peer = pairing.peerMetadata self.expiryDate = pairing.expiryDate - self.active = pairing.active } } diff --git a/Sources/WalletConnectPairing/Types/WCPairing.swift b/Sources/WalletConnectPairing/Types/WCPairing.swift index 9bd66f103..a471fa132 100644 --- a/Sources/WalletConnectPairing/Types/WCPairing.swift +++ b/Sources/WalletConnectPairing/Types/WCPairing.swift @@ -8,9 +8,7 @@ public struct WCPairing: SequenceObject { public let topic: String public let relay: RelayProtocolOptions - public private (set) var peerMetadata: AppMetadata? public private (set) var expiryDate: Date - public private (set) var active: Bool public private (set) var requestReceived: Bool public private (set) var methods: [String]? @@ -31,47 +29,25 @@ public struct WCPairing: SequenceObject { public init(uri: WalletConnectURI) { self.topic = uri.topic self.relay = uri.relay - self.active = false self.requestReceived = false self.methods = uri.methods self.expiryDate = Date(timeIntervalSince1970: TimeInterval(uri.expiryTimestamp)) } - - public mutating func activate() { - active = true - try? updateExpiry() - } public mutating func receivedRequest() { requestReceived = true } - - public mutating func updatePeerMetadata(_ metadata: AppMetadata?) { - peerMetadata = metadata - } - - public mutating func updateExpiry(_ ttl: TimeInterval = WCPairing.timeToLiveActive) throws { - let now = Self.dateInitializer() - let newExpiryDate = now.advanced(by: ttl) - let maxExpiryDate = now.advanced(by: Self.timeToLiveActive) - guard newExpiryDate > expiryDate && newExpiryDate <= maxExpiryDate else { - throw Errors.invalidUpdateExpiryValue - } - expiryDate = newExpiryDate - } } #if DEBUG extension WCPairing { - static func stub(expiryDate: Date = Date(timeIntervalSinceNow: 10000), isActive: Bool = true, topic: String = String.generateTopic()) -> WCPairing { - WCPairing(topic: topic, relay: RelayProtocolOptions.stub(), peerMetadata: AppMetadata.stub(), isActive: isActive, expiryDate: expiryDate) + static func stub(expiryDate: Date = Date(timeIntervalSinceNow: 10000), topic: String = String.generateTopic()) -> WCPairing { + WCPairing(topic: topic, relay: RelayProtocolOptions.stub(), expiryDate: expiryDate) } - init(topic: String, relay: RelayProtocolOptions, peerMetadata: AppMetadata, isActive: Bool = false, requestReceived: Bool = false, expiryDate: Date) { + init(topic: String, relay: RelayProtocolOptions, requestReceived: Bool = false, expiryDate: Date) { self.topic = topic self.relay = relay - self.peerMetadata = peerMetadata - self.active = isActive self.requestReceived = requestReceived self.expiryDate = expiryDate } diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 0aa2b143b..3e93c0cec 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.19.6"} +{"version": "1.20.0"} diff --git a/Sources/WalletConnectSign/Auth/Link/LinkAuthRequestSubscriber.swift b/Sources/WalletConnectSign/Auth/Link/LinkAuthRequestSubscriber.swift index c245a19d2..99000ef64 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkAuthRequestSubscriber.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkAuthRequestSubscriber.swift @@ -8,6 +8,7 @@ class LinkAuthRequestSubscriber { private let envelopesDispatcher: LinkEnvelopesDispatcher private let verifyClient: VerifyClientProtocol private let verifyContextStore: CodableStore + private let eventsClient: EventsClientProtocol var onRequest: (((request: AuthenticationRequest, context: VerifyContext?)) -> Void)? @@ -17,13 +18,16 @@ class LinkAuthRequestSubscriber { kms: KeyManagementServiceProtocol, envelopesDispatcher: LinkEnvelopesDispatcher, verifyClient: VerifyClientProtocol, - verifyContextStore: CodableStore + verifyContextStore: CodableStore, + eventsClient: EventsClientProtocol ) { self.logger = logger self.kms = kms self.envelopesDispatcher = envelopesDispatcher self.verifyClient = verifyClient self.verifyContextStore = verifyContextStore + self.eventsClient = eventsClient + subscribeForRequest() } @@ -33,6 +37,8 @@ class LinkAuthRequestSubscriber { .requestSubscription(on: SessionAuthenticatedProtocolMethod.responseApprove().method) .sink { [unowned self] (payload: RequestSubscriptionPayload) in + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeReceived(payload.id)) } + logger.debug("LinkAuthRequestSubscriber: Received request") diff --git a/Sources/WalletConnectSign/Auth/Link/LinkAuthRequester.swift b/Sources/WalletConnectSign/Auth/Link/LinkAuthRequester.swift index 228baef25..4b5ed89e2 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkAuthRequester.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkAuthRequester.swift @@ -13,6 +13,7 @@ actor LinkAuthRequester { private let authResponseTopicRecordsStore: CodableStore private let linkEnvelopesDispatcher: LinkEnvelopesDispatcher private let linkModeLinksStore: CodableStore + private let eventsClient: EventsClientProtocol init(kms: KeyManagementService, appMetadata: AppMetadata, @@ -20,7 +21,8 @@ actor LinkAuthRequester { iatProvader: IATProvider, authResponseTopicRecordsStore: CodableStore, linkEnvelopesDispatcher: LinkEnvelopesDispatcher, - linkModeLinksStore: CodableStore) { + linkModeLinksStore: CodableStore, + eventsClient: EventsClientProtocol) { self.kms = kms self.appMetadata = appMetadata self.logger = logger @@ -28,6 +30,7 @@ actor LinkAuthRequester { self.authResponseTopicRecordsStore = authResponseTopicRecordsStore self.linkEnvelopesDispatcher = linkEnvelopesDispatcher self.linkModeLinksStore = linkModeLinksStore + self.eventsClient = eventsClient } func request(params: AuthRequestParams, walletUniversalLink: String) async throws -> String { @@ -50,8 +53,6 @@ actor LinkAuthRequester { let requester = Participant(publicKey: pubKey.hexRepresentation, metadata: appMetadata) let payload = AuthPayload(requestParams: params, iat: iatProvader.iat) - - let sessionAuthenticateRequestParams = SessionAuthenticateRequestParams(requester: requester, authPayload: payload, ttl: params.ttl) let authResponseTopicRecord = AuthResponseTopicRecord(topic: responseTopic, unixTimestamp: sessionAuthenticateRequestParams.expiryTimestamp) authResponseTopicRecordsStore.set(authResponseTopicRecord, forKey: responseTopic) @@ -60,8 +61,10 @@ actor LinkAuthRequester { logger.debug("LinkAuthRequester: sending request") - return try await linkEnvelopesDispatcher.request(topic: UUID().uuidString,request: request, peerUniversalLink: walletUniversalLink, envelopeType: .type2) + let envelope = try await linkEnvelopesDispatcher.request(topic: UUID().uuidString,request: request, peerUniversalLink: walletUniversalLink, envelopeType: .type2) + Task { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeSent(request.id!)) } + return envelope } private func createRecapUrn(methods: [String]) throws -> String { diff --git a/Sources/WalletConnectSign/Auth/Link/LinkSessionAuthenticateResponder.swift b/Sources/WalletConnectSign/Auth/Link/LinkSessionAuthenticateResponder.swift index 31d6ba9fe..f5eb9f0c3 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkSessionAuthenticateResponder.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkSessionAuthenticateResponder.swift @@ -12,6 +12,7 @@ actor LinkSessionAuthenticateResponder { private let util: ApproveSessionAuthenticateUtil private let walletErrorResponder: WalletErrorResponder private let verifyContextStore: CodableStore + private let eventsClient: EventsClientProtocol init( linkEnvelopesDispatcher: LinkEnvelopesDispatcher, @@ -20,7 +21,8 @@ actor LinkSessionAuthenticateResponder { metadata: AppMetadata, approveSessionAuthenticateUtil: ApproveSessionAuthenticateUtil, walletErrorResponder: WalletErrorResponder, - verifyContextStore: CodableStore + verifyContextStore: CodableStore, + eventsClient: EventsClientProtocol ) { self.linkEnvelopesDispatcher = linkEnvelopesDispatcher self.logger = logger @@ -29,6 +31,7 @@ actor LinkSessionAuthenticateResponder { self.metadata = metadata self.util = approveSessionAuthenticateUtil self.walletErrorResponder = walletErrorResponder + self.eventsClient = eventsClient } func respond(requestId: RPCID, auths: [Cacao]) async throws -> (Session?, String) { @@ -60,6 +63,8 @@ actor LinkSessionAuthenticateResponder { let url = try await linkEnvelopesDispatcher.respond(topic: responseTopic, response: response, peerUniversalLink: peerUniversalLink, envelopeType: .type1(pubKey: responseKeys.publicKey.rawRepresentation)) + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeResponseApproveSent(requestId)) } + let session = try util.createSession( response: responseParams, pairingTopic: pairingTopic, diff --git a/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestResponseSubscriber.swift b/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestResponseSubscriber.swift index e0581269a..536f03937 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestResponseSubscriber.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestResponseSubscriber.swift @@ -5,17 +5,22 @@ import Combine class LinkSessionRequestResponseSubscriber { private var publishers = [AnyCancellable]() private let envelopesDispatcher: LinkEnvelopesDispatcher + private let eventsClient: EventsClientProtocol var onSessionResponse: ((Response) -> Void)? - init(envelopesDispatcher: LinkEnvelopesDispatcher) { + init(envelopesDispatcher: LinkEnvelopesDispatcher, + eventsClient: EventsClientProtocol + ) { self.envelopesDispatcher = envelopesDispatcher + self.eventsClient = eventsClient setupRequestSubscription() } func setupRequestSubscription() { envelopesDispatcher.responseErrorSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeReceived(payload.id)) } onSessionResponse?(Response( id: payload.id, topic: payload.topic, @@ -27,6 +32,7 @@ class LinkSessionRequestResponseSubscriber { envelopesDispatcher.responseSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeReceived(payload.id)) } Task(priority: .high) { onSessionResponse?(Response( id: payload.id, diff --git a/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestSubscriber.swift b/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestSubscriber.swift index 08827346d..be398636d 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestSubscriber.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkSessionRequestSubscriber.swift @@ -8,17 +8,20 @@ class LinkSessionRequestSubscriber { private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let envelopesDispatcher: LinkEnvelopesDispatcher + private let eventsClient: EventsClientProtocol init( sessionRequestsProvider: SessionRequestsProvider, sessionStore: WCSessionStorage, logger: ConsoleLogging, - envelopesDispatcher: LinkEnvelopesDispatcher + envelopesDispatcher: LinkEnvelopesDispatcher, + eventsClient: EventsClientProtocol ) { self.sessionRequestsProvider = sessionRequestsProvider self.sessionStore = sessionStore self.logger = logger self.envelopesDispatcher = envelopesDispatcher + self.eventsClient = eventsClient setupRequestSubscription() } @@ -29,6 +32,8 @@ class LinkSessionRequestSubscriber { private func setupRequestSubscription() { envelopesDispatcher.requestSubscription(on: SessionRequestProtocolMethod().method) .sink { [unowned self] (payload: RequestSubscriptionPayload) in + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeReceived(payload.id)) } + Task(priority: .high) { onSessionRequest(payload: payload) } diff --git a/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift b/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift index 602827661..e3e600aeb 100644 --- a/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift +++ b/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift @@ -20,6 +20,8 @@ class AuthResponseSubscriber { private let authResponseTopicRecordsStore: CodableStore private let linkModeLinksStore: CodableStore private let supportLinkMode: Bool + private let pairingStore: WCPairingStorage + private let eventsClient: EventsClientProtocol init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, @@ -33,7 +35,10 @@ class AuthResponseSubscriber { authResponseTopicRecordsStore: CodableStore, linkEnvelopesDispatcher: LinkEnvelopesDispatcher, linkModeLinksStore: CodableStore, - supportLinkMode: Bool) { + pairingStore: WCPairingStorage, + supportLinkMode: Bool, + eventsClient: EventsClientProtocol + ) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory @@ -47,6 +52,9 @@ class AuthResponseSubscriber { self.linkEnvelopesDispatcher = linkEnvelopesDispatcher self.linkModeLinksStore = linkModeLinksStore self.supportLinkMode = supportLinkMode + self.pairingStore = pairingStore + self.eventsClient = eventsClient + subscribeForResponse() subscribeForLinkResponse() } @@ -56,7 +64,8 @@ class AuthResponseSubscriber { .responseErrorSubscription(on: SessionAuthenticatedProtocolMethod.responseApprove()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in guard let error = AuthError(code: payload.error.code) else { return } - authResponsePublisherSubject.send((payload.id, .failure(error))) + Task { removePairing(pairingTopic: payload.topic) } + authResponsePublisherSubject.send((payload.id, .failure(error))) }.store(in: &publishers) networkingInteractor @@ -66,8 +75,8 @@ class AuthResponseSubscriber { let transportType = getTransportTypeUpgradeIfPossible(peerMetadata: payload.response.responder.metadata, requestId: payload.id) let pairingTopic = payload.topic - pairingRegisterer.activate(pairingTopic: pairingTopic, peerMetadata: nil) removeResponseTopicRecord(responseTopic: payload.topic) + Task { removePairing(pairingTopic: pairingTopic) } let requestId = payload.id let cacaos = payload.response.cacaos @@ -90,6 +99,7 @@ class AuthResponseSubscriber { private func subscribeForLinkResponse() { linkEnvelopesDispatcher.responseErrorSubscription(on: SessionAuthenticatedProtocolMethod.responseApprove()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + Task { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeResponseRejectReceived(payload.id)) } guard let error = AuthError(code: payload.error.code) else { return } authResponsePublisherSubject.send((payload.id, .failure(error))) }.store(in: &publishers) @@ -97,10 +107,11 @@ class AuthResponseSubscriber { linkEnvelopesDispatcher.responseSubscription(on: SessionAuthenticatedProtocolMethod.responseApprove()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeResponseApproveReceived(payload.id)) } + _ = getTransportTypeUpgradeIfPossible(peerMetadata: payload.response.responder.metadata, requestId: payload.id) let pairingTopic = payload.topic - pairingRegisterer.activate(pairingTopic: pairingTopic, peerMetadata: nil) removeResponseTopicRecord(responseTopic: payload.topic) let requestId = payload.id @@ -217,5 +228,10 @@ class AuthResponseSubscriber { authResponseTopicRecordsStore.delete(forKey: responseTopic) networkingInteractor.unsubscribe(topic: responseTopic) } + + private func removePairing(pairingTopic: String) { + pairingStore.delete(topic: pairingTopic) + kms.deleteSymmetricKey(for: pairingTopic) + } } diff --git a/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift b/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift index 37e89a07d..3dae4def8 100644 --- a/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift +++ b/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift @@ -12,6 +12,7 @@ actor SessionAuthenticateResponder { private let metadata: AppMetadata private let util: ApproveSessionAuthenticateUtil private let eventsClient: EventsClientProtocol + private let pairingStore: WCPairingStorage init( networkingInteractor: NetworkInteracting, @@ -22,7 +23,8 @@ actor SessionAuthenticateResponder { pairingRegisterer: PairingRegisterer, metadata: AppMetadata, approveSessionAuthenticateUtil: ApproveSessionAuthenticateUtil, - eventsClient: EventsClientProtocol + eventsClient: EventsClientProtocol, + pairingStore: WCPairingStorage ) { self.networkingInteractor = networkingInteractor self.logger = logger @@ -33,15 +35,16 @@ actor SessionAuthenticateResponder { self.metadata = metadata self.util = approveSessionAuthenticateUtil self.eventsClient = eventsClient + self.pairingStore = pairingStore } func respond(requestId: RPCID, auths: [Cacao]) async throws -> Session? { - eventsClient.saveEvent(SessionAuthenticateTraceEvents.signatureVerificationStarted) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.signatureVerificationStarted) do { try await util.recoverAndVerifySignature(cacaos: auths) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.signatureVerificationSuccess) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.signatureVerificationSuccess) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.signatureVerificationFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.signatureVerificationFailed) throw error } @@ -50,9 +53,9 @@ actor SessionAuthenticateResponder { do { (sessionAuthenticateRequestParams, pairingTopic) = try util.getsessionAuthenticateRequestParams(requestId: requestId) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.requestParamsRetrieved) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.requestParamsRetrieved) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.requestParamsRetrievalFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.requestParamsRetrievalFailed) throw error } @@ -61,17 +64,17 @@ actor SessionAuthenticateResponder { do { (responseTopic, responseKeys) = try util.generateAgreementKeys(requestParams: sessionAuthenticateRequestParams) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.agreementKeysGenerated) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.agreementKeysGenerated) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.agreementKeysGenerationFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.agreementKeysGenerationFailed) throw error } do { try kms.setAgreementSecret(responseKeys, topic: responseTopic) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.agreementSecretSet) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.agreementSecretSet) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.agreementSecretSetFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.agreementSecretSetFailed) throw error } @@ -85,24 +88,24 @@ actor SessionAuthenticateResponder { sessionSelfPubKey = try kms.createX25519KeyPair() sessionSelfPubKeyHex = sessionSelfPubKey.hexRepresentation sessionKeys = try kms.performKeyAgreement(selfPublicKey: sessionSelfPubKey, peerPublicKey: peerParticipant.publicKey) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.sessionKeysGenerated) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.sessionKeysGenerated) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.sessionKeysGenerationFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.sessionKeysGenerationFailed) throw error } let sessionTopic = sessionKeys.derivedTopic() do { try kms.setAgreementSecret(sessionKeys, topic: sessionTopic) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.sessionSecretSet) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.sessionSecretSet) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.sessionSecretSetFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.sessionSecretSetFailed) throw error } let selfParticipant = Participant(publicKey: sessionSelfPubKeyHex, metadata: metadata) let responseParams = SessionAuthenticateResponseParams(responder: selfParticipant, cacaos: auths) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.responseParamsCreated) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.responseParamsCreated) let response = RPCResponse(id: requestId, result: responseParams) @@ -113,9 +116,12 @@ actor SessionAuthenticateResponder { protocolMethod: SessionAuthenticatedProtocolMethod.responseApprove(), envelopeType: .type1(pubKey: responseKeys.publicKey.rawRepresentation) ) - eventsClient.saveEvent(SessionAuthenticateTraceEvents.responseSent) + Task { + removePairing(pairingTopic: pairingTopic) + } + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.responseSent) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.responseSendFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.responseSendFailed) throw error } @@ -128,24 +134,32 @@ actor SessionAuthenticateResponder { transportType: .relay, verifyContext: util.getVerifyContext(requestId: requestId, domain: sessionAuthenticateRequestParams.requester.metadata.url) ) - pairingRegisterer.activate( - pairingTopic: pairingTopic, - peerMetadata: sessionAuthenticateRequestParams.requester.metadata - ) verifyContextStore.delete(forKey: requestId.string) return session } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.sessionCreationFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.sessionCreationFailed) throw error } } func respondError(requestId: RPCID) async throws { + let pairingTopic = try? util.getHistoryRecord(requestId: requestId).topic do { - try await walletErrorResponder.respondError(AuthError.userRejeted, requestId: requestId) + let _ = try await walletErrorResponder.respondError(AuthError.userRejeted, requestId: requestId) + Task { + if let pairingTopic = pairingTopic { + removePairing(pairingTopic: pairingTopic) + } + } } catch { throw error } verifyContextStore.delete(forKey: requestId.string) } + + private func removePairing(pairingTopic: String) { + pairingStore.delete(topic: pairingTopic) + networkingInteractor.unsubscribe(topic: pairingTopic) + kms.deleteSymmetricKey(for: pairingTopic) + } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index fbe9a1a56..ad2c52b20 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -74,15 +74,15 @@ final class ApproveEngine { func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace], sessionProperties: [String: String]? = nil) async throws -> Session { eventsClient.startTrace(topic: "") logger.debug("Approving session proposal") - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.approvingSessionProposal) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.approvingSessionProposal) guard !sessionNamespaces.isEmpty else { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionNamespacesValidationFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionNamespacesValidationFailure) throw Errors.emtySessionNamespacesForbidden } guard let payload = try proposalPayloadsStore.get(key: proposerPubKey) else { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.proposalNotFound) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.proposalNotFound) throw Errors.proposalNotFound } let pairingTopic = payload.topic @@ -93,24 +93,24 @@ final class ApproveEngine { guard !proposal.isExpired() else { logger.debug("Proposal has expired, topic: \(payload.topic)") - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.proposalExpired) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.proposalExpired) proposalPayloadsStore.delete(forKey: proposerPubKey) throw Errors.proposalExpired } let networkConnectionStatus = await resolveNetworkConnectionStatus() guard networkConnectionStatus == .connected else { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.networkNotConnected) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.networkNotConnected) throw Errors.networkNotConnected } do { - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.sessionNamespacesValidationStarted) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionNamespacesValidationStarted) try Namespace.validate(sessionNamespaces) try Namespace.validateApproved(sessionNamespaces, against: proposal.requiredNamespaces) - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.sessionNamespacesValidationSuccess) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionNamespacesValidationSuccess) } catch { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionNamespacesValidationFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionNamespacesValidationFailure) throw error } @@ -120,7 +120,7 @@ final class ApproveEngine { selfPublicKey: selfPublicKey, peerPublicKey: proposal.proposer.publicKey ) else { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.agreementMissingOrInvalid) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.agreementMissingOrInvalid) throw Errors.agreementMissingOrInvalid } @@ -128,7 +128,7 @@ final class ApproveEngine { try kms.setAgreementSecret(agreementKey, topic: sessionTopic) guard let relay = proposal.relays.first else { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.relayNotFound) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.relayNotFound) throw Errors.relayNotFound } @@ -151,29 +151,39 @@ final class ApproveEngine { do { _ = try await proposeResponseTask - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.responseApproveSent) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.responseApproveSent) } catch { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) throw error } do { let session: WCSession = try await settleRequestTask + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) + logger.debug("Session settle request has been successfully processed") + + do { + _ = try await proposeResponseTask + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.responseApproveSent) + } catch { + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + throw error + } + sessionStore.setSession(session) + + Task { + removePairing(pairingTopic: pairingTopic) + } onSessionSettle?(session.publicRepresentation()) - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) logger.debug("Session proposal response and settle request have been sent") proposalPayloadsStore.delete(forKey: proposerPubKey) verifyContextStore.delete(forKey: proposerPubKey) - - pairingRegisterer.activate( - pairingTopic: payload.topic, - peerMetadata: payload.request.proposer.metadata - ) return session.publicRepresentation() } catch { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) throw error } } @@ -190,15 +200,20 @@ final class ApproveEngine { reason: reason ) - if let pairingTopic = rpcHistory.get(recordId: payload.id)?.topic, - let pairing = pairingStore.getPairing(forTopic: pairingTopic), - !pairing.active { - pairingStore.delete(topic: pairingTopic) + if let pairingTopic = rpcHistory.get(recordId: payload.id)?.topic { + Task { + removePairing(pairingTopic: pairingTopic) + } } proposalPayloadsStore.delete(forKey: proposerPubKey) verifyContextStore.delete(forKey: proposerPubKey) + } + private func removePairing(pairingTopic: String) { + pairingStore.delete(topic: pairingTopic) + networkingInteractor.unsubscribe(topic: pairingTopic) + kms.deleteSymmetricKey(for: pairingTopic) } func settle(topic: String, proposal: SessionProposal, namespaces: [String: SessionNamespace], sessionProperties: [String: String]? = nil, pairingTopic: String) async throws -> WCSession { @@ -332,6 +347,7 @@ private extension ApproveEngine { sessionTopicToProposal.set(proposal, forKey: sessionTopic) Task(priority: .high) { try await networkingInteractor.subscribe(topic: sessionTopic) + removePairing(pairingTopic: payload.topic) } } catch { return logger.debug(error.localizedDescription) @@ -339,15 +355,7 @@ private extension ApproveEngine { } func handleSessionProposeResponseError(payload: ResponseSubscriptionErrorPayload) { - guard let pairing = pairingStore.getPairing(forTopic: payload.topic) else { - return logger.debug(Errors.pairingNotFound.localizedDescription) - } - - if !pairing.active { - kms.deleteSymmetricKey(for: pairing.topic) - networkingInteractor.unsubscribe(topic: pairing.topic) - pairingStore.delete(topic: payload.topic) - } + removePairing(pairingTopic: payload.topic) logger.debug("Session Proposal has been rejected") kms.deletePrivateKey(for: payload.request.proposer.publicKey) @@ -392,7 +400,7 @@ private extension ApproveEngine { proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) pairingRegisterer.setReceived(pairingTopic: payload.topic) - + if let verifyContext = try? verifyContextStore.get(key: proposal.proposer.publicKey) { onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) return @@ -457,11 +465,6 @@ private extension ApproveEngine { metadata: metadata ) - pairingRegisterer.activate( - pairingTopic: pairingTopic, - peerMetadata: params.controller.metadata - ) - let session = WCSession( topic: sessionTopic, pairingTopic: pairingTopic, diff --git a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionRequester.swift b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionRequester.swift index 684e5522a..2b1d78d0a 100644 --- a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionRequester.swift +++ b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionRequester.swift @@ -5,15 +5,18 @@ final class LinkSessionRequester { private let sessionStore: WCSessionStorage private let linkEnvelopesDispatcher: LinkEnvelopesDispatcher private let logger: ConsoleLogging + private let eventsClient: EventsClientProtocol init( sessionStore: WCSessionStorage, linkEnvelopesDispatcher: LinkEnvelopesDispatcher, - logger: ConsoleLogging + logger: ConsoleLogging, + eventsClient: EventsClientProtocol ) { self.sessionStore = sessionStore self.linkEnvelopesDispatcher = linkEnvelopesDispatcher self.logger = logger + self.eventsClient = eventsClient } func request(_ request: Request) async throws -> String? { @@ -33,6 +36,8 @@ final class LinkSessionRequester { let ttl = try request.calculateTtl() let protocolMethod = SessionRequestProtocolMethod(ttl: ttl) let rpcRequest = RPCRequest(method: protocolMethod.method, params: sessionRequestParams, rpcid: request.id) - return try await linkEnvelopesDispatcher.request(topic: session.topic, request: rpcRequest, peerUniversalLink: peerUniversalLink, envelopeType: .type0) + let envelope = try await linkEnvelopesDispatcher.request(topic: session.topic, request: rpcRequest, peerUniversalLink: peerUniversalLink, envelopeType: .type0) + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeSent(request.id)) } + return envelope } } diff --git a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift index 5d215be7a..ea6df02c7 100644 --- a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift +++ b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift @@ -10,19 +10,22 @@ class LinkSessionResponder { private let linkEnvelopesDispatcher: LinkEnvelopesDispatcher private let sessionRequestsProvider: SessionRequestsProvider private let historyService: HistoryService + private let eventsClient: EventsClientProtocol init( logger: ConsoleLogging, sessionStore: WCSessionStorage, linkEnvelopesDispatcher: LinkEnvelopesDispatcher, sessionRequestsProvider: SessionRequestsProvider, - historyService: HistoryService + historyService: HistoryService, + eventsClient: EventsClientProtocol ) { self.logger = logger self.sessionStore = sessionStore self.linkEnvelopesDispatcher = linkEnvelopesDispatcher self.sessionRequestsProvider = sessionRequestsProvider self.historyService = historyService + self.eventsClient = eventsClient } func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws -> String { @@ -41,6 +44,7 @@ class LinkSessionResponder { guard sessionRequestNotExpired(requestId: requestId) else { logger.debug("request expired") + try await linkEnvelopesDispatcher.respondError( topic: topic, requestId: requestId, @@ -48,6 +52,8 @@ class LinkSessionResponder { reason: SignReasonCode.sessionRequestExpired, envelopeType: .type0 ) + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeResponseSent(requestId)) } + throw Errors.sessionRequestExpired } @@ -58,6 +64,8 @@ class LinkSessionResponder { peerUniversalLink: peerUniversalLink, envelopeType: .type0 ) + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionRequestLinkModeResponseSent(requestId)) } + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in guard let self = self else {return} sessionRequestsProvider.emitRequestIfPending() diff --git a/Sources/WalletConnectSign/LinkAndRelayDispatchers/WalletErrorResponder.swift b/Sources/WalletConnectSign/LinkAndRelayDispatchers/WalletErrorResponder.swift index 36708c376..53f5221cc 100644 --- a/Sources/WalletConnectSign/LinkAndRelayDispatchers/WalletErrorResponder.swift +++ b/Sources/WalletConnectSign/LinkAndRelayDispatchers/WalletErrorResponder.swift @@ -13,20 +13,23 @@ actor WalletErrorResponder { private let rpcHistory: RPCHistory private let logger: ConsoleLogging private let linkEnvelopesDispatcher: LinkEnvelopesDispatcher + private let eventsClient: EventsClientProtocol init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, kms: KeyManagementServiceProtocol, rpcHistory: RPCHistory, - linkEnvelopesDispatcher: LinkEnvelopesDispatcher) { + linkEnvelopesDispatcher: LinkEnvelopesDispatcher, + eventsClient: EventsClientProtocol + ) { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms self.rpcHistory = rpcHistory self.linkEnvelopesDispatcher = linkEnvelopesDispatcher + self.eventsClient = eventsClient } - func respondError(_ error: AuthError, requestId: RPCID) async throws -> String? { let transportType = try getHistoryRecord(requestId: requestId).transportType ?? .relay @@ -69,7 +72,11 @@ actor WalletErrorResponder { throw Errors.peerUniversalLinkNotFound } - return try await linkEnvelopesDispatcher.respondError(topic: topic, requestId: requestId, peerUniversalLink: peerUniversalLink, reason: error, envelopeType: .type1(pubKey: type1EnvelopeKey)) + let envelope = try await linkEnvelopesDispatcher.respondError(topic: topic, requestId: requestId, peerUniversalLink: peerUniversalLink, reason: error, envelopeType: .type1(pubKey: type1EnvelopeKey)) + + Task(priority: .low) { eventsClient.saveMessageEvent(.sessionAuthenticateLinkModeResponseRejectSent(requestId)) } + + return envelope } diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift index 06b2500f7..1277e69f6 100644 --- a/Sources/WalletConnectSign/Session.swift +++ b/Sources/WalletConnectSign/Session.swift @@ -5,6 +5,7 @@ import Foundation */ public struct Session: Codable { public let topic: String + @available(*, deprecated, message: "The pairingTopic property is deprecated.") public let pairingTopic: String public let peer: AppMetadata public let requiredNamespaces: [String: ProposalNamespace] diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 35b7941ce..7075c9373 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -147,7 +147,6 @@ public final class SignClient: SignClientProtocol { private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine private let disconnectService: DisconnectService - private let pairingPingService: PairingPingService private let sessionPingService: SessionPingService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine @@ -203,7 +202,6 @@ public final class SignClient: SignClientProtocol { networkingClient: NetworkingInteractor, sessionEngine: SessionEngine, approveEngine: ApproveEngine, - pairingPingService: PairingPingService, sessionPingService: SessionPingService, nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, @@ -239,7 +237,6 @@ public final class SignClient: SignClientProtocol { self.networkingClient = networkingClient self.sessionEngine = sessionEngine self.approveEngine = approveEngine - self.pairingPingService = pairingPingService self.sessionPingService = sessionPingService self.nonControllerSessionStateMachine = nonControllerSessionStateMachine self.controllerSessionStateMachine = controllerSessionStateMachine @@ -299,28 +296,6 @@ public final class SignClient: SignClientProtocol { return pairingURI } - /// For a dApp to propose a session to a wallet. - /// Function will propose a session on existing pairing. - /// - Parameters: - /// - requiredNamespaces: required namespaces for a session - /// - topic: pairing topic - public func connect( - requiredNamespaces: [String: ProposalNamespace], - optionalNamespaces: [String: ProposalNamespace]? = nil, - sessionProperties: [String: String]? = nil, - topic: String - ) async throws { - logger.debug("Connecting Application") - try pairingClient.validatePairingExistance(topic) - try await appProposeService.propose( - pairingTopic: topic, - namespaces: requiredNamespaces, - optionalNamespaces: optionalNamespaces, - sessionProperties: sessionProperties, - relay: RelayProtocolOptions(protocol: "irn", data: nil) - ) - } - //---------------------------------------AUTH----------------------------------- /// For a dApp to propose an authenticated session to a wallet. @@ -573,9 +548,6 @@ public final class SignClient: SignClientProtocol { sessionEngine.onSessionResponse = { [unowned self] response in sessionResponsePublisherSubject.send(response) } - pairingPingService.onResponse = { [unowned self] topic in - pingResponsePublisherSubject.send(topic) - } sessionPingService.onResponse = { [unowned self] topic in pingResponsePublisherSubject.send(topic) } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 98bdb3239..fca63020d 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -93,7 +93,6 @@ public struct SignClientFactory { let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) - let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) let appProposerService = AppProposeService(metadata: metadata, networkingInteractor: networkingClient, kms: kms, logger: logger) let proposalExpiryWatcher = ProposalExpiryWatcher(proposalPayloadsStore: proposalPayloadsStore, rpcHistory: rpcHistory) let pendingProposalsProvider = PendingProposalsProvider(proposalPayloadsStore: proposalPayloadsStore, verifyContextStore: verifyContextStore) @@ -116,9 +115,9 @@ public struct SignClientFactory { let linkModeLinksStore = CodableStore(defaults: keyValueStorage, identifier: SignStorageIdentifiers.linkModeLinks.rawValue) let supportLinkMode = metadata.redirect?.linkMode ?? false - let appRespondSubscriber = AuthResponseSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: rpcHistory, signatureVerifier: signatureVerifier, pairingRegisterer: pairingClient, kms: kms, sessionStore: sessionStore, messageFormatter: messageFormatter, sessionNamespaceBuilder: sessionNameSpaceBuilder, authResponseTopicRecordsStore: authResponseTopicRecordsStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, linkModeLinksStore: linkModeLinksStore, supportLinkMode: supportLinkMode) + let appRespondSubscriber = AuthResponseSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: rpcHistory, signatureVerifier: signatureVerifier, pairingRegisterer: pairingClient, kms: kms, sessionStore: sessionStore, messageFormatter: messageFormatter, sessionNamespaceBuilder: sessionNameSpaceBuilder, authResponseTopicRecordsStore: authResponseTopicRecordsStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, linkModeLinksStore: linkModeLinksStore, pairingStore: pairingStore, supportLinkMode: supportLinkMode, eventsClient: eventsClient) - let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: rpcHistory, linkEnvelopesDispatcher: linkEnvelopesDispatcher) + let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: rpcHistory, linkEnvelopesDispatcher: linkEnvelopesDispatcher, eventsClient: eventsClient) let authRequestSubscriber = AuthRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingClient, verifyClient: verifyClient, verifyContextStore: verifyContextStore, pairingStore: pairingStore) let approveSessionAuthenticateUtil = ApproveSessionAuthenticateUtil(logger: logger, kms: kms, rpcHistory: rpcHistory, signatureVerifier: signatureVerifier, messageFormatter: messageFormatter, sessionStore: sessionStore, sessionNamespaceBuilder: sessionNameSpaceBuilder, networkingInteractor: networkingClient, verifyContextStore: verifyContextStore, verifyClient: verifyClient) @@ -126,25 +125,25 @@ public struct SignClientFactory { let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: rpcHistory, verifyContextStore: verifyContextStore) let authResponseTopicResubscriptionService = AuthResponseTopicResubscriptionService(networkingInteractor: networkingClient, logger: logger, authResponseTopicRecordsStore: authResponseTopicRecordsStore) - let linkAuthRequester = LinkAuthRequester(kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider, authResponseTopicRecordsStore: authResponseTopicRecordsStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, linkModeLinksStore: linkModeLinksStore) - let linkAuthRequestSubscriber = LinkAuthRequestSubscriber(logger: logger, kms: kms, envelopesDispatcher: linkEnvelopesDispatcher, verifyClient: verifyClient, verifyContextStore: verifyContextStore) + let linkAuthRequester = LinkAuthRequester(kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider, authResponseTopicRecordsStore: authResponseTopicRecordsStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, linkModeLinksStore: linkModeLinksStore, eventsClient: eventsClient) + let linkAuthRequestSubscriber = LinkAuthRequestSubscriber(logger: logger, kms: kms, envelopesDispatcher: linkEnvelopesDispatcher, verifyClient: verifyClient, verifyContextStore: verifyContextStore, eventsClient: eventsClient) - let relaySessionAuthenticateResponder = SessionAuthenticateResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingClient, metadata: metadata, approveSessionAuthenticateUtil: approveSessionAuthenticateUtil, eventsClient: eventsClient) + let relaySessionAuthenticateResponder = SessionAuthenticateResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingClient, metadata: metadata, approveSessionAuthenticateUtil: approveSessionAuthenticateUtil, eventsClient: eventsClient, pairingStore: pairingStore) - let linkSessionAuthenticateResponder = LinkSessionAuthenticateResponder(linkEnvelopesDispatcher: linkEnvelopesDispatcher, logger: logger, kms: kms, metadata: metadata, approveSessionAuthenticateUtil: approveSessionAuthenticateUtil, walletErrorResponder: walletErrorResponder, verifyContextStore: verifyContextStore) + let linkSessionAuthenticateResponder = LinkSessionAuthenticateResponder(linkEnvelopesDispatcher: linkEnvelopesDispatcher, logger: logger, kms: kms, metadata: metadata, approveSessionAuthenticateUtil: approveSessionAuthenticateUtil, walletErrorResponder: walletErrorResponder, verifyContextStore: verifyContextStore, eventsClient: eventsClient) let approveSessionAuthenticateDispatcher = ApproveSessionAuthenticateDispatcher(relaySessionAuthenticateResponder: relaySessionAuthenticateResponder, logger: logger, rpcHistory: rpcHistory, approveSessionAuthenticateUtil: approveSessionAuthenticateUtil, linkSessionAuthenticateResponder: linkSessionAuthenticateResponder) let relaySessionRequester = SessionRequester(sessionStore: sessionStore, networkingInteractor: networkingClient, logger: logger) - let linkSessionRequester = LinkSessionRequester(sessionStore: sessionStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, logger: logger) + let linkSessionRequester = LinkSessionRequester(sessionStore: sessionStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, logger: logger, eventsClient: eventsClient) let sessionRequestDispatcher = SessionRequestDispatcher(relaySessionRequester: relaySessionRequester, linkSessionRequester: linkSessionRequester, logger: logger, sessionStore: sessionStore) - let linkSessionRequestSubscriber = LinkSessionRequestSubscriber(sessionRequestsProvider: sessionRequestsProvider, sessionStore: sessionStore, logger: logger, envelopesDispatcher: linkEnvelopesDispatcher) + let linkSessionRequestSubscriber = LinkSessionRequestSubscriber(sessionRequestsProvider: sessionRequestsProvider, sessionStore: sessionStore, logger: logger, envelopesDispatcher: linkEnvelopesDispatcher, eventsClient: eventsClient) let relaySessionResponder = SessionResponder(logger: logger, sessionStore: sessionStore, networkingInteractor: networkingClient, verifyContextStore: verifyContextStore, sessionRequestsProvider: sessionRequestsProvider, historyService: historyService) - let linkSessionResponder = LinkSessionResponder(logger: logger, sessionStore: sessionStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, sessionRequestsProvider: sessionRequestsProvider, historyService: historyService) + let linkSessionResponder = LinkSessionResponder(logger: logger, sessionStore: sessionStore, linkEnvelopesDispatcher: linkEnvelopesDispatcher, sessionRequestsProvider: sessionRequestsProvider, historyService: historyService, eventsClient: eventsClient) let sessionResponderDispatcher = SessionResponderDispatcher(relaySessionResponder: relaySessionResponder, linkSessionResponder: linkSessionResponder, logger: logger, sessionStore: sessionStore) - let linkSessionRequestResponseSubscriber = LinkSessionRequestResponseSubscriber(envelopesDispatcher: linkEnvelopesDispatcher) + let linkSessionRequestResponseSubscriber = LinkSessionRequestResponseSubscriber(envelopesDispatcher: linkEnvelopesDispatcher, eventsClient: eventsClient) let authenticateTransportTypeSwitcher = AuthenticateTransportTypeSwitcher(linkAuthRequester: linkAuthRequester, pairingClient: pairingClient, logger: logger, appRequestService: appRequestService, appProposeService: appProposerService) @@ -153,7 +152,6 @@ public struct SignClientFactory { networkingClient: networkingClient, sessionEngine: sessionEngine, approveEngine: approveEngine, - pairingPingService: pairingPingService, sessionPingService: sessionPingService, nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, diff --git a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift index f74e2c9c7..080843a99 100644 --- a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift +++ b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift @@ -18,8 +18,6 @@ public protocol SignClientProtocol { var pendingProposalsPublisher: AnyPublisher<[(proposal: Session.Proposal, context: VerifyContext?)], Never> { get } var requestExpirationPublisher: AnyPublisher { get } - func connect(requiredNamespaces: [String: ProposalNamespace], optionalNamespaces: [String: ProposalNamespace]?, sessionProperties: [String: String]?, topic: String) async throws - func request(params: Request) async throws func approve(proposalId: String, namespaces: [String: SessionNamespace], sessionProperties: [String: String]?) async throws -> Session func authenticate(_ params: AuthRequestParams, walletUniversalLink: String?) async throws -> WalletConnectURI? diff --git a/Sources/WalletConnectSign/SignDecryptionService.swift b/Sources/WalletConnectSign/SignDecryptionService.swift index a0b8f649a..9301c8517 100644 --- a/Sources/WalletConnectSign/SignDecryptionService.swift +++ b/Sources/WalletConnectSign/SignDecryptionService.swift @@ -50,7 +50,6 @@ public class SignDecryptionService { public func decryptAuthRequest(topic: String, ciphertext: String) throws -> AuthenticationRequest { let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, codingType: .base64Encoded, envelopeString: ciphertext) - setPairingMetadata(rpcRequest: rpcRequest, topic: topic) if let params = try rpcRequest.params?.get(SessionAuthenticateRequestParams.self), let id = rpcRequest.id { let authRequest = AuthenticationRequest(id: id, topic: topic, payload: params.authPayload, requester: params.requester.metadata) @@ -63,14 +62,4 @@ public class SignDecryptionService { public func getMetadata(topic: String) -> AppMetadata? { sessionStorage.getSession(forTopic: topic)?.peerParticipant.metadata } - - private func setPairingMetadata(rpcRequest: RPCRequest, topic: String) { - guard var pairing = pairingStorage.getPairing(forTopic: topic), - pairing.peerMetadata == nil, - let peerMetadata = try? rpcRequest.params?.get(SessionAuthenticateRequestParams.self).requester.metadata - else { return } - - pairing.updatePeerMetadata(peerMetadata) - pairingStorage.setPairing(pairing) - } } diff --git a/Sources/WalletConnectVerify/VerifyContextFactory.swift b/Sources/WalletConnectVerify/VerifyContextFactory.swift index 629973e38..6c280f2ef 100644 --- a/Sources/WalletConnectVerify/VerifyContextFactory.swift +++ b/Sources/WalletConnectVerify/VerifyContextFactory.swift @@ -1,7 +1,15 @@ - import Foundation class VerifyContextFactory { + + private func ensureUrlHasScheme(_ urlString: String) -> String { + if urlString.hasPrefix("http://") || urlString.hasPrefix("https://") { + return urlString + } else { + return "https://" + urlString + } + } + public func createVerifyContext(origin: String?, domain: String, isScam: Bool?, isVerified: Bool?) -> VerifyContext { guard isScam != true else { @@ -19,7 +27,11 @@ class VerifyContextFactory { ) } - if let origin, let originUrl = URL(string: origin), let domainUrl = URL(string: domain) { + // Ensure both origin and domain have a scheme + let originWithScheme = origin.map { ensureUrlHasScheme($0) } + let domainWithScheme = ensureUrlHasScheme(domain) + + if let originWithScheme, let originUrl = URL(string: originWithScheme), let domainUrl = URL(string: domainWithScheme) { return VerifyContext( origin: origin, validation: (originUrl.host == domainUrl.host) ? .valid : .invalid diff --git a/Sources/Web3Wallet/Web3WalletClient.swift b/Sources/Web3Wallet/Web3WalletClient.swift index 84f636b92..62576d5a9 100644 --- a/Sources/Web3Wallet/Web3WalletClient.swift +++ b/Sources/Web3Wallet/Web3WalletClient.swift @@ -177,9 +177,8 @@ public class Web3WalletClient { try await pairingClient.pair(uri: uri) } - public func disconnectPairing(topic: String) async { - await pairingClient.disconnect(topic: topic) - } + @available(*, deprecated, message: "This method is deprecated. Pairing will disconnect automatically") + public func disconnectPairing(topic: String) async {} /// For a wallet and a dApp to terminate a session /// diff --git a/Tests/EventsTests/EventsDispatcherTests.swift b/Tests/EventsTests/EventsDispatcherTests.swift index e7b980e5a..3ffc2f5d4 100644 --- a/Tests/EventsTests/EventsDispatcherTests.swift +++ b/Tests/EventsTests/EventsDispatcherTests.swift @@ -4,7 +4,7 @@ import XCTest class EventsDispatcherTests: XCTestCase { var mockNetworkingService: MockNetworkingService! var eventsDispatcher: EventsDispatcher! - let events = [Event(eventId: UUID().uuidString, bundleId: "com.wallet.example", timestamp: Int64(Date().timeIntervalSince1970 * 1000), props: Props(event: "ERROR", type: "test_error", properties: Properties(topic: "test_topic", trace: ["test_trace"])))] + let events = [TraceEvent(eventId: UUID().uuidString, bundleId: "com.wallet.example", timestamp: Int64(Date().timeIntervalSince1970 * 1000), props: TraceEvent.Props(event: "ERROR", type: "test_error", properties: TraceEvent.Properties(topic: "test_topic", trace: ["test_trace"])))] override func setUp() { super.setUp() diff --git a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift index 3c21567c6..36915f594 100644 --- a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift +++ b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift @@ -6,20 +6,11 @@ import WalletConnectNetworking public class PairingRegistererMock: PairingRegisterer where RequestParams: Codable { public let subject = PassthroughSubject, Never>() - public var isActivateCalled: Bool = false public var isReceivedCalled: Bool = false public func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams: Decodable, RequestParams: Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> } - - public func activate(pairingTopic: String, peerMetadata: WalletConnectPairing.AppMetadata?) { - isActivateCalled = true - } - - public func validatePairingExistance(_ topic: String) throws { - - } public func setReceived(pairingTopic: String) { isReceivedCalled = true diff --git a/Tests/VerifyTests/VerifyContextFactoryTests.swift b/Tests/VerifyTests/VerifyContextFactoryTests.swift index 0d43c2aed..026bfba67 100644 --- a/Tests/VerifyTests/VerifyContextFactoryTests.swift +++ b/Tests/VerifyTests/VerifyContextFactoryTests.swift @@ -49,6 +49,16 @@ class VerifyContextFactoryTests: XCTestCase { XCTAssertEqual(contextWithFalseVerification.validation, .scam) } + func testValidOriginAndDomainWithoutScheme() { + let context = factory.createVerifyContext(origin: "https://dev.lab.web3modal.com", domain: "dev.lab.web3modal.com", isScam: false, isVerified: nil) + XCTAssertEqual(context.validation, .valid) + } + + func testInvalidOriginAndDomainWithoutScheme() { + let context = factory.createVerifyContext(origin: "https://dev.lab.web3modal.com", domain: "different.com", isScam: false, isVerified: nil) + XCTAssertEqual(context.validation, .invalid) + } + // tests for createVerifyContextForLinkMode func testValidUniversalLink() { diff --git a/Tests/WalletConnectModalTests/Mocks/ModalSheetInteractorMock.swift b/Tests/WalletConnectModalTests/Mocks/ModalSheetInteractorMock.swift index e22c5d5f3..216685982 100644 --- a/Tests/WalletConnectModalTests/Mocks/ModalSheetInteractorMock.swift +++ b/Tests/WalletConnectModalTests/Mocks/ModalSheetInteractorMock.swift @@ -17,7 +17,7 @@ final class ModalSheetInteractorMock: ModalSheetInteractor { (1, wallets) } - func createPairingAndConnect() async throws -> WalletConnectURI? { + func createPairingAndConnect() async throws -> WalletConnectURI { .init(topic: "foo", symKey: "bar", relay: .init(protocol: "irn", data: nil), expiryTimestamp: 1706001526) } diff --git a/Tests/WalletConnectPairingTests/AppPairActivationServiceTests.swift b/Tests/WalletConnectPairingTests/AppPairActivationServiceTests.swift deleted file mode 100644 index 601862ad6..000000000 --- a/Tests/WalletConnectPairingTests/AppPairActivationServiceTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -import XCTest -@testable import WalletConnectPairing -@testable import TestingUtils -import WalletConnectUtils - -final class AppPairActivationServiceTests: XCTestCase { - - var service: AppPairActivationService! - var storageMock: WCPairingStorage! - var logger: ConsoleLogger! - - override func setUp() { - storageMock = WCPairingStorageMock() - logger = ConsoleLogger() - service = AppPairActivationService(pairingStorage: storageMock, logger: logger) - } - - override func tearDown() { - storageMock = nil - logger = nil - service = nil - } - - func testActivate() { - let pairing = WCPairing(uri: WalletConnectURI.stub()) - let topic = pairing.topic - let date = pairing.expiryDate - - storageMock.setPairing(pairing) - - XCTAssertFalse(pairing.active) - XCTAssertNil(pairing.peerMetadata) - - service.activate(for: topic, peerMetadata: .stub()) - - let activated = storageMock.getPairing(forTopic: topic)! - - XCTAssertTrue(activated.active) - XCTAssertNotNil(activated.peerMetadata) - XCTAssertTrue(activated.expiryDate > date) - } -} diff --git a/Tests/WalletConnectPairingTests/AppPairServiceTests.swift b/Tests/WalletConnectPairingTests/AppPairServiceTests.swift index 9839d5192..304bb2697 100644 --- a/Tests/WalletConnectPairingTests/AppPairServiceTests.swift +++ b/Tests/WalletConnectPairingTests/AppPairServiceTests.swift @@ -30,6 +30,5 @@ final class AppPairServiceTests: XCTestCase { XCTAssert(cryptoMock.hasSymmetricKey(for: uri.topic), "Proposer must store the symmetric key matching the URI.") XCTAssert(storageMock.hasPairing(forTopic: uri.topic), "The engine must store a pairing after creating one") XCTAssert(networkingInteractor.didSubscribe(to: uri.topic), "Proposer must subscribe to pairing topic.") - XCTAssert(storageMock.getPairing(forTopic: uri.topic)?.active == false, "Recently created pairing must be inactive.") } } diff --git a/Tests/WalletConnectPairingTests/WCPairingTests.swift b/Tests/WalletConnectPairingTests/WCPairingTests.swift deleted file mode 100644 index 8565b5bfa..000000000 --- a/Tests/WalletConnectPairingTests/WCPairingTests.swift +++ /dev/null @@ -1,102 +0,0 @@ -import XCTest -@testable import WalletConnectPairing -@testable import WalletConnectUtils -@testable import WalletConnectUtils - -final class WCPairingTests: XCTestCase { - - var referenceDate: Date! - - override func setUp() { - referenceDate = Date() - func getDate() -> Date { return referenceDate } - WCPairing.dateInitializer = getDate - } - - override func tearDown() { - WCPairing.dateInitializer = Date.init - } - - func testAbsoluteValues() { - XCTAssertEqual(WCPairing.timeToLiveInactive, 5 * .minute, "Inactive time-to-live is 5 minutes.") - XCTAssertEqual(WCPairing.timeToLiveActive, 30 * .day, "Active time-to-live is 30 days.") - } - - func testInitInactiveFromTopic() { - let pairing = WCPairing(uri: WalletConnectURI.stub()) - let inactiveExpiry = referenceDate.advanced(by: WCPairing.timeToLiveInactive) - XCTAssertFalse(pairing.active) - XCTAssertEqual(pairing.expiryDate.timeIntervalSince1970, inactiveExpiry.timeIntervalSince1970, accuracy: 1) - } - - func testInitInactiveFromURI() { - let pairing = WCPairing(uri: WalletConnectURI.stub()) - let inactiveExpiry = referenceDate.advanced(by: WCPairing.timeToLiveInactive) - XCTAssertFalse(pairing.active) - XCTAssertEqual(pairing.expiryDate.timeIntervalSince1970, inactiveExpiry.timeIntervalSince1970, accuracy: 1) - } - - func testUpdateExpiryForTopic() { - var pairing = WCPairing(uri: WalletConnectURI.stub()) - let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) - try? pairing.updateExpiry() - XCTAssertEqual(pairing.expiryDate, activeExpiry) - } - - func testUpdateExpiryForUri() { - var pairing = WCPairing(uri: WalletConnectURI.stub()) - let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) - try? pairing.updateExpiry() - XCTAssertEqual(pairing.expiryDate, activeExpiry) - } - - func testActivateTopic() { - var pairing = WCPairing(uri: WalletConnectURI.stub()) - let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) - XCTAssertFalse(pairing.active) - pairing.activate() - XCTAssertTrue(pairing.active) - XCTAssertEqual(pairing.expiryDate, activeExpiry) - } - - func testActivateURI() { - var pairing = WCPairing(uri: WalletConnectURI.stub()) - let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) - XCTAssertFalse(pairing.active) - pairing.activate() - XCTAssertTrue(pairing.active) - XCTAssertEqual(pairing.expiryDate, activeExpiry) - } - - func testUpdateExpiryWhenValueIsGreaterThanMax() { - var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) - XCTAssertThrowsError(try pairing.updateExpiry(40 * .day)) { error in - XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) - } - } - - func testUpdateExpiryWhenNewExpiryDateIsLessThanExpiryDate() { - let expiryDate = referenceDate.advanced(by: 40 * .day) - var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) - XCTAssertThrowsError(try pairing.updateExpiry(10 * .minute)) { error in - XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) - } - } - - func testActivateWhenCanUpdateExpiry() { - var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) - XCTAssertFalse(pairing.active) - pairing.activate() - XCTAssertTrue(pairing.active) - XCTAssertEqual(referenceDate.advanced(by: 30 * .day), pairing.expiryDate) - } - - func testActivateWhenUpdateExpiryIsInvalid() { - let expiryDate = referenceDate.advanced(by: 40 * .day) - var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) - XCTAssertFalse(pairing.active) - pairing.activate() - XCTAssertTrue(pairing.active) - XCTAssertEqual(expiryDate, pairing.expiryDate) - } -} diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift index ae3a4772d..d11d3644c 100644 --- a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift +++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift @@ -128,7 +128,6 @@ final class AppProposalServiceTests: XCTestCase { networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) - _ = storageMock.getPairing(forTopic: topicA)! await fulfillment(of: [exp], timeout: 5) @@ -156,37 +155,8 @@ final class AppProposalServiceTests: XCTestCase { let response = RPCResponse.stubError(forRequest: request) networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) - XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") - XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") + XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete the pairing.") XCTAssertFalse(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must delete symmetric key if pairing is inactive.") XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") } - - func testSessionProposeErrorOnActivePairing() async { - let uri = try! await appPairService.create(supportedMethods: nil) - let pairing = storageMock.getPairing(forTopic: uri.topic)! - let topicA = pairing.topic - let relayOptions = RelayProtocolOptions(protocol: "", data: nil) - - // Client propose session - // FIXME: namespace stub - try? await service.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) - - guard let request = networkingInteractor.requests.first?.request, - let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { - XCTFail("Proposer must publish session proposal request"); return - } - - var storedPairing = storageMock.getPairing(forTopic: topicA)! - storedPairing.activate() - storageMock.setPairing(storedPairing) - - let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) - - XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") - XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") - XCTAssert(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must not delete symmetric key if pairing is active.") - XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") - } } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 1d36d7902..552c8b1aa 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -83,7 +83,6 @@ final class ApproveEngineTests: XCTestCase { XCTAssert(cryptoMock.hasAgreementSecret(for: topicB), "Responder must store agreement key for topic B") XCTAssertEqual(networkingInteractor.didRespondOnTopic!, topicA, "Responder must respond on topic A") XCTAssertTrue(sessionStorageMock.hasSession(forTopic: topicB), "Responder must persist session on topic B") - XCTAssertTrue(pairingRegisterer.isActivateCalled) } func testReceiveProposal() {