diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index 03d23797f..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", diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index a984e0a53..f7829209b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -314,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 */; }; @@ -654,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 = ""; }; @@ -1805,7 +1803,6 @@ children = ( C5F32A2B2954814200A6476E /* ConnectionDetailsModule.swift */, C5F32A2D2954814A00A6476E /* ConnectionDetailsRouter.swift */, - C5F32A2F2954816100A6476E /* ConnectionDetailsInteractor.swift */, C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */, C5F32A332954817600A6476E /* ConnectionDetailsView.swift */, ); @@ -2533,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; 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/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 5b924af0f..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" 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/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index 8fc8b43c4..a025e49e1 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -30,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 } } @@ -78,7 +78,7 @@ extension WalletPairService { 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 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 b4889b8e3..e3e600aeb 100644 --- a/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift +++ b/Sources/WalletConnectSign/Auth/Services/App/AuthResponseSubscriber.swift @@ -21,6 +21,7 @@ class AuthResponseSubscriber { private let linkModeLinksStore: CodableStore private let supportLinkMode: Bool private let pairingStore: WCPairingStorage + private let eventsClient: EventsClientProtocol init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, @@ -35,7 +36,9 @@ class AuthResponseSubscriber { linkEnvelopesDispatcher: LinkEnvelopesDispatcher, linkModeLinksStore: CodableStore, pairingStore: WCPairingStorage, - supportLinkMode: Bool) { + supportLinkMode: Bool, + eventsClient: EventsClientProtocol + ) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory @@ -50,6 +53,8 @@ class AuthResponseSubscriber { self.linkModeLinksStore = linkModeLinksStore self.supportLinkMode = supportLinkMode self.pairingStore = pairingStore + self.eventsClient = eventsClient + subscribeForResponse() subscribeForLinkResponse() } @@ -94,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) @@ -101,6 +107,8 @@ 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 diff --git a/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift b/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift index f33758a03..3dae4def8 100644 --- a/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift +++ b/Sources/WalletConnectSign/Auth/Services/Wallet/SessionAuthenticateResponder.swift @@ -39,12 +39,12 @@ actor SessionAuthenticateResponder { } 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 } @@ -53,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 } @@ -64,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 } @@ -88,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) @@ -119,9 +119,9 @@ actor SessionAuthenticateResponder { Task { removePairing(pairingTopic: pairingTopic) } - eventsClient.saveEvent(SessionAuthenticateTraceEvents.responseSent) + eventsClient.saveTraceEvent(SessionAuthenticateTraceEvents.responseSent) } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.responseSendFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.responseSendFailed) throw error } @@ -137,7 +137,7 @@ actor SessionAuthenticateResponder { verifyContextStore.delete(forKey: requestId.string) return session } catch { - eventsClient.saveEvent(SessionAuthenticateErrorEvents.sessionCreationFailed) + eventsClient.saveTraceEvent(SessionAuthenticateErrorEvents.sessionCreationFailed) throw error } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 1b2dc96d4..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 } @@ -149,16 +149,24 @@ final class ApproveEngine { pairingTopic: pairingTopic ) + do { + _ = try await proposeResponseTask + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.responseApproveSent) + } catch { + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + throw error + } + do { let session: WCSession = try await settleRequestTask - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) logger.debug("Session settle request has been successfully processed") do { _ = try await proposeResponseTask - eventsClient.saveEvent(SessionApproveExecutionTraceEvents.responseApproveSent) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.responseApproveSent) } catch { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) throw error } @@ -168,12 +176,14 @@ final class ApproveEngine { removePairing(pairingTopic: pairingTopic) } onSessionSettle?(session.publicRepresentation()) + eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) + logger.debug("Session proposal response and settle request have been sent") proposalPayloadsStore.delete(forKey: proposerPubKey) verifyContextStore.delete(forKey: proposerPubKey) return session.publicRepresentation() } catch { - eventsClient.saveEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) + eventsClient.saveTraceEvent(ApproveSessionTraceErrorEvents.sessionSettleFailure) throw error } } 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/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 523eff3a4..fca63020d 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -115,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, pairingStore: pairingStore, 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) @@ -125,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, 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) 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()