diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index b323e8fe8..f314e3f78 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -43,6 +43,8 @@ 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; + 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; + 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; }; @@ -203,6 +205,7 @@ 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; + 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; @@ -341,6 +344,7 @@ files = ( A5E03DFF2864662500888481 /* WalletConnect in Frameworks */, A5E03DF52864651200888481 /* Starscream in Frameworks */, + 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */, A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -520,6 +524,14 @@ path = Connect; sourceTree = ""; }; + 84D2A66728A4F5260088AE09 /* Auth */ = { + isa = PBXGroup; + children = ( + 84D2A66528A4F51E0088AE09 /* AuthTests.swift */, + ); + path = Auth; + sourceTree = ""; + }; A50F3944288005A700064555 /* Types */ = { isa = PBXGroup; children = ( @@ -857,6 +869,7 @@ A5E03E0A28646A8A00888481 /* Stubs */, A5E03E0928646A8100888481 /* Sign */, A5E03E0828646A7B00888481 /* Chat */, + 84D2A66728A4F5260088AE09 /* Auth */, ); path = IntegrationTests; sourceTree = ""; @@ -1017,6 +1030,7 @@ A5E03DF42864651200888481 /* Starscream */, A5E03DFE2864662500888481 /* WalletConnect */, A5E03E00286466EA00888481 /* WalletConnectChat */, + 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */, ); productName = IntegrationTests; productReference = A5E03DED286464DB00888481 /* IntegrationTests.xctest */; @@ -1261,6 +1275,7 @@ files = ( 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, + 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, @@ -1423,6 +1438,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -1767,6 +1783,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectAuth; + }; A5629AE92877F2D600094373 /* WalletConnectChat */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectChat; diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift new file mode 100644 index 000000000..1b721162f --- /dev/null +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -0,0 +1,57 @@ +import Foundation +import XCTest +import WalletConnectUtils +@testable import WalletConnectKMS +import WalletConnectRelay +import Combine +@testable import Auth + +final class AuthTests: XCTestCase { + var app: AuthClient! + var wallet: AuthClient! + private var publishers = [AnyCancellable]() + + override func setUp() { + app = makeClient(prefix: "👻 App") + let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! + wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) + + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + app.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wallet.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) + } + + + func makeClient(prefix: String, account: Account? = nil) -> AuthClient { + let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) + let relayHost = "relay.walletconnect.com" + let projectId = "8ba9ee138960775e5231b70cc5ef1c3a" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + + return AuthClientFactory.create( + metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), + account: account, + logger: logger, + keyValueStorage: RuntimeKeyValueStorage(), + keychainStorage: keychain, + relayClient: relayClient) + } + + func testRequest() async { + + } +} diff --git a/Package.swift b/Package.swift index 8b5f47d04..22c22aac8 100644 --- a/Package.swift +++ b/Package.swift @@ -16,6 +16,9 @@ let package = Package( .library( name: "WalletConnectChat", targets: ["Chat"]), + .library( + name: "WalletConnectAuth", + targets: ["Auth"]), .library( name: "WalletConnectRouter", targets: ["WalletConnectRouter"]) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 359021680..cca676e15 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -2,8 +2,9 @@ import Foundation import Combine import WalletConnectUtils import WalletConnectPairing +import WalletConnectRelay -class AuthClient { +public class AuthClient { enum Errors: Error { case malformedPairingURI case unknownWalletAddress @@ -19,6 +20,8 @@ class AuthClient { authResponsePublisherSubject.eraseToAnyPublisher() } + public let socketConnectionStatusPublisher: AnyPublisher + private let appPairService: AppPairService private let appRequestService: AppRequestService private let appRespondSubscriber: AppRespondSubscriber @@ -43,7 +46,9 @@ class AuthClient { pendingRequestsProvider: PendingRequestsProvider, cleanupService: CleanupService, logger: ConsoleLogging, - pairingStorage: WCPairingStorage) { + pairingStorage: WCPairingStorage, + socketConnectionStatusPublisher: AnyPublisher +) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService @@ -55,6 +60,7 @@ class AuthClient { self.cleanupService = cleanupService self.logger = logger self.pairingStorage = pairingStorage + self.socketConnectionStatusPublisher = socketConnectionStatusPublisher setUpPublishers() } @@ -92,7 +98,7 @@ class AuthClient { } #if DEBUG - /// Delete all stored data sach as: pairings, sessions, keys + /// Delete all stored data such as: pairings, keys /// /// - Note: Doesn't unsubscribe from topics public func cleanup() throws { diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift new file mode 100644 index 000000000..6d48fa179 --- /dev/null +++ b/Sources/Auth/AuthClientFactory.swift @@ -0,0 +1,46 @@ +import Foundation +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectPairing + +public struct AuthClientFactory { + + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient) -> AuthClient { + let logger = ConsoleLogger(loggingLevel: .off) + let keyValueStorage = UserDefaults.standard + let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + } + + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { + let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let history = RPCHistory(keyValueStore: historyStorage) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + let messageFormatter = SIWEMessageFormatter() + let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) + let messageSigner = MessageSigner(signer: Signer()) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) + let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: messageFormatter, address: account?.address) + let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) + let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) + let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) + + return AuthClient(appPairService: appPairService, + appRequestService: appRequestService, + appRespondSubscriber: appRespondSubscriber, + walletPairService: walletPairService, + walletRequestSubscriber: walletRequestSubscriber, + walletRespondService: walletRespondService, + account: account, + pendingRequestsProvider: pendingRequestsProvider, + cleanupService: cleanupService, + logger: logger, + pairingStorage: pairingStore, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + } +} diff --git a/Sources/Auth/Services/Common/CleanupService.swift b/Sources/Auth/Services/Common/CleanupService.swift index dac2b63c5..6a5a01334 100644 --- a/Sources/Auth/Services/Common/CleanupService.swift +++ b/Sources/Auth/Services/Common/CleanupService.swift @@ -8,7 +8,7 @@ final class CleanupService { private let pairingStore: WCPairingStorage private let kms: KeyManagementServiceProtocol - init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore) { + init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol) { self.pairingStore = pairingStore self.kms = kms } diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index ffdc61603..d3bc9a3bb 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -22,6 +22,7 @@ extension NetworkInteracting { } class NetworkingInteractor: NetworkInteracting { + private var publishers = Set() private let relayClient: RelayClient private let serializer: Serializing private let rpcHistory: RPCHistory @@ -45,9 +46,10 @@ class NetworkingInteractor: NetworkInteracting { self.rpcHistory = rpcHistory self.logger = logger self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher - relayClient.onMessage = { [unowned self] topic, message in + relayClient.messagePublisher.sink { [unowned self] (topic, message) in manageSubscription(topic, message) } + .store(in: &publishers) } func subscribe(topic: String) async throws { diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index c2fc00bcb..cd3388428 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -6,7 +6,7 @@ import JSONRPC class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging - private let address: String + private let address: String? private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting var onRequest: ((_ id: RPCID, _ message: String) -> Void)? @@ -14,7 +14,7 @@ class WalletRequestSubscriber { init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, messageFormatter: SIWEMessageFormatting, - address: String) { + address: String?) { self.networkingInteractor = networkingInteractor self.logger = logger self.address = address @@ -23,6 +23,10 @@ class WalletRequestSubscriber { } private func subscribeForRequest() { + guard let address = address else { + logger.warn("unexpected request") + return + } networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.request.id, diff --git a/Sources/Auth/StorageDomainIdentifiers.swift b/Sources/Auth/StorageDomainIdentifiers.swift new file mode 100644 index 000000000..ed7156c67 --- /dev/null +++ b/Sources/Auth/StorageDomainIdentifiers.swift @@ -0,0 +1,6 @@ +import Foundation + +enum StorageDomainIdentifiers: String { + case jsonRpcHistory = "com.walletconnect.sdk.wc_jsonRpcHistoryRecord" + case pairings = "com.walletconnect.sdk.pairingSequences" +} diff --git a/Sources/Auth/Types/Aliases/Account.swift b/Sources/Auth/Types/Aliases/Account.swift index 84759e639..3ab41d381 100644 --- a/Sources/Auth/Types/Aliases/Account.swift +++ b/Sources/Auth/Types/Aliases/Account.swift @@ -1,4 +1,4 @@ import WalletConnectUtils import Foundation -typealias Account = WalletConnectUtils.Account +public typealias Account = WalletConnectUtils.Account diff --git a/Sources/Auth/Types/RequestParams.swift b/Sources/Auth/Types/RequestParams.swift index 24e7afab9..a3f456add 100644 --- a/Sources/Auth/Types/RequestParams.swift +++ b/Sources/Auth/Types/RequestParams.swift @@ -1,6 +1,6 @@ import Foundation -struct RequestParams { +public struct RequestParams { let domain: String let chainId: String let nonce: String diff --git a/Sources/Auth/Types/RespondParams.swift b/Sources/Auth/Types/RespondParams.swift index fcd9b3293..b3757a64a 100644 --- a/Sources/Auth/Types/RespondParams.swift +++ b/Sources/Auth/Types/RespondParams.swift @@ -1,6 +1,6 @@ import Foundation -struct RespondParams { +public struct RespondParams { let id: Int64 let topic: String let signature: CacaoSignature diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 77e5d6d8a..d8fc4d288 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -360,7 +360,7 @@ public final class SignClient { } #if DEBUG - /// Delete all stored data sach as: pairings, sessions, keys + /// Delete all stored data such as: pairings, sessions, keys /// /// - Note: Doesn't unsubscribe from topics public func cleanup() throws {