From 476b7758673b4a775d898c6513d2384634ead636 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Apr 2023 10:04:08 +0200 Subject: [PATCH 01/89] savepoint --- Example/IntegrationTests/Push/PushTests.swift | 20 ++++++++++- .../Client/Wallet/PushSubscribeService.swift | 34 +++++++++++++++++++ .../Client/Wallet/WalletPushClient.swift | 10 +++++- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index bf7b0d59e..c5e6d232f 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -234,7 +234,25 @@ final class PushTests: XCTestCase { wait(for: [expectation], timeout: InputConfig.defaultTimeout) } - private func sign(_ message: String) -> SigningResult { + // Push Subscribe + func testWalletCreatesSubscription() async { + let expectation = expectation(description: "expects to create push subscription") + + let publicKey = cast.subscribeTopic() + + try! await walletPushClient.subscribe(publicKey: publicKey) + walletPushClient.subscriptionsPublisher.sink { subscriptions in + XCTAssertNotNil(subscriptions.first) + expectation.fulfill() + } + await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) + } + +} + + +private extension PushTests { + func sign(_ message: String) -> SigningResult { let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create(projectId: InputConfig.projectId) return .signed(try! signer.sign(message: message, privateKey: privateKey, type: .eip191)) diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift new file mode 100644 index 000000000..512ada645 --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift @@ -0,0 +1,34 @@ + +import Foundation + +class PushSubscribeService { + + func subscribe(publicKey: String, account: Account, onSign: @escaping SigningCallback) async throws { + + logger.debug("Subscribing for Push") + + let peerPublicKey = try AgreementPublicKey(hex: publicKey) + let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() + + + + let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) + let pushTopic = keys.derivedTopic() + + _ = try await identityClient.register(account: account, onSign: onSign) + + try kms.setAgreementSecret(keys, topic: responseTopic) + + + } + + + + + + private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { + let selfPubKey = try kms.createX25519KeyPair() + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation) + return keys + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index c7848a79e..67beacd81 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -35,6 +35,7 @@ public class WalletPushClient { private let deletePushSubscriptionService: DeletePushSubscriptionService private let deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber + private let pushSubscribeService: PushSubscribeService public let logger: ConsoleLogging @@ -57,7 +58,9 @@ public class WalletPushClient { deletePushSubscriptionService: DeletePushSubscriptionService, deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber, resubscribeService: PushResubscribeService, - pushSubscriptionsObserver: PushSubscriptionsObserver) { + pushSubscriptionsObserver: PushSubscriptionsObserver, + pushSubscribeService: PushSubscribeService + ) { self.logger = logger self.pairingRegisterer = pairingRegisterer self.proposeResponder = proposeResponder @@ -69,9 +72,14 @@ public class WalletPushClient { self.deletePushSubscriptionSubscriber = deletePushSubscriptionSubscriber self.resubscribeService = resubscribeService self.pushSubscriptionsObserver = pushSubscriptionsObserver + self.pushSubscribeService = pushSubscribeService setupSubscriptions() } + public func subscribe(publicKey: String) async throws { + try await pushSubscribeService.subscribe(publicKey: publicKey) + } + public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws { try await proposeResponder.respond(requestId: id, onSign: onSign) } From 1bc1160e215e36685bcb9a525167d5e127e93014 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Apr 2023 11:39:28 +0200 Subject: [PATCH 02/89] implement subscribe method --- .../Wallet/PushSubscribeRequester.swift | 86 +++++++++++++++++++ .../Client/Wallet/PushSubscribeService.swift | 34 -------- .../Client/Wallet/WalletPushClient.swift | 8 +- .../PushSubscribeProtocolMethod.swift | 10 +++ .../AcceptSubscriptionJWTPayload.swift | 3 +- 5 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift delete mode 100644 Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift create mode 100644 Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift new file mode 100644 index 000000000..43d52c529 --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift @@ -0,0 +1,86 @@ + +import Foundation + +class PushSubscribeRequester { + + + private let keyserverURL: URL + private let identityClient: IdentityClient + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementService + private let logger: ConsoleLogging + private let subscriptionsStore: CodableStore + // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs + private let groupKeychainStorage: KeychainStorageProtocol + + init(keyserverURL: URL, + networkingInteractor: NetworkInteracting, + identityClient: IdentityClient, + logger: ConsoleLogging, + kms: KeyManagementService, + groupKeychainStorage: KeychainStorageProtocol, + subscriptionsStore: CodableStore + ) { + self.keyserverURL = keyserverURL + self.identityClient = identityClient + self.networkingInteractor = networkingInteractor + self.logger = logger + self.kms = kms + self.groupKeychainStorage = groupKeychainStorage + self.subscriptionsStore = subscriptionsStore + } + + func subscribe(publicKey: String, account: Account, onSign: @escaping SigningCallback) async throws { + + logger.debug("Subscribing for Push") + + let peerPublicKey = try AgreementPublicKey(hex: publicKey) + let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() + + +// + let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) +// let pushTopic = keys.derivedTopic() + + _ = try await identityClient.register(account: account, onSign: onSign) + + try kms.setAgreementSecret(keys, topic: subscribeTopic) + + + let request = try createJWTRequest(subscriptionAccount: account, dappUrl: <#T##String#>) + + let protocolMethod = PushSubscribeProtocolMethod() + + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) + +// logger.debug("PushSubscribeRequester: subscribing to push on topic: \(subscribeTopic)") +// +// try kms.setAgreementSecret(keys, topic: pushTopic) +// +// try groupKeychainStorage.add(keys, forKey: pushTopic) + +// logger.debug("Subscribing to push topic: \(pushTopic)") + +// try await networkingInteractor.subscribe(topic: pushTopic) + + + } + + + + private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { + let selfPubKey = try kms.createX25519KeyPair() + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation) + return keys + } + + private func createJWTRequest(subscriptionAccount: Account, dappUrl: String) throws -> RPCRequest { + let protocolMethod = PushSubscribeProtocolMethod().method + let jwtPayload = CreateSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl) + let wrapper = try identityClient.signAndCreateWrapper( + payload: jwtPayload, + account: subscriptionAccount + ) + return RPCRequest(method: protocolMethod, params: wrapper) + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift deleted file mode 100644 index 512ada645..000000000 --- a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeService.swift +++ /dev/null @@ -1,34 +0,0 @@ - -import Foundation - -class PushSubscribeService { - - func subscribe(publicKey: String, account: Account, onSign: @escaping SigningCallback) async throws { - - logger.debug("Subscribing for Push") - - let peerPublicKey = try AgreementPublicKey(hex: publicKey) - let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() - - - - let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) - let pushTopic = keys.derivedTopic() - - _ = try await identityClient.register(account: account, onSign: onSign) - - try kms.setAgreementSecret(keys, topic: responseTopic) - - - } - - - - - - private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { - let selfPubKey = try kms.createX25519KeyPair() - let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation) - return keys - } -} diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 67beacd81..6a2b71408 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -35,7 +35,7 @@ public class WalletPushClient { private let deletePushSubscriptionService: DeletePushSubscriptionService private let deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber - private let pushSubscribeService: PushSubscribeService + private let pushSubscribeRequester: PushSubscribeRequester public let logger: ConsoleLogging @@ -59,7 +59,7 @@ public class WalletPushClient { deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber, resubscribeService: PushResubscribeService, pushSubscriptionsObserver: PushSubscriptionsObserver, - pushSubscribeService: PushSubscribeService + pushSubscribeRequester: PushSubscribeRequester ) { self.logger = logger self.pairingRegisterer = pairingRegisterer @@ -72,12 +72,12 @@ public class WalletPushClient { self.deletePushSubscriptionSubscriber = deletePushSubscriptionSubscriber self.resubscribeService = resubscribeService self.pushSubscriptionsObserver = pushSubscriptionsObserver - self.pushSubscribeService = pushSubscribeService + self.pushSubscribeRequester = pushSubscribeRequester setupSubscriptions() } public func subscribe(publicKey: String) async throws { - try await pushSubscribeService.subscribe(publicKey: publicKey) + try await pushSubscribeRequester.subscribe(publicKey: publicKey) } public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws { diff --git a/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift new file mode 100644 index 000000000..a101dc0e5 --- /dev/null +++ b/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift @@ -0,0 +1,10 @@ + +import Foundation + +struct PushSubscribeProtocolMethod: ProtocolMethod { + let method: String = "wc_pushSubscribe" + + let requestConfig: RelayConfig = RelayConfig(tag: 4006, prompt: true, ttl: 86400) + + let responseConfig: RelayConfig = RelayConfig(tag: 4007, prompt: true, ttl: 86400) +} diff --git a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift index 3ca3f0bcc..aa2140fa1 100644 --- a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift +++ b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift @@ -1,6 +1,7 @@ import Foundation -struct AcceptSubscriptionJWTPayload: JWTClaimsCodable { +typealias AcceptSubscriptionJWTPayload = CreateSubscriptionJWTPayload +struct CreateSubscriptionJWTPayload: JWTClaimsCodable { struct Claims: JWTClaims { /// timestamp when jwt was issued From 660b81589c408974ad108a5203b2ac226c5bedf5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Apr 2023 15:29:26 +0200 Subject: [PATCH 03/89] savepoint --- .../Wallet/PushSubscribeRequester.swift | 17 ++++++--- .../Client/Wallet/WebDidResolver.swift | 37 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift index 43d52c529..52eb95ca6 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift @@ -9,6 +9,7 @@ class PushSubscribeRequester { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementService private let logger: ConsoleLogging + private let webDidResolver: WebDidResolver private let subscriptionsStore: CodableStore // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs private let groupKeychainStorage: KeychainStorageProtocol @@ -19,7 +20,8 @@ class PushSubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, groupKeychainStorage: KeychainStorageProtocol, - subscriptionsStore: CodableStore + subscriptionsStore: CodableStore, + webDidResolver: WebDidResolver ) { self.keyserverURL = keyserverURL self.identityClient = identityClient @@ -28,16 +30,17 @@ class PushSubscribeRequester { self.kms = kms self.groupKeychainStorage = groupKeychainStorage self.subscriptionsStore = subscriptionsStore + self.webDidResolver = webDidResolver } - func subscribe(publicKey: String, account: Account, onSign: @escaping SigningCallback) async throws { + func subscribe(dappUrl: String, account: Account, onSign: @escaping SigningCallback) async throws { + logger.debug("Subscribing for Push") - let peerPublicKey = try AgreementPublicKey(hex: publicKey) + let peerPublicKey = try await resolvePublicKey(dappUrl: dappUrl) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() - // let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) // let pushTopic = keys.derivedTopic() @@ -47,7 +50,7 @@ class PushSubscribeRequester { try kms.setAgreementSecret(keys, topic: subscribeTopic) - let request = try createJWTRequest(subscriptionAccount: account, dappUrl: <#T##String#>) + let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) let protocolMethod = PushSubscribeProtocolMethod() @@ -66,6 +69,10 @@ class PushSubscribeRequester { } + private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { + let didDoc = webDidResolver.resolveDidDoc(url: dappUrl) + + } private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift new file mode 100644 index 000000000..ecdc5b3af --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -0,0 +1,37 @@ +// + +import Foundation + +class WebDidResolver { + func resolveDidDoc(url: String) -> WebDidDoc { + + } +} + +struct WebDidDoc { + +} +// MARK: - WebDidDoc +struct WebDidDoc: Codable { + let context: [String] + let id: String + let verificationMethod: [VerificationMethod] + let authentication, assertionMethod, keyAgreement: [String] + + enum CodingKeys: String, CodingKey { + case context = "@context" + case id, verificationMethod, authentication, assertionMethod, keyAgreement + } +} + +// MARK: - VerificationMethod +struct VerificationMethod: Codable { + let id, type, controller: String + let publicKeyJwk: PublicKeyJwk +} + +// MARK: - PublicKeyJwk +struct PublicKeyJwk: Codable { + let kty, crv, x: String + let y: String? +} From aa63e435ed3c3dea661a5558ee2ce18429e1e7f8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Apr 2023 11:04:27 +0200 Subject: [PATCH 04/89] add web diddoc, add base64url public key init --- .../CryptoKitWrapper/AgreementCryptoKit.swift | 15 ++++++++ .../Wallet/PushSubscribeRequester.swift | 16 +++++++-- .../Client/Wallet/WebDidResolver.swift | 28 --------------- .../WalletConnectPush/Types/WebDidDoc.swift | 36 +++++++++++++++++++ 4 files changed, 65 insertions(+), 30 deletions(-) create mode 100644 Sources/WalletConnectPush/Types/WebDidDoc.swift diff --git a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift index 01f0ed2a2..8d60c4f7a 100644 --- a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift +++ b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift @@ -18,6 +18,9 @@ extension Curve25519.KeyAgreement.PrivateKey: Equatable { // MARK: - Public Key public struct AgreementPublicKey: GenericPasswordConvertible, Equatable { + enum Errors: Error { + case invalidBase64urlString + } fileprivate let key: Curve25519.KeyAgreement.PublicKey @@ -34,6 +37,18 @@ public struct AgreementPublicKey: GenericPasswordConvertible, Equatable { try self.init(rawRepresentation: data) } + public init(base64url: String) throws { + var base64 = base64url + .replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + + if base64.count % 4 != 0 { + base64.append(String(repeating: "=", count: 4 - base64.count % 4)) + } + guard let raw = Data(base64Encoded: base64) else { throw Errors.invalidBase64urlString } + try self.init(rawRepresentation: raw) + } + public var rawRepresentation: Data { key.rawRepresentation } diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift index 52eb95ca6..fedc3f198 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift @@ -3,6 +3,11 @@ import Foundation class PushSubscribeRequester { + enum Errors: Error { + case didDocDoesNotContainKeyAgreement + case noVerificationMethodForKey + case unsupportedCurve + } private let keyserverURL: URL private let identityClient: IdentityClient @@ -41,12 +46,13 @@ class PushSubscribeRequester { let peerPublicKey = try await resolvePublicKey(dappUrl: dappUrl) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() -// + let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) // let pushTopic = keys.derivedTopic() _ = try await identityClient.register(account: account, onSign: onSign) + try kms.setAgreementSecret(keys, topic: subscribeTopic) @@ -54,6 +60,8 @@ class PushSubscribeRequester { let protocolMethod = PushSubscribeProtocolMethod() + try await networkingInteractor.subscribe(topic: subscribeTopic) + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) // logger.debug("PushSubscribeRequester: subscribing to push on topic: \(subscribeTopic)") @@ -71,7 +79,11 @@ class PushSubscribeRequester { private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { let didDoc = webDidResolver.resolveDidDoc(url: dappUrl) - + guard let keyAgreement = didDoc.keyAgreement.first else { throw Errors.didDocDoesNotContainKeyAgreement } + guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyAgreement }) else { throw Errors.noVerificationMethodForKey } + guard verificationMethod.publicKeyJwk.crv == .X25519 else { throw Errors.unsupportedCurve} + let pubKeyBase64Url = verificationMethod.publicKeyJwk.x + return try AgreementPublicKey(base64url: pubKeyBase64Url) } diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift index ecdc5b3af..9c36e500b 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -7,31 +7,3 @@ class WebDidResolver { } } - -struct WebDidDoc { - -} -// MARK: - WebDidDoc -struct WebDidDoc: Codable { - let context: [String] - let id: String - let verificationMethod: [VerificationMethod] - let authentication, assertionMethod, keyAgreement: [String] - - enum CodingKeys: String, CodingKey { - case context = "@context" - case id, verificationMethod, authentication, assertionMethod, keyAgreement - } -} - -// MARK: - VerificationMethod -struct VerificationMethod: Codable { - let id, type, controller: String - let publicKeyJwk: PublicKeyJwk -} - -// MARK: - PublicKeyJwk -struct PublicKeyJwk: Codable { - let kty, crv, x: String - let y: String? -} diff --git a/Sources/WalletConnectPush/Types/WebDidDoc.swift b/Sources/WalletConnectPush/Types/WebDidDoc.swift new file mode 100644 index 000000000..b7bc997a2 --- /dev/null +++ b/Sources/WalletConnectPush/Types/WebDidDoc.swift @@ -0,0 +1,36 @@ + +import Foundation + +// MARK: - WebDidDoc +struct WebDidDoc: Codable { + let context: [String] + let id: String + let verificationMethod: [VerificationMethod] + let authentication: [String] + let keyAgreement: [String] + + enum CodingKeys: String, CodingKey { + case context = "@context" + case id, verificationMethod, authentication, keyAgreement + } +} +extension WebDidDoc { + + struct VerificationMethod: Codable { + let id: String + let type: String + let controller: String + let publicKeyJwk: PublicKeyJwk + } + + struct PublicKeyJwk: Codable { + enum Curve: Codable { + case X25519 + } + let kty: String + The x member contains the x coordinate for the elliptic curve point. It is represented as the base64url encoding of the coordinate's big endian representation. + + let crv: Curve + let x: String + } +} From 3699801ac559442f86eb98f6533ef2ae58b7cbdc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Apr 2023 12:49:47 +0200 Subject: [PATCH 05/89] Add PushSubscribeResponseSubscriber --- .../PushMessageSubscriber.swift | 0 .../PushRequestResponder.swift | 0 .../PushSubscribeRequester.swift | 17 +----- .../PushSubscribeResponseSubscriber.swift | 56 +++++++++++++++++++ .../WalletConnectPush/Types/WebDidDoc.swift | 2 +- 5 files changed, 59 insertions(+), 16 deletions(-) rename Sources/WalletConnectPush/Client/Wallet/{ => ProtocolEngine/wc_pushMessage}/PushMessageSubscriber.swift (100%) rename Sources/WalletConnectPush/Client/Wallet/{ => ProtocolEngine/wc_pushRequest}/PushRequestResponder.swift (100%) rename Sources/WalletConnectPush/Client/Wallet/{ => ProtocolEngine/wc_pushSubscribe}/PushSubscribeRequester.swift (85%) create mode 100644 Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/PushMessageSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushMessage/PushMessageSubscriber.swift similarity index 100% rename from Sources/WalletConnectPush/Client/Wallet/PushMessageSubscriber.swift rename to Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushMessage/PushMessageSubscriber.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift similarity index 100% rename from Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift rename to Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift similarity index 85% rename from Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift rename to Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index fedc3f198..793fe0d9b 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -14,8 +14,6 @@ class PushSubscribeRequester { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementService private let logger: ConsoleLogging - private let webDidResolver: WebDidResolver - private let subscriptionsStore: CodableStore // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs private let groupKeychainStorage: KeychainStorageProtocol @@ -25,7 +23,6 @@ class PushSubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, groupKeychainStorage: KeychainStorageProtocol, - subscriptionsStore: CodableStore, webDidResolver: WebDidResolver ) { self.keyserverURL = keyserverURL @@ -34,7 +31,6 @@ class PushSubscribeRequester { self.logger = logger self.kms = kms self.groupKeychainStorage = groupKeychainStorage - self.subscriptionsStore = subscriptionsStore self.webDidResolver = webDidResolver } @@ -62,18 +58,9 @@ class PushSubscribeRequester { try await networkingInteractor.subscribe(topic: subscribeTopic) - try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) - -// logger.debug("PushSubscribeRequester: subscribing to push on topic: \(subscribeTopic)") -// -// try kms.setAgreementSecret(keys, topic: pushTopic) -// -// try groupKeychainStorage.add(keys, forKey: pushTopic) - -// logger.debug("Subscribing to push topic: \(pushTopic)") - -// try await networkingInteractor.subscribe(topic: pushTopic) + logger.debug("PushSubscribeRequester: subscribing to subscribe topic: \(subscribeTopic)") + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift new file mode 100644 index 000000000..8ce89007d --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -0,0 +1,56 @@ + +import Foundation +import Combine + +class PushSubscribeResponseSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + private let logger: ConsoleLogging + private let subscriptionsStore: CodableStore + private let groupKeychainStorage: KeychainStorageProtocol + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging, + groupKeychainStorage: KeychainStorageProtocol, + subscriptionsStore: CodableStore) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.groupKeychainStorage = groupKeychainStorage + self.subscriptionsStore = subscriptionsStore + subscribeForSubscriptionResponse() + } + + + private func subscribeForSubscriptionResponse() { + let protocolMethod = PushSubscribeProtocolMethod() + networkingInteractor.responseSubscription(on: protocolMethod) + .sink {[unowned self] (payload: ResponseSubscriptionPayload) in + logger.debug("Received Push Subscribe response") + + guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { throw } + let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() + try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) + + try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) + + + let jwt = payload.request.jwtString + let (_, claims) = try CreateSubscriptionJWTPayload.decodeAndVerify(from: payload.request) + let account = try Account(DIDPKHString: claims.sub) + let metadata = ?? // wher we should take it from? + + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) + + subscriptionsStore.set(pushSubscription, forKey: pushTopic) + + + logger.debug("Subscribing to push topic: \(pushSubscriptionTopic)") + + try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) + + }.store(in: &publishers) + } +} diff --git a/Sources/WalletConnectPush/Types/WebDidDoc.swift b/Sources/WalletConnectPush/Types/WebDidDoc.swift index b7bc997a2..61f709d64 100644 --- a/Sources/WalletConnectPush/Types/WebDidDoc.swift +++ b/Sources/WalletConnectPush/Types/WebDidDoc.swift @@ -28,9 +28,9 @@ extension WebDidDoc { case X25519 } let kty: String - The x member contains the x coordinate for the elliptic curve point. It is represented as the base64url encoding of the coordinate's big endian representation. let crv: Curve + /// The x member contains the x coordinate for the elliptic curve point. It is represented as the base64url encoding of the coordinate's big endian representation. let x: String } } From a0a23a6f7611ebec4f356311a37525888aefafac Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 14 Apr 2023 11:46:23 +0200 Subject: [PATCH 06/89] implement resolveDidDoc() --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 3 ++- .../PushSubscribeResponseSubscriber.swift | 1 - .../WalletConnectPush/Client/Wallet/WebDidResolver.swift | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 793fe0d9b..6789c9a22 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -16,6 +16,7 @@ class PushSubscribeRequester { private let logger: ConsoleLogging // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs private let groupKeychainStorage: KeychainStorageProtocol + private let webDidResolver: WebDidResolver init(keyserverURL: URL, networkingInteractor: NetworkInteracting, @@ -65,7 +66,7 @@ class PushSubscribeRequester { } private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { - let didDoc = webDidResolver.resolveDidDoc(url: dappUrl) + let didDoc = webDidResolver.resolveDidDoc(domainUrl: dappUrl) guard let keyAgreement = didDoc.keyAgreement.first else { throw Errors.didDocDoesNotContainKeyAgreement } guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyAgreement }) else { throw Errors.noVerificationMethodForKey } guard verificationMethod.publicKeyJwk.crv == .X25519 else { throw Errors.unsupportedCurve} diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 8ce89007d..5462d2d75 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -46,7 +46,6 @@ class PushSubscribeResponseSubscriber { subscriptionsStore.set(pushSubscription, forKey: pushTopic) - logger.debug("Subscribing to push topic: \(pushSubscriptionTopic)") try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift index 9c36e500b..c1d80e604 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -1,9 +1,14 @@ -// import Foundation class WebDidResolver { - func resolveDidDoc(url: String) -> WebDidDoc { + enum Errors: Error { + case invalidUrl + } + func resolveDidDoc(domainUrl: String) async throws -> WebDidDoc { + guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/did.json") else { throw Errors.invalidUrl } + let (data, _) = try await URLSession.shared.data(from: didDocUrl) + return try JSONDecoder().decode(WebDidDoc.self, from: data) } } From 196dd4a9a41befa0aaab027266826a4f04c932bf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 14 Apr 2023 12:24:44 +0200 Subject: [PATCH 07/89] update wallet client factory --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 2 +- .../Client/Wallet/WalletPushClient.swift | 9 ++++++--- .../Client/Wallet/WalletPushClientFactory.swift | 10 +++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 6789c9a22..1dbe3a7bf 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -66,7 +66,7 @@ class PushSubscribeRequester { } private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { - let didDoc = webDidResolver.resolveDidDoc(domainUrl: dappUrl) + let didDoc = try await webDidResolver.resolveDidDoc(domainUrl: dappUrl) guard let keyAgreement = didDoc.keyAgreement.first else { throw Errors.didDocDoesNotContainKeyAgreement } guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyAgreement }) else { throw Errors.noVerificationMethodForKey } guard verificationMethod.publicKeyJwk.crv == .X25519 else { throw Errors.unsupportedCurve} diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 6a2b71408..397e0eee2 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -46,6 +46,7 @@ public class WalletPushClient { private let subscriptionsProvider: SubscriptionsProvider private let pushMessagesDatabase: PushMessagesDatabase private let resubscribeService: PushResubscribeService + private let pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber init(logger: ConsoleLogging, kms: KeyManagementServiceProtocol, @@ -59,7 +60,8 @@ public class WalletPushClient { deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber, resubscribeService: PushResubscribeService, pushSubscriptionsObserver: PushSubscriptionsObserver, - pushSubscribeRequester: PushSubscribeRequester + pushSubscribeRequester: PushSubscribeRequester, + pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber ) { self.logger = logger self.pairingRegisterer = pairingRegisterer @@ -73,11 +75,12 @@ public class WalletPushClient { self.resubscribeService = resubscribeService self.pushSubscriptionsObserver = pushSubscriptionsObserver self.pushSubscribeRequester = pushSubscribeRequester + self.pushSubscribeResponseSubscriber = pushSubscribeResponseSubscriber setupSubscriptions() } - public func subscribe(publicKey: String) async throws { - try await pushSubscribeRequester.subscribe(publicKey: publicKey) + public func subscribe(dappUrl: String, account: Account, onSign: @escaping SigningCallback) async throws { + try await pushSubscribeRequester.subscribe(dappUrl: dappUrl, account: account, onSign: onSign) } public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws { diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index ee88eb8de..01db1aa11 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -52,6 +52,12 @@ public struct WalletPushClientFactory { let deletePushSubscriptionSubscriber = DeletePushSubscriptionSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore) let resubscribeService = PushResubscribeService(networkInteractor: networkInteractor, subscriptionsStorage: subscriptionStore) let pushSubscriptionsObserver = PushSubscriptionsObserver(store: subscriptionStore) + + let webDidResolver = WebDidResolver() + let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, webDidResolver: webDidResolver) + + let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore) + return WalletPushClient( logger: logger, kms: kms, @@ -64,7 +70,9 @@ public struct WalletPushClientFactory { deletePushSubscriptionService: deletePushSubscriptionService, deletePushSubscriptionSubscriber: deletePushSubscriptionSubscriber, resubscribeService: resubscribeService, - pushSubscriptionsObserver: pushSubscriptionsObserver + pushSubscriptionsObserver: pushSubscriptionsObserver, + pushSubscribeRequester: pushSubscribeRequester, + pushSubscribeResponseSubscriber: pushSubscribeResponseSubscriber ) } } From ce07beca6f7a4c99f982ccde3b5ff7316838bce8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Apr 2023 08:52:07 +0200 Subject: [PATCH 08/89] fix errors --- Example/IntegrationTests/Push/PushTests.swift | 6 ++---- Example/SmokeTests.xctestplan | 1 + .../PushSubscribeResponseSubscriber.swift | 9 ++++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index c5e6d232f..a478b8558 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -238,13 +238,11 @@ final class PushTests: XCTestCase { func testWalletCreatesSubscription() async { let expectation = expectation(description: "expects to create push subscription") - let publicKey = cast.subscribeTopic() - - try! await walletPushClient.subscribe(publicKey: publicKey) + try! await walletPushClient.subscribe(dappUrl: <#T##String#>, account: Account.stub(), onSign: sign) walletPushClient.subscriptionsPublisher.sink { subscriptions in XCTAssertNotNil(subscriptions.first) expectation.fulfill() - } + }.store(in: &publishers) await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) } diff --git a/Example/SmokeTests.xctestplan b/Example/SmokeTests.xctestplan index a5ce3e0df..3bc6a711c 100644 --- a/Example/SmokeTests.xctestplan +++ b/Example/SmokeTests.xctestplan @@ -44,6 +44,7 @@ "PushTests\/testDappDeletePushSubscription()", "PushTests\/testRequestPush()", "PushTests\/testWalletApprovesPushRequest()", + "PushTests\/testWalletCreatesSubscription()", "PushTests\/testWalletDeletePushSubscription()", "PushTests\/testWalletRejectsPushRequest()", "RegistryTests", diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 5462d2d75..fe2742ba1 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -3,6 +3,9 @@ import Foundation import Combine class PushSubscribeResponseSubscriber { + enum Errors: Error { + case noKeyForTopic + } private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private var publishers = [AnyCancellable]() @@ -30,7 +33,7 @@ class PushSubscribeResponseSubscriber { .sink {[unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Subscribe response") - guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { throw } + guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { throw Errors.noKeyForTopic } let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) @@ -42,9 +45,9 @@ class PushSubscribeResponseSubscriber { let account = try Account(DIDPKHString: claims.sub) let metadata = ?? // wher we should take it from? - let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata) - subscriptionsStore.set(pushSubscription, forKey: pushTopic) + subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) logger.debug("Subscribing to push topic: \(pushSubscriptionTopic)") From 88feac88b32ce78c0d51967c08908f73cbcb1b4a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Apr 2023 10:26:28 +0200 Subject: [PATCH 09/89] Add PushClientProxy --- Package.swift | 2 +- .../PushClientProxy/PushClientProxy.swift | 99 +++++++++++++++++++ .../PushClientProxy/PushClientRequest.swift | 3 + .../PushClientRequestSubscriber.swift | 3 + Sources/Web3Inbox/Web3InboxImports.swift | 1 + Sources/Web3Inbox/WebView/WebViewEvent.swift | 10 ++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift create mode 100644 Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift create mode 100644 Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift diff --git a/Package.swift b/Package.swift index d65fe6fe8..219bd8598 100644 --- a/Package.swift +++ b/Package.swift @@ -84,7 +84,7 @@ let package = Package( dependencies: ["WalletConnectNetworking"]), .target( name: "Web3Inbox", - dependencies: ["WalletConnectChat"]), + dependencies: ["WalletConnectChat", "WalletConnectPush"]), .target( name: "WalletConnectSigner", dependencies: ["WalletConnectNetworking"]), diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift new file mode 100644 index 000000000..8d663b8d5 --- /dev/null +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -0,0 +1,99 @@ +import Foundation + +final class PushClientProxy { + + private let client: WalletPushClient + + var onSign: SigningCallback + var onResponse: ((RPCResponse) async throws -> Void)? + + init(client: WalletPushClient, onSign: @escaping SigningCallback) { + self.client = client + self.onSign = onSign + } + + func request(_ request: RPCRequest) async throws { + guard let event = PushWebViewEvent(rawValue: request.method) + else { throw Errors.unregisteredMethod } + + switch event { + case .approve: + let params = try parse(ApproveRequest.self, params: request.params) + try await client.reject(id: params.id) + try await respond(request: request) + case .reject: + let params = try parse(RejectRequest.self, params: request.params) + try await client.reject(id: params.id) + try await respond(request: request) + case .subscribe: + let params = try parse(SubscribeRequest.self, params: request.params) + try await client.subscribe(dappUrl: params.dappUrl, account: params.account, onSign: onSign) + try await respond(request: request) + case .getActiveSubscriptions: + let subscriptions = client.getActiveSubscriptions() + try await respond(with: subscriptions, request: request) + case .getMessageHistory: + let params = try parse(GetMessageHistoryRequest.self, params: request.params) + let messages = client.getMessageHistory(topic: params.topic) + try await respond(with: messages, request: request) + case .deleteSubscription: + let params = try parse(DeleteSubscriptionRequest.self, params: request.params) + try await client.deleteSubscription(topic: params.topic) + try await respond(request: request) + case .deletePushMessage: + let params = try parse(DeletePushMessageRequest.self, params: request.params) + client.deletePushMessage(id: params.id) + try await respond(request: request) + } + } +} + +private extension PushClientProxy { + + private typealias Blob = Dictionary + + enum Errors: Error { + case unregisteredMethod + case unregisteredParams + } + + struct ApproveRequest: Codable { + let id: RPCID + } + + struct RejectRequest: Codable { + let id: RPCID + } + + struct SubscribeRequest: Codable { + let dappUrl: String + let account: Account + } + + struct GetActiveSubscriptionsRequest: Codable { + + } + + struct GetMessageHistoryRequest: Codable { + let topic: String + } + + struct DeleteSubscriptionRequest: Codable { + let topic: String + } + + struct DeletePushMessageRequest: Codable { + let id: String + } + + func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { + guard let params = try params?.get(Request.self) + else { throw Errors.unregisteredParams } + return params + } + + func respond(with object: Object = Blob(), request: RPCRequest) async throws { + let response = RPCResponse(matchingRequest: request, result: object) + try await onResponse?(response) + } +} diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift new file mode 100644 index 000000000..57be8e8ec --- /dev/null +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift @@ -0,0 +1,3 @@ +// + +import Foundation diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift new file mode 100644 index 000000000..57be8e8ec --- /dev/null +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift @@ -0,0 +1,3 @@ +// + +import Foundation diff --git a/Sources/Web3Inbox/Web3InboxImports.swift b/Sources/Web3Inbox/Web3InboxImports.swift index 54b421e88..fd78f4977 100644 --- a/Sources/Web3Inbox/Web3InboxImports.swift +++ b/Sources/Web3Inbox/Web3InboxImports.swift @@ -1,3 +1,4 @@ #if !CocoaPods @_exported import WalletConnectChat +@_exported import WalletConnectPush #endif diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift index 3556bf3e8..79ff62175 100644 --- a/Sources/Web3Inbox/WebView/WebViewEvent.swift +++ b/Sources/Web3Inbox/WebView/WebViewEvent.swift @@ -12,3 +12,13 @@ enum WebViewEvent: String { case reject case invite } + +enum PushWebViewEvent: String { + case approve + case reject + case subscribe + case getActiveSubscriptions + case getMessageHistory + case deleteSubscription + case deletePushMessage +} From a3b6b69d3684cec34a2237677e5b038e969c6e98 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 17 Apr 2023 11:14:36 +0200 Subject: [PATCH 10/89] Add PushClientRequestSubscriber --- .../PushClientProxy/PushClientRequest.swift | 11 +++- .../PushClientRequestSubscriber.swift | 53 ++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift index 57be8e8ec..467a2c372 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift @@ -1,3 +1,10 @@ -// - import Foundation + +enum PushClientRequest: String { + case pushRequest = "push_request" + case pushMessage = "push-message" + + var method: String { + return rawValue + } +} diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift index 57be8e8ec..0d14ede5e 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift @@ -1,3 +1,52 @@ -// - import Foundation +import Combine + +final class PushClientRequestSubscriber { + + private var publishers: Set = [] + + private let client: WalletPushClient + private let logger: ConsoleLogging + + var onRequest: ((RPCRequest) async throws -> Void)? + + init(client: WalletPushClient, logger: ConsoleLogging) { + self.client = client + self.logger = logger + + setupSubscriptions() + } + + func setupSubscriptions() { + client.requestPublisher.sink { [unowned self] id, account, metadata in + let params = RequestPayload(id: id, account: account, metadata: metadata) + handle(event: .pushRequest, params: params) + }.store(in: &publishers) + client.pushMessagePublisher.sink { [unowned self] record in + handle(event: .pushMessage, params: record) + }.store(in: &publishers) + } +} + +private extension PushClientRequestSubscriber { + + struct RequestPayload: Codable { + let id: RPCID + let account: Account + let metadata: AppMetadata + } + + func handle(event: PushClientRequest, params: Codable) { + Task { + do { + let request = RPCRequest( + method: event.method, + params: params + ) + try await onRequest?(request) + } catch { + logger.error("Client Request error: \(error.localizedDescription)") + } + } + } +} From 845f6c0489ad44717736e036aec0e7afd72499fe Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 07:49:46 +0200 Subject: [PATCH 11/89] savepoint --- .../ChatClient/ChatClientProxy.swift | 2 +- .../Web3Inbox/WebView/ChatWebViewEvent.swift | 14 +++++++++++ .../Web3Inbox/WebView/PushWebViewEvent.swift | 11 +++++++++ Sources/Web3Inbox/WebView/WebViewEvent.swift | 24 ------------------- .../WebView/WebViewRequestSubscriber.swift | 1 + 5 files changed, 27 insertions(+), 25 deletions(-) create mode 100644 Sources/Web3Inbox/WebView/ChatWebViewEvent.swift create mode 100644 Sources/Web3Inbox/WebView/PushWebViewEvent.swift delete mode 100644 Sources/Web3Inbox/WebView/WebViewEvent.swift diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift index 1363b0c6a..ef51bd3b4 100644 --- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift +++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift @@ -13,7 +13,7 @@ final class ChatClientProxy { } func request(_ request: RPCRequest) async throws { - guard let event = WebViewEvent(rawValue: request.method) + guard let event = ChatWebViewEvent(rawValue: request.method) else { throw Errors.unregisteredMethod } switch event { diff --git a/Sources/Web3Inbox/WebView/ChatWebViewEvent.swift b/Sources/Web3Inbox/WebView/ChatWebViewEvent.swift new file mode 100644 index 000000000..eaf58ea47 --- /dev/null +++ b/Sources/Web3Inbox/WebView/ChatWebViewEvent.swift @@ -0,0 +1,14 @@ +import Foundation + +enum ChatWebViewEvent: String { + case getReceivedInvites + case getSentInvites + case getThreads + case register + case resolve + case getMessages + case message + case accept + case reject + case invite +} diff --git a/Sources/Web3Inbox/WebView/PushWebViewEvent.swift b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift new file mode 100644 index 000000000..59793f77b --- /dev/null +++ b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift @@ -0,0 +1,11 @@ +import Foundation + +enum PushWebViewEvent: String { + case approve + case reject + case subscribe + case getActiveSubscriptions + case getMessageHistory + case deleteSubscription + case deletePushMessage +} diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/WebViewEvent.swift deleted file mode 100644 index 79ff62175..000000000 --- a/Sources/Web3Inbox/WebView/WebViewEvent.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -enum WebViewEvent: String { - case getReceivedInvites - case getSentInvites - case getThreads - case register - case resolve - case getMessages - case message - case accept - case reject - case invite -} - -enum PushWebViewEvent: String { - case approve - case reject - case subscribe - case getActiveSubscriptions - case getMessageHistory - case deleteSubscription - case deletePushMessage -} diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index 9d7df5a54..e826ca927 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -18,6 +18,7 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { didReceive message: WKScriptMessage ) { guard message.name == WebViewRequestSubscriber.name else { return } + chat/push? guard let body = message.body as? String, let data = body.data(using: .utf8), From 97724544e7a83020087c69c5ef27c13e87e2505a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 08:58:40 +0200 Subject: [PATCH 12/89] Add dapps metadata store --- Package.swift | 2 +- .../PushSubscribeRequester.swift | 13 ++++++---- .../PushSubscribeResponseSubscriber.swift | 26 ++++++++++++++----- .../Client/Wallet/WalletPushClient.swift | 9 +++++-- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/Package.swift b/Package.swift index 219bd8598..10e12d42f 100644 --- a/Package.swift +++ b/Package.swift @@ -64,7 +64,7 @@ let package = Package( path: "Sources/Web3Wallet"), .target( name: "WalletConnectPush", - dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking", "WalletConnectIdentity"], + dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking", "WalletConnectIdentity", "WalletConnectSigner"], path: "Sources/WalletConnectPush"), .target( name: "WalletConnectEcho", diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 1dbe3a7bf..a0863f309 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -17,6 +17,7 @@ class PushSubscribeRequester { // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs private let groupKeychainStorage: KeychainStorageProtocol private let webDidResolver: WebDidResolver + private let dappsMetadataStore: CodableStore init(keyserverURL: URL, networkingInteractor: NetworkInteracting, @@ -24,7 +25,8 @@ class PushSubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, groupKeychainStorage: KeychainStorageProtocol, - webDidResolver: WebDidResolver + webDidResolver: WebDidResolver, + dappsMetadataStore: CodableStore ) { self.keyserverURL = keyserverURL self.identityClient = identityClient @@ -33,23 +35,24 @@ class PushSubscribeRequester { self.kms = kms self.groupKeychainStorage = groupKeychainStorage self.webDidResolver = webDidResolver + self.dappsMetadataStore = dappsMetadataStore } - func subscribe(dappUrl: String, account: Account, onSign: @escaping SigningCallback) async throws { + func subscribe(metadata: AppMetadata, account: Account, onSign: @escaping SigningCallback) async throws { + let dappUrl = metadata.url logger.debug("Subscribing for Push") let peerPublicKey = try await resolvePublicKey(dappUrl: dappUrl) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() - + dappsMetadataStore.set(metadata, forKey: dappUrl) + let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) -// let pushTopic = keys.derivedTopic() _ = try await identityClient.register(account: account, onSign: onSign) - try kms.setAgreementSecret(keys, topic: subscribeTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index fe2742ba1..cf6ba0f09 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -3,26 +3,32 @@ import Foundation import Combine class PushSubscribeResponseSubscriber { - enum Errors: Error { - case noKeyForTopic - } + private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let subscriptionsStore: CodableStore private let groupKeychainStorage: KeychainStorageProtocol + private let dappsMetadataStore: CodableStore + private var subscriptionPublisherSubject = PassthroughSubject, Never>() + var subscriptionPublisher: AnyPublisher, Never> { + return subscriptionPublisherSubject.eraseToAnyPublisher() + } init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, logger: ConsoleLogging, groupKeychainStorage: KeychainStorageProtocol, - subscriptionsStore: CodableStore) { + subscriptionsStore: CodableStore, + dappsMetadataStore: CodableStore + ) { self.networkingInteractor = networkingInteractor self.kms = kms self.logger = logger self.groupKeychainStorage = groupKeychainStorage self.subscriptionsStore = subscriptionsStore + self.dappsMetadataStore = dappsMetadataStore subscribeForSubscriptionResponse() } @@ -33,7 +39,10 @@ class PushSubscribeResponseSubscriber { .sink {[unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Subscribe response") - guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { throw Errors.noKeyForTopic } + guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { + logger.debug("PushSubscribeResponseSubscriber: no sym key for topic") + return + } let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) @@ -43,7 +52,12 @@ class PushSubscribeResponseSubscriber { let jwt = payload.request.jwtString let (_, claims) = try CreateSubscriptionJWTPayload.decodeAndVerify(from: payload.request) let account = try Account(DIDPKHString: claims.sub) - let metadata = ?? // wher we should take it from? + + guard let metadata = dappsMetadataStore.get(key: payload.topic) else { + logger.debug("PushSubscribeResponseSubscriber: no metadata for subscribe topic") + return + } + dappsMetadataStore.delete(forKey: payload.topic) let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 397e0eee2..ad54568a0 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -9,6 +9,11 @@ public class WalletPushClient { private var publishers = Set() + /// publishes new subscriptions + public var subscriptionPublisher: AnyPublisher, Never> { + return pushSubscribeResponseSubscriber.subscriptionPublisher + } + public var subscriptionsPublisher: AnyPublisher<[PushSubscription], Never> { return pushSubscriptionsObserver.subscriptionsPublisher } @@ -79,8 +84,8 @@ public class WalletPushClient { setupSubscriptions() } - public func subscribe(dappUrl: String, account: Account, onSign: @escaping SigningCallback) async throws { - try await pushSubscribeRequester.subscribe(dappUrl: dappUrl, account: account, onSign: onSign) + public func subscribe(metadata: AppMetadata, account: Account, onSign: @escaping SigningCallback) async throws { + try await pushSubscribeRequester.subscribe(metadata: metadata, account: account, onSign: onSign) } public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws { From a5ef423e9128555db4506b4b339fc3ef66a0f87b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 09:27:36 +0200 Subject: [PATCH 13/89] Fix push sdk build --- .../PushSubscribeResponseSubscriber.swift | 30 ++++++++++++------- .../Wallet/WalletPushClientFactory.swift | 7 +++-- .../PushStorageIdntifiers.swift | 1 + 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index cf6ba0f09..b4335b161 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -3,6 +3,9 @@ import Foundation import Combine class PushSubscribeResponseSubscriber { + enum Errors: Error { + case couldNotCreateSubscription + } private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol @@ -32,7 +35,6 @@ class PushSubscribeResponseSubscriber { subscribeForSubscriptionResponse() } - private func subscribeForSubscriptionResponse() { let protocolMethod = PushSubscribeProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) @@ -41,31 +43,37 @@ class PushSubscribeResponseSubscriber { guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { logger.debug("PushSubscribeResponseSubscriber: no sym key for topic") + subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() - try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) - - try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) - - let jwt = payload.request.jwtString - let (_, claims) = try CreateSubscriptionJWTPayload.decodeAndVerify(from: payload.request) - let account = try Account(DIDPKHString: claims.sub) - guard let metadata = dappsMetadataStore.get(key: payload.topic) else { - logger.debug("PushSubscribeResponseSubscriber: no metadata for subscribe topic") + var account: Account! + var metadata: AppMetadata! + do { + try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) + try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) + let (_, claims) = try CreateSubscriptionJWTPayload.decodeAndVerify(from: payload.request) + account = try Account(DIDPKHString: claims.sub) + metadata = try dappsMetadataStore.get(key: payload.topic) + } catch { + logger.debug("PushSubscribeResponseSubscriber: error: \(error)") + subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } + dappsMetadataStore.delete(forKey: payload.topic) let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) + subscriptionPublisherSubject.send(.success(pushSubscription)) + logger.debug("Subscribing to push topic: \(pushSubscriptionTopic)") - try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) + Task { try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) } }.store(in: &publishers) } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 01db1aa11..758c2bd28 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -54,9 +54,12 @@ public struct WalletPushClientFactory { let pushSubscriptionsObserver = PushSubscriptionsObserver(store: subscriptionStore) let webDidResolver = WebDidResolver() - let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, webDidResolver: webDidResolver) - let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore) + let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.dappsMetadataStore) + + let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, webDidResolver: webDidResolver, dappsMetadataStore: dappsMetadataStore) + + let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore) return WalletPushClient( logger: logger, diff --git a/Sources/WalletConnectPush/PushStorageIdntifiers.swift b/Sources/WalletConnectPush/PushStorageIdntifiers.swift index 5dd0f0a53..57ac3611c 100644 --- a/Sources/WalletConnectPush/PushStorageIdntifiers.swift +++ b/Sources/WalletConnectPush/PushStorageIdntifiers.swift @@ -3,4 +3,5 @@ import Foundation enum PushStorageIdntifiers { static let pushSubscription = "com.walletconnect.sdk.pushSbscription" static let pushMessagesRecords = "com.walletconnect.sdk.pushMessagesRecords" + static let dappsMetadataStore = "com.walletconnect.sdk.dappsMetadataStore" } From 3496c78fb5d263e39692951b45feb0cbd152027f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 10:40:48 +0200 Subject: [PATCH 14/89] Fix test and diddoc decoding issue --- Example/IntegrationTests/Push/PushTests.swift | 3 ++- .../wc_pushSubscribe/PushSubscribeRequester.swift | 5 ++--- .../wc_pushSubscribe/PushSubscribeResponseSubscriber.swift | 1 - Sources/WalletConnectPush/Types/WebDidDoc.swift | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index a478b8558..66dfa5306 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -238,7 +238,8 @@ final class PushTests: XCTestCase { func testWalletCreatesSubscription() async { let expectation = expectation(description: "expects to create push subscription") - try! await walletPushClient.subscribe(dappUrl: <#T##String#>, account: Account.stub(), onSign: sign) + let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) + try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) walletPushClient.subscriptionsPublisher.sink { subscriptions in XCTAssertNotNil(subscriptions.first) expectation.fulfill() diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index a0863f309..6d4212afa 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -55,15 +55,14 @@ class PushSubscribeRequester { try kms.setAgreementSecret(keys, topic: subscribeTopic) - let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) let protocolMethod = PushSubscribeProtocolMethod() - try await networkingInteractor.subscribe(topic: subscribeTopic) - logger.debug("PushSubscribeRequester: subscribing to subscribe topic: \(subscribeTopic)") + try await networkingInteractor.subscribe(topic: subscribeTopic) + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index b4335b161..8db34c0b6 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -47,7 +47,6 @@ class PushSubscribeResponseSubscriber { return } let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() - let jwt = payload.request.jwtString var account: Account! var metadata: AppMetadata! diff --git a/Sources/WalletConnectPush/Types/WebDidDoc.swift b/Sources/WalletConnectPush/Types/WebDidDoc.swift index 61f709d64..28f301773 100644 --- a/Sources/WalletConnectPush/Types/WebDidDoc.swift +++ b/Sources/WalletConnectPush/Types/WebDidDoc.swift @@ -6,7 +6,7 @@ struct WebDidDoc: Codable { let context: [String] let id: String let verificationMethod: [VerificationMethod] - let authentication: [String] + let authentication: [String]? let keyAgreement: [String] enum CodingKeys: String, CodingKey { @@ -24,8 +24,8 @@ extension WebDidDoc { } struct PublicKeyJwk: Codable { - enum Curve: Codable { - case X25519 + enum Curve: String, Codable { + case X25519 = "X25519" } let kty: String From 0dfd6e0d2b02a5985a9b53cbafb7f3f2514f0428 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 12:46:37 +0200 Subject: [PATCH 15/89] update logs --- .../ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift | 1 + Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 6d4212afa..0a9e0e652 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -68,6 +68,7 @@ class PushSubscribeRequester { } private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { + logger.debug("PushSubscribeRequester: Resolving DIDDoc for: \(dappUrl)") let didDoc = try await webDidResolver.resolveDidDoc(domainUrl: dappUrl) guard let keyAgreement = didDoc.keyAgreement.first else { throw Errors.didDocDoesNotContainKeyAgreement } guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyAgreement }) else { throw Errors.noVerificationMethodForKey } diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift index c1d80e604..f7e6617ad 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -2,6 +2,7 @@ import Foundation class WebDidResolver { + enum Errors: Error { case invalidUrl } From 5492e8ce44624749164bf55c009f3e3ae16d030e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 18 Apr 2023 14:00:19 +0200 Subject: [PATCH 16/89] Add web3inbox to the wallet --- Example/ExampleApp.xcodeproj/project.pbxproj | 27 ++++++++++++++ .../Wallet/Main/MainPresenter.swift | 1 + .../Wallet/Main/MainRouter.swift | 5 +++ .../Wallet/Main/Model/TabPage.swift | 7 +++- .../Wallet/Web3Inbox/Web3InboxModule.swift | 13 +++++++ .../Wallet/Web3Inbox/Web3InboxRouter.swift | 12 +++++++ .../Web3Inbox/Web3InboxViewController.swift | 36 +++++++++++++++++++ .../PushClientProxy/PushClientProxy.swift | 4 +-- .../WebView/WebViewRequestSubscriber.swift | 2 +- 9 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift create mode 100644 Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift create mode 100644 Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index aa09f61e1..b24b1f387 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -18,6 +18,10 @@ 84474A0129B9EB74005F520B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 84474A0029B9EB74005F520B /* Starscream */; }; 84474A0229B9ECA2005F520B /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; }; + 84536D6E29EEAE1F008EA8DB /* Web3InboxModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */; }; + 84536D7029EEAE28008EA8DB /* Web3InboxRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */; }; + 84536D7229EEAE32008EA8DB /* Web3InboxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */; }; + 84536D7429EEBCF0008EA8DB /* Web3Inbox in Frameworks */ = {isa = PBXBuildFile; productRef = 84536D7329EEBCF0008EA8DB /* Web3Inbox */; }; 845B8D8C2934B36C0084A966 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B8D8B2934B36C0084A966 /* Account.swift */; }; 847BD1D62989492500076C90 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D12989492500076C90 /* MainViewController.swift */; }; 847BD1D82989492500076C90 /* MainModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D32989492500076C90 /* MainModule.swift */; }; @@ -309,6 +313,9 @@ 8439CB88293F658E00F2F2E2 /* PushMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushMessage.swift; sourceTree = ""; }; 844749F329B9E5B9005F520B /* RelayIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RelayIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 844749F529B9E5B9005F520B /* RelayClientEndToEndTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayClientEndToEndTests.swift; sourceTree = ""; }; + 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxModule.swift; sourceTree = ""; }; + 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxRouter.swift; sourceTree = ""; }; + 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxViewController.swift; sourceTree = ""; }; 845AA7D929BA1EBA00F33739 /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = IntegrationTests.xctestplan; path = ExampleApp.xcodeproj/IntegrationTests.xctestplan; sourceTree = ""; }; 845AA7DC29BB424800F33739 /* SmokeTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SmokeTests.xctestplan; sourceTree = ""; }; 845B8D8B2934B36C0084A966 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; @@ -608,6 +615,7 @@ files = ( C56EE27D293F56F8004840D1 /* WalletConnectChat in Frameworks */, C5133A78294125CC00A8314C /* Web3 in Frameworks */, + 84536D7429EEBCF0008EA8DB /* Web3Inbox in Frameworks */, C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */, C55D349929630D440004314A /* Web3Wallet in Frameworks */, 84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */, @@ -681,6 +689,16 @@ path = RelayIntegrationTests; sourceTree = ""; }; + 84536D6C29EEAE0D008EA8DB /* Web3Inbox */ = { + isa = PBXGroup; + children = ( + 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */, + 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */, + 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */, + ); + path = Web3Inbox; + sourceTree = ""; + }; 847BD1DB2989493F00076C90 /* Main */ = { isa = PBXGroup; children = ( @@ -1391,6 +1409,7 @@ C56EE229293F5668004840D1 /* Wallet */ = { isa = PBXGroup; children = ( + 84536D6C29EEAE0D008EA8DB /* Web3Inbox */, 847BD1DB2989493F00076C90 /* Main */, C55D3477295DD4AA0004314A /* Welcome */, C55D3474295DCB850004314A /* AuthRequest */, @@ -1735,6 +1754,7 @@ C55D349829630D440004314A /* Web3Wallet */, C5B2F7042970573D000DBA0E /* SolanaSwift */, 84E6B85329787AAE00428BAF /* WalletConnectPush */, + 84536D7329EEBCF0008EA8DB /* Web3Inbox */, ); productName = ChatWallet; productReference = C56EE21B293F55ED004840D1 /* WalletApp.app */; @@ -2047,6 +2067,8 @@ C55D34B12965FB750004314A /* SessionProposalInteractor.swift in Sources */, C56EE247293F566D004840D1 /* ScanModule.swift in Sources */, C56EE28D293F5757004840D1 /* AppearanceConfigurator.swift in Sources */, + 84536D6E29EEAE1F008EA8DB /* Web3InboxModule.swift in Sources */, + 84536D7029EEAE28008EA8DB /* Web3InboxRouter.swift in Sources */, 847BD1D82989492500076C90 /* MainModule.swift in Sources */, 847BD1E7298A806800076C90 /* NotificationsInteractor.swift in Sources */, C56EE241293F566D004840D1 /* WalletModule.swift in Sources */, @@ -2074,6 +2096,7 @@ C55D347F295DD7140004314A /* AuthRequestModule.swift in Sources */, C56EE242293F566D004840D1 /* ScanPresenter.swift in Sources */, C56EE28B293F5757004840D1 /* SceneDelegate.swift in Sources */, + 84536D7229EEAE32008EA8DB /* Web3InboxViewController.swift in Sources */, C56EE276293F56D7004840D1 /* UIViewController.swift in Sources */, C56EE275293F56D7004840D1 /* InputConfig.swift in Sources */, C55D3493295DFA750004314A /* WelcomeModule.swift in Sources */, @@ -2806,6 +2829,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 84536D7329EEBCF0008EA8DB /* Web3Inbox */ = { + isa = XCSwiftPackageProductDependency; + productName = Web3Inbox; + }; 847CF3AE28E3141700F1D760 /* WalletConnectPush */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectPush; diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index d33cd6bf3..e41869a12 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -15,6 +15,7 @@ final class MainPresenter { return [ router.walletViewController(), router.notificationsViewController(), + router.web3InboxViewController() ] } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift index 75d71e842..8467818aa 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift @@ -22,6 +22,11 @@ final class MainRouter { .wrapToNavigationController() } + func web3InboxViewController() -> UIViewController { + return Web3InboxModule.create(app: app) + .wrapToNavigationController() + } + func present(pushRequest: PushRequest) { PushRequestModule.create(app: app, pushRequest: pushRequest) .presentFullScreen(from: viewController, transparentBackground: true) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift index 0416f515c..698349ebd 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift @@ -3,6 +3,7 @@ import UIKit enum TabPage: CaseIterable { case wallet case notifications + case web3Inbox var title: String { switch self { @@ -10,6 +11,8 @@ enum TabPage: CaseIterable { return "Apps" case .notifications: return "Notifications" + case .web3Inbox: + return "w3i" } } @@ -19,6 +22,8 @@ enum TabPage: CaseIterable { return UIImage(systemName: "house.fill")! case .notifications: return UIImage(systemName: "bell.fill")! + case .web3Inbox: + return UIImage(systemName: "bell.fill")! } } @@ -27,6 +32,6 @@ enum TabPage: CaseIterable { } static var enabledTabs: [TabPage] { - return [.wallet, .notifications] + return [.wallet, .notifications, .web3Inbox] } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift new file mode 100644 index 000000000..ad0633441 --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift @@ -0,0 +1,13 @@ +import SwiftUI + +final class Web3InboxModule { + + @discardableResult + static func create(app: Application) -> UIViewController { + let router = Web3InboxRouter(app: app) + let viewController = Web3InboxViewController() + router.viewController = viewController + return viewController + } + +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift new file mode 100644 index 000000000..3631c35be --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift @@ -0,0 +1,12 @@ +import UIKit + +final class Web3InboxRouter { + + weak var viewController: UIViewController! + + private let app: Application + + init(app: Application) { + self.app = app + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift new file mode 100644 index 000000000..1b4a16f5f --- /dev/null +++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift @@ -0,0 +1,36 @@ +import UIKit +import WebKit +import Web3Inbox + +final class Web3InboxViewController: UIViewController { + + + init() { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + let account = Account(blockchain: Blockchain("eip155:1")!, address: EthKeyStore.shared.address)! + Web3Inbox.configure(account: account, onSign: onSing) + + edgesForExtendedLayout = [] + navigationItem.title = "Web3Inbox SDK" + navigationItem.largeTitleDisplayMode = .never + view = Web3Inbox.instance.getWebView() + } +} + +private extension Web3InboxViewController { + + func onSing(_ message: String) -> SigningResult { + let privateKey = EthKeyStore.shared.privateKeyRaw + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() + let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) + return .signed(signature) + } +} diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index 8d663b8d5..5414c34f2 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -27,7 +27,7 @@ final class PushClientProxy { try await respond(request: request) case .subscribe: let params = try parse(SubscribeRequest.self, params: request.params) - try await client.subscribe(dappUrl: params.dappUrl, account: params.account, onSign: onSign) + try await client.subscribe(metadata: params.metadata, account: params.account, onSign: onSign) try await respond(request: request) case .getActiveSubscriptions: let subscriptions = client.getActiveSubscriptions() @@ -66,7 +66,7 @@ private extension PushClientProxy { } struct SubscribeRequest: Codable { - let dappUrl: String + let metadata: AppMetadata let account: Account } diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index e826ca927..f22118c92 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -18,7 +18,7 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { didReceive message: WKScriptMessage ) { guard message.name == WebViewRequestSubscriber.name else { return } - chat/push? +// chat/push? guard let body = message.body as? String, let data = body.data(using: .utf8), From 19d37d7702170c2c12989a6510ec1477c26406a2 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 19 Apr 2023 08:17:30 +0200 Subject: [PATCH 17/89] Add webview script formatter --- .../Web3Inbox/Web3InboxClientFactory.swift | 2 +- Sources/Web3Inbox/WebView/WebViewProxy.swift | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 76c01863c..8db29ff65 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -12,7 +12,7 @@ final class Web3InboxClientFactory { let logger = ConsoleLogger(suffix: "📬") let webviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() - let webViewProxy = WebViewProxy(webView: webView) + let webViewProxy = WebViewProxy(webView: webView, scriptFormatter: <#WebViewScriptFormatter#>) let clientProxy = ChatClientProxy(client: chatClient, onSign: onSign) let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger) diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index 73130fede..f3d0871bf 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -4,29 +4,41 @@ import WebKit actor WebViewProxy { private let webView: WKWebView - - init(webView: WKWebView) { + private let scriptFormatter: WebViewScriptFormatter + init(webView: WKWebView, + scriptFormatter: WebViewScriptFormatter) { self.webView = webView + self.scriptFormatter = scriptFormatter } @MainActor func respond(_ response: RPCResponse) async throws { let body = try response.json() - let script = await formatScript(body: body) + let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } @MainActor func request(_ request: RPCRequest) async throws { let body = try request.json() - let script = await formatScript(body: body) + let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } } -private extension WebViewProxy { +protocol WebViewScriptFormatter { + func formatScript(body: String) -> String +} + +class ChatWebViewScriptFormatter: WebViewScriptFormatter { func formatScript(body: String) -> String { return "window.\(WebViewRequestSubscriber.name).chat.postMessage(\(body))" } } + +class PushWebViewScriptFormatter: WebViewScriptFormatter { + func formatScript(body: String) -> String { + return "window.\(WebViewRequestSubscriber.name).push.postMessage(\(body))" + } +} From a8c90f8e4935346e500350baac3ce1c41ff41fc3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 19 Apr 2023 08:27:40 +0200 Subject: [PATCH 18/89] fix w3i factory --- Sources/Web3Inbox/Web3Inbox.swift | 2 +- Sources/Web3Inbox/Web3InboxClient.swift | 59 ++++++++++++++----- .../Web3Inbox/Web3InboxClientFactory.swift | 17 ++++-- 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index 1582c1ed1..9e8b803cf 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -7,7 +7,7 @@ public final class Web3Inbox { guard let account, let onSign else { fatalError("Error - you must call Web3Inbox.configure(_:) before accessing the shared instance.") } - return Web3InboxClientFactory.create(chatClient: Chat.instance, account: account, onSign: onSign) + return Web3InboxClientFactory.create(chatClient: Chat.instance, pushClient: Push.wallet, account: account, onSign: onSign) }() private static var account: Account? diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 1fbb15bd4..80db7e57d 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -7,28 +7,39 @@ public final class Web3InboxClient { private var account: Account private let logger: ConsoleLogging - private let clientProxy: ChatClientProxy - private let clientSubscriber: ChatClientRequestSubscriber + private let chatClientProxy: ChatClientProxy + private let chatClientSubscriber: ChatClientRequestSubscriber + + private let pushClientProxy: PushClientProxy + private let pushClientSubscriber: PushClientRequestSubscriber + + private let chatWebviewProxy: WebViewProxy + private let pushWebviewProxy: WebViewProxy - private let webviewProxy: WebViewProxy private let webviewSubscriber: WebViewRequestSubscriber init( webView: WKWebView, account: Account, logger: ConsoleLogging, - clientProxy: ChatClientProxy, + chatClientProxy: ChatClientProxy, clientSubscriber: ChatClientRequestSubscriber, - webviewProxy: WebViewProxy, - webviewSubscriber: WebViewRequestSubscriber + chatWebviewProxy: WebViewProxy, + pushWebviewProxy: WebViewProxy, + webviewSubscriber: WebViewRequestSubscriber, + pushClientProxy: PushClientProxy, + pushClientSubscriber: PushClientRequestSubscriber ) { self.webView = webView self.account = account self.logger = logger - self.clientProxy = clientProxy - self.clientSubscriber = clientSubscriber - self.webviewProxy = webviewProxy + self.chatClientProxy = chatClientProxy + self.chatClientSubscriber = clientSubscriber + self.chatWebviewProxy = chatWebviewProxy + self.pushWebviewProxy = pushWebviewProxy self.webviewSubscriber = webviewSubscriber + self.pushClientProxy = pushClientProxy + self.pushClientSubscriber = pushClientSubscriber setupSubscriptions() } @@ -41,7 +52,7 @@ public final class Web3InboxClient { _ account: Account, onSign: @escaping SigningCallback ) async throws { - clientProxy.onSign = onSign + chatClientProxy.onSign = onSign try await authorize(account: account) } } @@ -52,14 +63,30 @@ private extension Web3InboxClient { func setupSubscriptions() { webviewSubscriber.onRequest = { [unowned self] request in - try await self.clientProxy.request(request) + try await self.chatClientProxy.request(request) + } + + + // Chat + chatClientProxy.onResponse = { [unowned self] response in + try await self.chatWebviewProxy.respond(response) + } + + chatClientSubscriber.onRequest = { [unowned self] request in + try await self.chatWebviewProxy.request(request) } - clientProxy.onResponse = { [unowned self] response in - try await self.webviewProxy.respond(response) + + + // Push + + pushClientProxy.onResponse = { [unowned self] response in + try await self.pushWebviewProxy.respond(response) } - clientSubscriber.onRequest = { [unowned self] request in - try await self.webviewProxy.request(request) + + pushClientSubscriber.onRequest = { [unowned self] request in + try await self.pushWebviewProxy.request(request) } + } func authorize(account: Account) async throws { @@ -69,6 +96,6 @@ private extension Web3InboxClient { method: ChatClientRequest.setAccount.method, params: ["account": account.address] ) - try await webviewProxy.request(request) + try await chatWebviewProxy.request(request) } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 8db29ff65..f039a14ab 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -5,6 +5,7 @@ final class Web3InboxClientFactory { static func create( chatClient: ChatClient, + pushClient: WalletPushClient, account: Account, onSign: @escaping SigningCallback ) -> Web3InboxClient { @@ -12,18 +13,26 @@ final class Web3InboxClientFactory { let logger = ConsoleLogger(suffix: "📬") let webviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() - let webViewProxy = WebViewProxy(webView: webView, scriptFormatter: <#WebViewScriptFormatter#>) + let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter()) + let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter()) + let clientProxy = ChatClientProxy(client: chatClient, onSign: onSign) let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger) + let pushClientProxy = PushClientProxy(client: pushClient, onSign: onSign) + let pushClientSubscriber = PushClientRequestSubscriber(client: pushClient, logger: logger) + return Web3InboxClient( webView: webView, account: account, logger: ConsoleLogger(), - clientProxy: clientProxy, + chatClientProxy: clientProxy, clientSubscriber: clientSubscriber, - webviewProxy: webViewProxy, - webviewSubscriber: webviewSubscriber + chatWebviewProxy: chatWebViewProxy, + pushWebviewProxy: pushWebViewProxy, + webviewSubscriber: webviewSubscriber, + pushClientProxy: pushClientProxy, + pushClientSubscriber: pushClientSubscriber ) } From ec3628947794a09c6fb35a442523f3c48dd4308a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 19 Apr 2023 10:46:47 +0200 Subject: [PATCH 19/89] savepoint --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 10 +++++++--- Sources/Web3Inbox/Web3InboxClient.swift | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 0a9e0e652..373777a19 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -44,13 +44,16 @@ class PushSubscribeRequester { logger.debug("Subscribing for Push") - let peerPublicKey = try await resolvePublicKey(dappUrl: dappUrl) + let peerPublicKey = try AgreementPublicKey(hex: "4829d7bee9cee035e611e13e12992f5f40a41004473a96cca77558fa20710e72") let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() dappsMetadataStore.set(metadata, forKey: dappUrl) let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) +// try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) + + print("xxxxxxxxxxxxx \(keys.sharedKey.hexRepresentation)") _ = try await identityClient.register(account: account, onSign: onSign) try kms.setAgreementSecret(keys, topic: subscribeTopic) @@ -63,8 +66,7 @@ class PushSubscribeRequester { try await networkingInteractor.subscribe(topic: subscribeTopic) - try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod) - + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { @@ -80,6 +82,8 @@ class PushSubscribeRequester { private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { let selfPubKey = try kms.createX25519KeyPair() + print("yyyyyyyyyyy \(selfPubKey.rawRepresentation.description)") + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation) return keys } diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 80db7e57d..c5dea0df5 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -67,6 +67,8 @@ private extension Web3InboxClient { } + + // Chat chatClientProxy.onResponse = { [unowned self] response in try await self.chatWebviewProxy.respond(response) From 79a0a4bf5fd4b3451cdb4f39cf2e843f77fb0232 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 19 Apr 2023 13:48:47 +0200 Subject: [PATCH 20/89] savepoint --- .../wc_pushRequest/PushRequestResponder.swift | 2 +- .../wc_pushSubscribe/PushSubscribeRequester.swift | 15 ++++++++++----- .../PushSubscribeResponseSubscriber.swift | 2 +- .../AcceptSubscriptionJWTPayload.swift | 12 ++++++++++-- Sources/Web3Inbox/Web3InboxClient.swift | 10 ++++++---- Sources/Web3Inbox/Web3InboxClientFactory.swift | 2 +- Sources/Web3Inbox/WebView/WebViewFactory.swift | 4 ++++ .../WebView/WebViewRequestSubscriber.swift | 2 ++ 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index ae7b83357..0321131fa 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -83,7 +83,7 @@ class PushRequestResponder { } private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse { - let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl) + let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 373777a19..18370c538 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -51,20 +51,25 @@ class PushSubscribeRequester { let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) -// try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) + let subscriptionTopic = keys.derivedTopic() + + try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) + print("xxxxxxxxxxxxx \(keys.sharedKey.hexRepresentation)") _ = try await identityClient.register(account: account, onSign: onSign) - try kms.setAgreementSecret(keys, topic: subscribeTopic) + try kms.setAgreementSecret(keys, topic: subscriptionTopic) + + logger.debug("setting symm key for topic \(subscriptionTopic)") let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) let protocolMethod = PushSubscribeProtocolMethod() - logger.debug("PushSubscribeRequester: subscribing to subscribe topic: \(subscribeTopic)") + logger.debug("PushSubscribeRequester: subscribing to subscription topic: \(subscriptionTopic)") - try await networkingInteractor.subscribe(topic: subscribeTopic) + try await networkingInteractor.subscribe(topic: subscriptionTopic) try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } @@ -90,7 +95,7 @@ class PushSubscribeRequester { private func createJWTRequest(subscriptionAccount: Account, dappUrl: String) throws -> RPCRequest { let protocolMethod = PushSubscribeProtocolMethod().method - let jwtPayload = CreateSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl) + let jwtPayload = CreateSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 8db34c0b6..b5024b90f 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -42,7 +42,7 @@ class PushSubscribeResponseSubscriber { logger.debug("Received Push Subscribe response") guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { - logger.debug("PushSubscribeResponseSubscriber: no sym key for topic") + logger.debug("PushSubscribeResponseSubscriber: no symmetric key for topic \(payload.topic)") subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } diff --git a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift index b0d11b450..b0d3c17b5 100644 --- a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift +++ b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift @@ -18,6 +18,8 @@ struct CreateSubscriptionJWTPayload: JWTClaimsCodable { let sub: String /// description of action intent. Must be equal to "push_subscription" let act: String + + let scp: String } struct Wrapper: JWTWrapper { @@ -35,17 +37,20 @@ struct CreateSubscriptionJWTPayload: JWTClaimsCodable { let keyserver: URL let subscriptionAccount: Account let dappUrl: String + let scope: String - init(keyserver: URL, subscriptionAccount: Account, dappUrl: String) { + init(keyserver: URL, subscriptionAccount: Account, dappUrl: String, scope: String) { self.keyserver = keyserver self.subscriptionAccount = subscriptionAccount self.dappUrl = dappUrl + self.scope = scope } init(claims: Claims) throws { self.keyserver = try claims.ksu.asURL() self.subscriptionAccount = try Account(DIDPKHString: claims.sub) self.dappUrl = claims.aud + self.scope = claims.scp } func encode(iss: String) throws -> Claims { @@ -56,7 +61,10 @@ struct CreateSubscriptionJWTPayload: JWTClaimsCodable { ksu: keyserver.absoluteString, aud: dappUrl, sub: subscriptionAccount.did, - act: "push_subscription" + act: "push_subscription", + scp: scope ) } } +//🦋 Wallet: [Push] PushSubscribeResponseSubscriber: no sym key for topic - 11:59:04.1430 +//add scope to sunscriptionAuth diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index c5dea0df5..4c9ded73c 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -62,10 +62,12 @@ public final class Web3InboxClient { private extension Web3InboxClient { func setupSubscriptions() { - webviewSubscriber.onRequest = { [unowned self] request in - try await self.chatClientProxy.request(request) - } - +// chatWebviewSubscriber.onRequest = { [unowned self] request in +// print(request.method) +//// try await self.pushClientProxy.request(request) +// try await self.chatClientProxy.request(request) +// } +// diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index f039a14ab..32b1631f8 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -37,6 +37,6 @@ final class Web3InboxClientFactory { } private static func hostUrlString(account: Account) -> String { - return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&account=\(account.address)" + return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" } } diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index 39fc26212..9e917326b 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -17,6 +17,10 @@ final class WebViewFactory { webviewSubscriber, name: WebViewRequestSubscriber.name ) +// configuration.userContentController.add( +// webviewSubscriber2, +// name: WebViewRequestSubscriber.name +// ) let webview = WKWebView(frame: .zero, configuration: configuration) let request = URLRequest(url: URL(string: host)!) webview.load(request) diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index f22118c92..acb271617 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -25,6 +25,8 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { let request = try? JSONDecoder().decode(RPCRequest.self, from: data) else { return } + print(request) + Task { do { try await onRequest?(request) From 1f6a38349e324593a5e727600394ff57f1c43d97 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 20 Apr 2023 07:59:14 +0200 Subject: [PATCH 21/89] update passing key in networking interactor --- .../NetworkingInteractor.swift | 6 +++--- .../wc_pushSubscribe/PushSubscribeRequester.swift | 11 ++++------- .../PushSubscribeResponseSubscriber.swift | 6 +++++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 7c092867b..b6242cc00 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -134,7 +134,7 @@ public class NetworkingInteractor: NetworkInteracting { if let (deserializedJsonRpcRequest, derivedTopic): (RPCRequest, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { handleRequest(topic: topic, request: deserializedJsonRpcRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) } else if let (response, derivedTopic): (RPCResponse, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) + handleResponse(topic: topic, response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } else { logger.debug("Networking Interactor - Received unknown object type from networking relay") } @@ -149,11 +149,11 @@ public class NetworkingInteractor: NetworkInteracting { } } - private func handleResponse(response: RPCResponse, publishedAt: Date, derivedTopic: String?) { + private func handleResponse(topic: String, response: RPCResponse, publishedAt: Date, derivedTopic: String?) { do { try rpcHistory.resolve(response) let record = rpcHistory.get(recordId: response.id!)! - responsePublisherSubject.send((record.topic, record.request, response, publishedAt, derivedTopic)) + responsePublisherSubject.send((topic, record.request, response, publishedAt, derivedTopic)) } catch { logger.debug("Handle json rpc response error: \(error)") } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 18370c538..fcae9e591 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -44,19 +44,17 @@ class PushSubscribeRequester { logger.debug("Subscribing for Push") - let peerPublicKey = try AgreementPublicKey(hex: "4829d7bee9cee035e611e13e12992f5f40a41004473a96cca77558fa20710e72") + let peerPublicKey = try await resolvePublicKey(dappUrl: metadata.url) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() - dappsMetadataStore.set(metadata, forKey: dappUrl) - let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) let subscriptionTopic = keys.derivedTopic() + + dappsMetadataStore.set(metadata, forKey: subscriptionTopic) try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) - - print("xxxxxxxxxxxxx \(keys.sharedKey.hexRepresentation)") _ = try await identityClient.register(account: account, onSign: onSign) try kms.setAgreementSecret(keys, topic: subscriptionTopic) @@ -87,8 +85,7 @@ class PushSubscribeRequester { private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys { let selfPubKey = try kms.createX25519KeyPair() - print("yyyyyyyyyyy \(selfPubKey.rawRepresentation.description)") - + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation) return keys } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index b5024b90f..f8bf56d2c 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -49,7 +49,7 @@ class PushSubscribeResponseSubscriber { let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() var account: Account! - var metadata: AppMetadata! + var metadata: AppMetadata? do { try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) @@ -62,6 +62,10 @@ class PushSubscribeResponseSubscriber { return } + guard let metadata = metadata else { + logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(payload.topic)") + return + } dappsMetadataStore.delete(forKey: payload.topic) let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata) From f426080f92a86b0ec43057ba58d9146b522c8814 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 20 Apr 2023 08:30:46 +0200 Subject: [PATCH 22/89] Add chat and push web view subscribers --- Sources/Web3Inbox/Web3InboxClient.swift | 26 ++++++++++++------- .../Web3Inbox/Web3InboxClientFactory.swift | 10 ++++--- .../Web3Inbox/WebView/WebViewFactory.swift | 24 ++++++++++------- Sources/Web3Inbox/WebView/WebViewProxy.swift | 4 +-- .../WebView/WebViewRequestSubscriber.swift | 5 ++-- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 4c9ded73c..46fe57edc 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -16,7 +16,8 @@ public final class Web3InboxClient { private let chatWebviewProxy: WebViewProxy private let pushWebviewProxy: WebViewProxy - private let webviewSubscriber: WebViewRequestSubscriber + private let chatWebviewSubscriber: WebViewRequestSubscriber + private let pushWebviewSubscriber: WebViewRequestSubscriber init( webView: WKWebView, @@ -26,7 +27,8 @@ public final class Web3InboxClient { clientSubscriber: ChatClientRequestSubscriber, chatWebviewProxy: WebViewProxy, pushWebviewProxy: WebViewProxy, - webviewSubscriber: WebViewRequestSubscriber, + chatWebviewSubscriber: WebViewRequestSubscriber, + pushWebviewSubscriber: WebViewRequestSubscriber, pushClientProxy: PushClientProxy, pushClientSubscriber: PushClientRequestSubscriber ) { @@ -37,7 +39,8 @@ public final class Web3InboxClient { self.chatClientSubscriber = clientSubscriber self.chatWebviewProxy = chatWebviewProxy self.pushWebviewProxy = pushWebviewProxy - self.webviewSubscriber = webviewSubscriber + self.chatWebviewSubscriber = chatWebviewSubscriber + self.pushWebviewSubscriber = pushWebviewSubscriber self.pushClientProxy = pushClientProxy self.pushClientSubscriber = pushClientSubscriber @@ -62,13 +65,16 @@ public final class Web3InboxClient { private extension Web3InboxClient { func setupSubscriptions() { -// chatWebviewSubscriber.onRequest = { [unowned self] request in -// print(request.method) -//// try await self.pushClientProxy.request(request) -// try await self.chatClientProxy.request(request) -// } -// - + chatWebviewSubscriber.onRequest = { [unowned self] request in + print(request.method) + print("^^^^^^Chat^^^^^^") + try await self.chatClientProxy.request(request) + } + pushWebviewSubscriber.onRequest = { [unowned self] request in + print(request.method) + print("^^^^^^Push^^^^^^") + try await self.pushClientProxy.request(request) + } // Chat diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 32b1631f8..2520dc209 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -11,8 +11,9 @@ final class Web3InboxClientFactory { ) -> Web3InboxClient { let host = hostUrlString(account: account) let logger = ConsoleLogger(suffix: "📬") - let webviewSubscriber = WebViewRequestSubscriber(logger: logger) - let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create() + let chatWebviewSubscriber = WebViewRequestSubscriber(logger: logger) + let pushWebviewSubscriber = WebViewRequestSubscriber(logger: logger) + let webView = WebViewFactory(host: host, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create() let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter()) let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter()) @@ -30,13 +31,14 @@ final class Web3InboxClientFactory { clientSubscriber: clientSubscriber, chatWebviewProxy: chatWebViewProxy, pushWebviewProxy: pushWebViewProxy, - webviewSubscriber: webviewSubscriber, + chatWebviewSubscriber: chatWebviewSubscriber, + pushWebviewSubscriber: pushWebviewSubscriber, pushClientProxy: pushClientProxy, pushClientSubscriber: pushClientSubscriber ) } private static func hostUrlString(account: Account) -> String { - return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" + return "https://web3inbox-dev-hidden-git-feat-targeted-ex-3bf147-walletconnect1.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" } } diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index 9e917326b..f3363a43a 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -4,23 +4,29 @@ import WebKit final class WebViewFactory { private let host: String - private let webviewSubscriber: WebViewRequestSubscriber + private let chatWebviewSubscriber: WebViewRequestSubscriber + private let pushWebviewSubscriber: WebViewRequestSubscriber - init(host: String, webviewSubscriber: WebViewRequestSubscriber) { + init( + host: String, + chatWebviewSubscriber: WebViewRequestSubscriber, + pushWebviewSubscriber: WebViewRequestSubscriber + ) { self.host = host - self.webviewSubscriber = webviewSubscriber + self.chatWebviewSubscriber = chatWebviewSubscriber + self.pushWebviewSubscriber = pushWebviewSubscriber } func create() -> WKWebView { let configuration = WKWebViewConfiguration() configuration.userContentController.add( - webviewSubscriber, - name: WebViewRequestSubscriber.name + chatWebviewSubscriber, + name: WebViewRequestSubscriber.chat + ) + configuration.userContentController.add( + pushWebviewSubscriber, + name: WebViewRequestSubscriber.push ) -// configuration.userContentController.add( -// webviewSubscriber2, -// name: WebViewRequestSubscriber.name -// ) let webview = WKWebView(frame: .zero, configuration: configuration) let request = URLRequest(url: URL(string: host)!) webview.load(request) diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index f3d0871bf..435ceabea 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -33,12 +33,12 @@ protocol WebViewScriptFormatter { class ChatWebViewScriptFormatter: WebViewScriptFormatter { func formatScript(body: String) -> String { - return "window.\(WebViewRequestSubscriber.name).chat.postMessage(\(body))" + return "window.web3inbox.chat.postMessage(\(body))" } } class PushWebViewScriptFormatter: WebViewScriptFormatter { func formatScript(body: String) -> String { - return "window.\(WebViewRequestSubscriber.name).push.postMessage(\(body))" + return "window.web3inbox.push.postMessage(\(body))" } } diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index acb271617..32a2551eb 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -3,7 +3,8 @@ import WebKit final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { - static let name = "web3inbox" + static let chat = "web3inboxChat" + static let push = "web3inboxPush" var onRequest: ((RPCRequest) async throws -> Void)? @@ -17,8 +18,6 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { - guard message.name == WebViewRequestSubscriber.name else { return } -// chat/push? guard let body = message.body as? String, let data = body.data(using: .utf8), From c13a642d80ea2832fc919f9be3db28e84dd7b836 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 20 Apr 2023 15:38:37 +0200 Subject: [PATCH 23/89] fix method typo update w3i url --- Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift | 2 +- Sources/Web3Inbox/Web3InboxClientFactory.swift | 2 +- Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift index 467a2c372..bbc39b4ae 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift @@ -2,7 +2,7 @@ import Foundation enum PushClientRequest: String { case pushRequest = "push_request" - case pushMessage = "push-message" + case pushMessage = "push_message" var method: String { return rawValue diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 2520dc209..f5cdb3126 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -39,6 +39,6 @@ final class Web3InboxClientFactory { } private static func hostUrlString(account: Account) -> String { - return "https://web3inbox-dev-hidden-git-feat-targeted-ex-3bf147-walletconnect1.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" + return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" } } diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index 32a2551eb..3c1c8a6c6 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -24,8 +24,6 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { let request = try? JSONDecoder().decode(RPCRequest.self, from: data) else { return } - print(request) - Task { do { try await onRequest?(request) From c515ed007644a067172cf9521bdf03d9f7a2c1a6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 21 Apr 2023 12:00:34 +0200 Subject: [PATCH 24/89] update logs --- Sources/Web3Inbox/Web3InboxClient.swift | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 46fe57edc..0b2af1637 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -65,19 +65,9 @@ public final class Web3InboxClient { private extension Web3InboxClient { func setupSubscriptions() { - chatWebviewSubscriber.onRequest = { [unowned self] request in - print(request.method) - print("^^^^^^Chat^^^^^^") - try await self.chatClientProxy.request(request) - } - pushWebviewSubscriber.onRequest = { [unowned self] request in - print(request.method) - print("^^^^^^Push^^^^^^") - try await self.pushClientProxy.request(request) - } - // Chat + chatClientProxy.onResponse = { [unowned self] response in try await self.chatWebviewProxy.respond(response) } @@ -86,6 +76,10 @@ private extension Web3InboxClient { try await self.chatWebviewProxy.request(request) } + chatWebviewSubscriber.onRequest = { [unowned self] request in + logger.debug("w3i: chat method \(request.method) requested") + try await self.chatClientProxy.request(request) + } // Push @@ -97,6 +91,10 @@ private extension Web3InboxClient { try await self.pushWebviewProxy.request(request) } + pushWebviewSubscriber.onRequest = { [unowned self] request in + logger.debug("w3i: push method \(request.method) requested") + try await self.pushClientProxy.request(request) + } } func authorize(account: Account) async throws { From 92aa81ee481175970b0a56c116f26692883da1a0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 24 Apr 2023 13:28:45 +0200 Subject: [PATCH 25/89] Add NotifyUpdateRequester --- .../Dapp/ProposalResponseSubscriber.swift | 6 +-- .../NotifyUpdateRequester.swift | 51 +++++++++++++++++++ .../wc_pushRequest/PushRequestResponder.swift | 2 +- .../PushSubscribeRequester.swift | 2 +- .../PushSubscribeResponseSubscriber.swift | 4 +- .../Client/Wallet/WalletPushClient.swift | 9 +++- .../NotifyUpdateProtocolMethod.swift | 11 ++++ ...oad.swift => SubscriptionJWTPayload.swift} | 3 +- .../Types/NotificationScope.swift | 9 ++++ .../WalletConnectPush/Types/PushMessage.swift | 4 +- 10 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift create mode 100644 Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift rename Sources/WalletConnectPush/RPCRequests/{AcceptSubscriptionJWTPayload.swift => SubscriptionJWTPayload.swift} (94%) create mode 100644 Sources/WalletConnectPush/Types/NotificationScope.swift diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 603b81bbb..67e46fe5a 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -35,7 +35,7 @@ class ProposalResponseSubscriber { private func subscribeForProposalResponse() { let protocolMethod = PushRequestProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Proposal response") Task(priority: .userInitiated) { do { @@ -49,10 +49,10 @@ class ProposalResponseSubscriber { }.store(in: &publishers) } - private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) { + private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) { let jwt = payload.response.jwtString - _ = try AcceptSubscriptionJWTPayload.decodeAndVerify(from: payload.response) + _ = try SubscriptionJWTPayload.decodeAndVerify(from: payload.response) logger.debug("subscriptionAuth JWT validated") guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift new file mode 100644 index 000000000..54be6fe3a --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift @@ -0,0 +1,51 @@ + +import Foundation + +class NotifyUpdateRequester { + + enum Errors: Error { + case noSubscriptionForGivenTopic + } + + private let keyserverURL: URL + private let identityClient: IdentityClient + private let networkingInteractor: NetworkInteracting + private let logger: ConsoleLogging + private let subscriptionsStore: CodableStore + + + init(keyserverURL: URL, + identityClient: IdentityClient, + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + subscriptionsStore: CodableStore + ) { + self.keyserverURL = keyserverURL + self.identityClient = identityClient + self.networkingInteractor = networkingInteractor + self.logger = logger + self.subscriptionsStore = subscriptionsStore + } + + func update(topic: String, scope: Set) async throws { + + guard let subscription = try subscriptionsStore.get(key: topic) else { throw Errors.noSubscriptionForGivenTopic } + + let request = try createJWTRequest(subscriptionAccount: subscription.account, dappUrl: subscription.metadata.url, scope: scope) + + let protocolMethod = NotifyUpdateProtocolMethod() + + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) + } + + private func createJWTRequest(subscriptionAccount: Account, dappUrl: String, scope: Set) throws -> RPCRequest { + let protocolMethod = NotifyUpdateProtocolMethod().method + let scopeClaim = scope.map {$0.rawValue}.joined(separator: " ") + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scopeClaim) + let wrapper = try identityClient.signAndCreateWrapper( + payload: jwtPayload, + account: subscriptionAccount + ) + return RPCRequest(method: protocolMethod, params: wrapper) + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index 0321131fa..c86deab5d 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -83,7 +83,7 @@ class PushRequestResponder { } private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse { - let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index fcae9e591..94ec28388 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -92,7 +92,7 @@ class PushSubscribeRequester { private func createJWTRequest(subscriptionAccount: Account, dappUrl: String) throws -> RPCRequest { let protocolMethod = PushSubscribeProtocolMethod().method - let jwtPayload = CreateSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index f8bf56d2c..ed68b1c4e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -38,7 +38,7 @@ class PushSubscribeResponseSubscriber { private func subscribeForSubscriptionResponse() { let protocolMethod = PushSubscribeProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) - .sink {[unowned self] (payload: ResponseSubscriptionPayload) in + .sink {[unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Subscribe response") guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { @@ -53,7 +53,7 @@ class PushSubscribeResponseSubscriber { do { try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) - let (_, claims) = try CreateSubscriptionJWTPayload.decodeAndVerify(from: payload.request) + let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) } catch { diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index ad54568a0..f60650d77 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -52,6 +52,7 @@ public class WalletPushClient { private let pushMessagesDatabase: PushMessagesDatabase private let resubscribeService: PushResubscribeService private let pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber + private let notifyUpdateRequester: NotifyUpdateRequester init(logger: ConsoleLogging, kms: KeyManagementServiceProtocol, @@ -66,7 +67,8 @@ public class WalletPushClient { resubscribeService: PushResubscribeService, pushSubscriptionsObserver: PushSubscriptionsObserver, pushSubscribeRequester: PushSubscribeRequester, - pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber + pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber, + notifyUpdateRequester: NotifyUpdateRequester ) { self.logger = logger self.pairingRegisterer = pairingRegisterer @@ -81,6 +83,7 @@ public class WalletPushClient { self.pushSubscriptionsObserver = pushSubscriptionsObserver self.pushSubscribeRequester = pushSubscribeRequester self.pushSubscribeResponseSubscriber = pushSubscribeResponseSubscriber + self.notifyUpdateRequester = notifyUpdateRequester setupSubscriptions() } @@ -96,6 +99,10 @@ public class WalletPushClient { try await proposeResponder.respondError(requestId: id) } + public func update(topic: String, scope: Set) { + notifyUpdateRequester.update(topic: topic, scope: scope) + } + public func getActiveSubscriptions() -> [PushSubscription] { subscriptionsProvider.getActiveSubscriptions() } diff --git a/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift new file mode 100644 index 000000000..b95a67cc5 --- /dev/null +++ b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift @@ -0,0 +1,11 @@ + +import Foundation + +struct NotifyUpdateProtocolMethod: ProtocolMethod { + let method: String = "wc_notifyUpdate" + + let requestConfig: RelayConfig = RelayConfig(tag: 4008, prompt: true, ttl: 86400) + + let responseConfig: RelayConfig = RelayConfig(tag: 4009, prompt: true, ttl: 86400) +} + diff --git a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift similarity index 94% rename from Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift rename to Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift index b0d3c17b5..e094ddd14 100644 --- a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift +++ b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift @@ -1,7 +1,6 @@ import Foundation -typealias AcceptSubscriptionJWTPayload = CreateSubscriptionJWTPayload -struct CreateSubscriptionJWTPayload: JWTClaimsCodable { +struct SubscriptionJWTPayload: JWTClaimsCodable { struct Claims: JWTClaims { /// timestamp when jwt was issued diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift new file mode 100644 index 000000000..354f21228 --- /dev/null +++ b/Sources/WalletConnectPush/Types/NotificationScope.swift @@ -0,0 +1,9 @@ + +import Foundation + +public enum NotificationScope: String, Hashable { + case promotional + case transactional + case `private` + case alerts +} diff --git a/Sources/WalletConnectPush/Types/PushMessage.swift b/Sources/WalletConnectPush/Types/PushMessage.swift index eb32df560..393f8b6af 100644 --- a/Sources/WalletConnectPush/Types/PushMessage.swift +++ b/Sources/WalletConnectPush/Types/PushMessage.swift @@ -5,11 +5,13 @@ public struct PushMessage: Codable, Equatable { public let body: String public let icon: String public let url: String + public let type: String - public init(title: String, body: String, icon: String, url: String) { + public init(title: String, body: String, icon: String, url: String, type: String) { self.title = title self.body = body self.icon = icon self.url = url + self.type = type } } From 2eeb539cf3c1bf1e15c378b44cf63cc9d88e824c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 25 Apr 2023 09:57:54 +0200 Subject: [PATCH 26/89] Add Notify Update Response subscriber --- .../NotifyUpdateResponseSubscriber.swift | 70 +++++++++++++++++++ .../PushSubscribeResponseSubscriber.swift | 2 +- .../Client/Wallet/WalletPushClient.swift | 4 +- .../Types/NotificationScope.swift | 2 +- .../Types/PushSubscription.swift | 2 + 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift new file mode 100644 index 000000000..e763f89f9 --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -0,0 +1,70 @@ + +import Foundation +import Combine + +class NotifyUpdateResponseSubscriber { + enum Errors: Error { + case subscriptionDoesNotExist + } + + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + private let logger: ConsoleLogging + private let subscriptionsStore: CodableStore + private let groupKeychainStorage: KeychainStorageProtocol + private let dappsMetadataStore: CodableStore + private var subscriptionPublisherSubject = PassthroughSubject, Never>() + var subscriptionPublisher: AnyPublisher, Never> { + return subscriptionPublisherSubject.eraseToAnyPublisher() + } + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging, + groupKeychainStorage: KeychainStorageProtocol, + subscriptionsStore: CodableStore, + dappsMetadataStore: CodableStore + ) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.groupKeychainStorage = groupKeychainStorage + self.subscriptionsStore = subscriptionsStore + self.dappsMetadataStore = dappsMetadataStore + subscribeForUpdateResponse() + } + + private func subscribeForUpdateResponse() { + let protocolMethod = NotifyUpdateProtocolMethod() + networkingInteractor.responseSubscription(on: protocolMethod) + .sink {[unowned self] (payload: ResponseSubscriptionPayload) in + logger.debug("Received Push Update response") + + let subscriptionTopic = payload.topic + + // force unwrap is safe because jwt has been signed by self peer + let (_, claims) = try! SubscriptionJWTPayload.decodeAndVerify(from: payload.request) + let updatedScopeString = claims.scp + + let scope = updatedScopeString + .components(separatedBy: " ") + .compactMap { NotificationScope(rawValue: $0) } + + guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else { + logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist") + subscriptionPublisherSubject.send(.failure(Errors.subscriptionDoesNotExist)) + return + } + + let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope)) + + subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) + + subscriptionPublisherSubject.send(.success(updatedSubscription)) + + logger.debug("Updated Subscription") + + }.store(in: &publishers) + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index ed68b1c4e..79456577e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -68,7 +68,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) - let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata) + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: <#Set#>) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index f60650d77..9907c2928 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -99,8 +99,8 @@ public class WalletPushClient { try await proposeResponder.respondError(requestId: id) } - public func update(topic: String, scope: Set) { - notifyUpdateRequester.update(topic: topic, scope: scope) + public func update(topic: String, scope: Set) async throws { + try await notifyUpdateRequester.update(topic: topic, scope: scope) } public func getActiveSubscriptions() -> [PushSubscription] { diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift index 354f21228..4426725bb 100644 --- a/Sources/WalletConnectPush/Types/NotificationScope.swift +++ b/Sources/WalletConnectPush/Types/NotificationScope.swift @@ -1,7 +1,7 @@ import Foundation -public enum NotificationScope: String, Hashable { +public enum NotificationScope: String, Hashable, Codable { case promotional case transactional case `private` diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 783ec3dd1..e3c640ee8 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -7,4 +7,6 @@ public struct PushSubscription: Codable, Equatable { public let account: Account public let relay: RelayProtocolOptions public let metadata: AppMetadata + public let scope: Set + // Add expiry? } From 499188d1bc4b4a94af0e098647c1b04940a2a6d5 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Tue, 25 Apr 2023 11:11:36 +0100 Subject: [PATCH 27/89] Save --- .../AuthRequest/AuthRequestModule.swift | 4 +- .../AuthRequest/AuthRequestPresenter.swift | 5 +- .../Wallet/AuthRequest/AuthRequestView.swift | 26 +++++++-- .../ConnectionDetailsInteractor.swift | 2 +- .../ConnectionDetailsRouter.swift | 4 +- .../Wallet/Main/MainInteractor.swift | 2 +- .../Wallet/Main/MainPresenter.swift | 4 +- .../Wallet/Main/MainRouter.swift | 4 +- .../SessionProposalInteractor.swift | 4 +- .../SessionProposalModule.swift | 5 +- .../SessionProposalPresenter.swift | 5 +- .../SessionProposal/SessionProposalView.swift | 27 +++++++-- .../SessionRequest/SessionRequestModule.swift | 4 +- .../SessionRequestPresenter.swift | 5 +- .../SessionRequest/SessionRequestView.swift | 26 +++++++-- .../Wallet/Wallet/WalletInteractor.swift | 4 +- .../Wallet/Wallet/WalletPresenter.swift | 8 +-- .../Wallet/Wallet/WalletRouter.swift | 8 +-- Package.swift | 6 +- Sources/Auth/AuthClient.swift | 4 +- Sources/Auth/AuthClientProtocol.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 46 ++++++++++----- Sources/Auth/Types/Public/AuthContext.swift | 11 ++++ .../Serialiser/Serializer.swift | 17 ++++-- .../Serialiser/Serializing.swift | 4 +- .../NetworkInteracting.swift | 2 +- .../NetworkingInteractor.swift | 18 +++--- .../RequestSubscriptionPayload.swift | 4 +- .../PairingRequestsSubscriber.swift | 2 +- .../Client/Common/PushDecryptionService.swift | 2 +- .../Engine/Common/ApproveEngine.swift | 50 ++++++++++++++-- .../Engine/Common/SessionEngine.swift | 26 +++++++-- Sources/WalletConnectSign/Session.swift | 11 ++++ .../WalletConnectSign/Sign/SignClient.swift | 16 +++--- .../Sign/SignClientProtocol.swift | 4 +- .../AssertionRegistrer.swift | 25 +++++++- .../Register/VerifyResponse.swift | 5 ++ .../Register/VerifyService.swift | 34 +++++++++++ .../WalletConnectVerify/VerifyClient.swift | 9 +-- .../VerifyClientFactory.swift | 22 ++++--- Sources/Web3Wallet/Web3WalletClient.swift | 6 +- .../WalletRequestSubscriberTests.swift | 2 +- .../NetworkingInteractorMock.swift | 2 +- .../AppAttestationRegistrerTests.swift | 57 +++++++++++++++++++ .../ApproveEngineTests.swift | 4 +- 45 files changed, 420 insertions(+), 118 deletions(-) create mode 100644 Sources/Auth/Types/Public/AuthContext.swift create mode 100644 Sources/WalletConnectVerify/Register/VerifyResponse.swift create mode 100644 Sources/WalletConnectVerify/Register/VerifyService.swift diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift index 484e7d354..dea4664ef 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift @@ -3,10 +3,10 @@ import Web3Wallet final class AuthRequestModule { @discardableResult - static func create(app: Application, request: AuthRequest) -> UIViewController { + static func create(app: Application, request: AuthRequest, context: AuthContext?) -> UIViewController { let router = AuthRequestRouter(app: app) let interactor = AuthRequestInteractor() - let presenter = AuthRequestPresenter(interactor: interactor, router: router, request: request) + let presenter = AuthRequestPresenter(interactor: interactor, router: router, request: request, context: context) let view = AuthRequestView().environmentObject(presenter) let viewController = SceneViewController(viewModel: presenter, content: view) diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift index 56d11f511..2b7254949 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift @@ -8,6 +8,7 @@ final class AuthRequestPresenter: ObservableObject { private let router: AuthRequestRouter let request: AuthRequest + let verified: Bool? var message: String { return interactor.formatted(request: request) @@ -18,12 +19,14 @@ final class AuthRequestPresenter: ObservableObject { init( interactor: AuthRequestInteractor, router: AuthRequestRouter, - request: AuthRequest + request: AuthRequest, + context: AuthContext? ) { defer { setupInitialState() } self.interactor = interactor self.router = router self.request = request + self.verified = (context?.validation == .valid) ? true : (context?.validation == .unknown ? nil : false) } @MainActor diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestView.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestView.swift index 00086e83b..067a6b00e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestView.swift @@ -17,10 +17,28 @@ struct AuthRequestView: View { .resizable() .scaledToFit() - Text(presenter.request.payload.domain) - .foregroundColor(.grey8) - .font(.system(size: 22, weight: .bold, design: .rounded)) - .padding(.top, 10) + HStack { + Text(presenter.request.payload.domain) + .foregroundColor(.grey8) + .font(.system(size: 22, weight: .bold, design: .rounded)) + + if let verified = presenter.verified { + if verified { + Image(systemName: "checkmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .green) + } else { + Image(systemName: "xmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .red) + } + } else { + Image(systemName: "exclamationmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .orange) + } + } + .padding(.top, 10) Text("would like to connect") .foregroundColor(.grey8) diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift index c9fe17a65..b693b01f1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift @@ -3,7 +3,7 @@ import Combine import Web3Wallet final class ConnectionDetailsInteractor { - var requestPublisher: AnyPublisher { + var requestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { return Web3Wallet.instance.authRequestPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift index 4dcf67cd1..6007b767c 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift @@ -10,8 +10,8 @@ final class ConnectionDetailsRouter { self.app = app } - func present(request: AuthRequest) { - AuthRequestModule.create(app: app, request: request) + func present(request: AuthRequest, context: AuthContext) { + AuthRequestModule.create(app: app, request: request, context: context) .wrapToNavigationController() .present(from: viewController) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift index 7487db34c..a11b3c92a 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift @@ -5,7 +5,7 @@ import Foundation final class MainInteractor { - var sessionProposalPublisher: AnyPublisher { + var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { return Web3Wallet.instance.sessionProposalPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index d33cd6bf3..1ace0f96a 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -39,8 +39,8 @@ extension MainPresenter { interactor.sessionProposalPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] proposal in - self?.router.present(proposal: proposal) + .sink { [weak self] session in + self?.router.present(proposal: session.proposal, context: session.context) } .store(in: &disposeBag) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift index 75d71e842..3decadff3 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift @@ -12,8 +12,8 @@ final class MainRouter { .wrapToNavigationController() } - func present(proposal: Session.Proposal) { - SessionProposalModule.create(app: app, proposal: proposal) + func present(proposal: Session.Proposal, context: Session.Context?) { + SessionProposalModule.create(app: app, proposal: proposal, context: context) .presentFullScreen(from: viewController, transparentBackground: true) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift index 85199af00..25bb49a56 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalInteractor.swift @@ -7,8 +7,8 @@ final class SessionProposalInteractor { do { let sessionNamespaces = try AutoNamespaces.build( sessionProposal: proposal, - chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!], - methods: ["eth_sendTransaction", "personal_sign"], + chains: [Blockchain("eip155:1")!, Blockchain("eip155:5")!], + methods: ["eth_signTransaction", "personal_sign", "eth_signTypedData", "eth_sendTransaction", "eth_sign"], events: ["accountsChanged", "chainChanged"], accounts: [ Account(blockchain: Blockchain("eip155:5")!, address: ETHSigner.address)!, diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift index 4f0736d0a..db63282ad 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift @@ -4,13 +4,14 @@ import Web3Wallet final class SessionProposalModule { @discardableResult - static func create(app: Application, proposal: Session.Proposal) -> UIViewController { + static func create(app: Application, proposal: Session.Proposal, context: Session.Context?) -> UIViewController { let router = SessionProposalRouter(app: app) let interactor = SessionProposalInteractor() let presenter = SessionProposalPresenter( interactor: interactor, router: router, - proposal: proposal + proposal: proposal, + context: context ) let view = SessionProposalView().environmentObject(presenter) let viewController = SceneViewController(viewModel: presenter, content: view) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift index 3c5eca87f..5ec61140e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift @@ -8,18 +8,21 @@ final class SessionProposalPresenter: ObservableObject { private let router: SessionProposalRouter let sessionProposal: Session.Proposal + let verified: Bool? private var disposeBag = Set() init( interactor: SessionProposalInteractor, router: SessionProposalRouter, - proposal: Session.Proposal + proposal: Session.Proposal, + context: Session.Context? ) { defer { setupInitialState() } self.interactor = interactor self.router = router self.sessionProposal = proposal + self.verified = (context?.validation == .valid) ? true : (context?.validation == .unknown ? nil : false) } @MainActor diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift index 9c69a7043..97717d9b5 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalView.swift @@ -18,10 +18,29 @@ struct SessionProposalView: View { .resizable() .scaledToFit() - Text(presenter.sessionProposal.proposer.name) - .foregroundColor(.grey8) - .font(.system(size: 22, weight: .bold, design: .rounded)) - .padding(.top, 10) + HStack { + Text(presenter.sessionProposal.proposer.name) + .foregroundColor(.grey8) + .font(.system(size: 22, weight: .bold, design: .rounded)) + + if let verified = presenter.verified { + if verified { + Image(systemName: "checkmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .green) + } else { + Image(systemName: "xmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .red) + } + } else { + Image(systemName: "exclamationmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .orange) + } + } + + .padding(.top, 10) Text("would like to connect") .foregroundColor(.grey8) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift index 7c1aca962..f48de1900 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift @@ -3,10 +3,10 @@ import Web3Wallet final class SessionRequestModule { @discardableResult - static func create(app: Application, sessionRequest: Request) -> UIViewController { + static func create(app: Application, sessionRequest: Request, sessionContext: Session.Context?) -> UIViewController { let router = SessionRequestRouter(app: app) let interactor = SessionRequestInteractor() - let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest) + let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest, context: sessionContext) let view = SessionRequestView().environmentObject(presenter) let viewController = SceneViewController(viewModel: presenter, content: view) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift index c811d9f77..f216ff368 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift @@ -11,6 +11,7 @@ final class SessionRequestPresenter: ObservableObject { @Published var errorMessage = "Error" let sessionRequest: Request + let verified: Bool? var message: String { return String(describing: sessionRequest.params.value) @@ -21,12 +22,14 @@ final class SessionRequestPresenter: ObservableObject { init( interactor: SessionRequestInteractor, router: SessionRequestRouter, - sessionRequest: Request + sessionRequest: Request, + context: Session.Context? ) { defer { setupInitialState() } self.interactor = interactor self.router = router self.sessionRequest = sessionRequest + self.verified = (context?.validation == .valid) ? true : (context?.validation == .unknown ? nil : false) } @MainActor diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestView.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestView.swift index e6a719606..dc56b2e24 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestView.swift @@ -17,10 +17,28 @@ struct SessionRequestView: View { .resizable() .scaledToFit() - Text(presenter.sessionRequest.method) - .foregroundColor(.grey8) - .font(.system(size: 22, weight: .bold, design: .rounded)) - .padding(.top, 10) + HStack { + Text(presenter.sessionRequest.method) + .foregroundColor(.grey8) + .font(.system(size: 22, weight: .bold, design: .rounded)) + + if let verified = presenter.verified { + if verified { + Image(systemName: "checkmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .green) + } else { + Image(systemName: "xmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .red) + } + } else { + Image(systemName: "exclamationmark.shield.fill") + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .orange) + } + } + .padding(.top, 10) if presenter.message != "[:]" { authRequestView() diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift index e709edeae..4b0f24b08 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift @@ -4,11 +4,11 @@ import Web3Wallet import WalletConnectPush final class WalletInteractor { - var requestPublisher: AnyPublisher { + var requestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { return Web3Wallet.instance.authRequestPublisher } - var sessionRequestPublisher: AnyPublisher { + var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { return Web3Wallet.instance.sessionRequestPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift index 7a98087ad..b1c406c3f 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift @@ -61,15 +61,15 @@ extension WalletPresenter { private func setupInitialState() { interactor.requestPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] request in - self?.router.present(request: request) + .sink { [weak self] result in + self?.router.present(request: result.request, context: result.context) } .store(in: &disposeBag) interactor.sessionRequestPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] sessionRequest in - self?.router.present(sessionRequest: sessionRequest) + .sink { [weak self] request, context in + self?.router.present(sessionRequest: request, sessionContext: context) }.store(in: &disposeBag) interactor.sessionsPublisher diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift index 2eb2fcfd4..3c5e2a31f 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift @@ -10,13 +10,13 @@ final class WalletRouter { self.app = app } - func present(request: AuthRequest) { - AuthRequestModule.create(app: app, request: request) + func present(request: AuthRequest, context: AuthContext?) { + AuthRequestModule.create(app: app, request: request, context: context) .presentFullScreen(from: viewController, transparentBackground: true) } - func present(sessionRequest: Request) { - SessionRequestModule.create(app: app, sessionRequest: sessionRequest) + func present(sessionRequest: Request, sessionContext: Session.Context?) { + SessionRequestModule.create(app: app, sessionRequest: sessionRequest, sessionContext: sessionContext) .presentFullScreen(from: viewController, transparentBackground: true) } diff --git a/Package.swift b/Package.swift index d65fe6fe8..d2636a694 100644 --- a/Package.swift +++ b/Package.swift @@ -48,7 +48,7 @@ let package = Package( targets: [ .target( name: "WalletConnectSign", - dependencies: ["WalletConnectPairing"], + dependencies: ["WalletConnectPairing", "WalletConnectVerify"], path: "Sources/WalletConnectSign"), .target( name: "WalletConnectChat", @@ -111,7 +111,7 @@ let package = Package( dependencies: []), .target( name: "WalletConnectVerify", - dependencies: ["WalletConnectUtils"]), + dependencies: ["WalletConnectUtils", "WalletConnectNetworking"]), .testTarget( name: "WalletConnectSignTests", dependencies: ["WalletConnectSign", "WalletConnectUtils", "TestingUtils"]), @@ -132,7 +132,7 @@ let package = Package( dependencies: ["WalletConnectRelay", "WalletConnectUtils", "TestingUtils"]), .testTarget( name: "VerifyTests", - dependencies: ["WalletConnectVerify", "TestingUtils"]), + dependencies: ["WalletConnectVerify", "TestingUtils", "WalletConnectSign"]), .testTarget( name: "WalletConnectKMSTests", dependencies: ["WalletConnectKMS", "WalletConnectUtils", "TestingUtils"]), diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 2ab5e242a..dc66e9de3 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -13,7 +13,7 @@ public class AuthClient: AuthClientProtocol { /// Publisher that sends authentication requests /// /// Wallet should subscribe on events in order to receive auth requests. - public var authRequestPublisher: AnyPublisher { + public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { authRequestPublisherSubject.eraseToAnyPublisher() } @@ -37,7 +37,7 @@ public class AuthClient: AuthClientProtocol { private let pairingRegisterer: PairingRegisterer private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - private var authRequestPublisherSubject = PassthroughSubject() + private var authRequestPublisherSubject = PassthroughSubject<(request: AuthRequest, context: AuthContext?), Never>() private let appRequestService: AppRequestService private let appRespondSubscriber: AppRespondSubscriber private let walletRequestSubscriber: WalletRequestSubscriber diff --git a/Sources/Auth/AuthClientProtocol.swift b/Sources/Auth/AuthClientProtocol.swift index 31413cb76..520154a26 100644 --- a/Sources/Auth/AuthClientProtocol.swift +++ b/Sources/Auth/AuthClientProtocol.swift @@ -2,7 +2,7 @@ import Foundation import Combine public protocol AuthClientProtocol { - var authRequestPublisher: AnyPublisher { get } + var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { get } func formatMessage(payload: AuthPayload, address: String) throws -> String func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 67b541fd2..589065fed 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -1,6 +1,8 @@ import Foundation import Combine +import WalletConnectVerify + class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging @@ -8,34 +10,52 @@ class WalletRequestSubscriber { private var publishers = [AnyCancellable]() private let walletErrorResponder: WalletErrorResponder private let pairingRegisterer: PairingRegisterer - var onRequest: ((AuthRequest) -> Void)? - + var onRequest: (((AuthRequest, AuthContext?)) -> Void)? + init( networkingInteractor: NetworkInteracting, logger: ConsoleLogging, kms: KeyManagementServiceProtocol, walletErrorResponder: WalletErrorResponder, pairingRegisterer: PairingRegisterer) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.kms = kms - self.walletErrorResponder = walletErrorResponder - self.pairingRegisterer = pairingRegisterer - subscribeForRequest() - } - + self.networkingInteractor = networkingInteractor + self.logger = logger + self.kms = kms + self.walletErrorResponder = walletErrorResponder + self.pairingRegisterer = pairingRegisterer + subscribeForRequest() + } + private func subscribeForRequest() { pairingRegisterer.register(method: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") - + pairingRegisterer.activate( pairingTopic: payload.topic, peerMetadata: payload.request.requester.metadata ) - + let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) - onRequest?(request) + + if #available(iOS 14.0, *) { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() + + Task(priority: .high) { + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + let authContext = AuthContext( + origin: origin, + validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onRequest?((request, authContext)) + } + } else { + onRequest?((request, nil)) + } + }.store(in: &publishers) } } diff --git a/Sources/Auth/Types/Public/AuthContext.swift b/Sources/Auth/Types/Public/AuthContext.swift new file mode 100644 index 000000000..82c189564 --- /dev/null +++ b/Sources/Auth/Types/Public/AuthContext.swift @@ -0,0 +1,11 @@ +public struct AuthContext: Equatable, Hashable { + public enum ValidationStatus { + case unknown + case valid + case invalid + } + + public let origin: String? + public let validation: ValidationStatus + public let verifyUrl: String +} diff --git a/Sources/WalletConnectKMS/Serialiser/Serializer.swift b/Sources/WalletConnectKMS/Serialiser/Serializer.swift index 88abeda39..44b67d242 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializer.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializer.swift @@ -40,12 +40,13 @@ public class Serializer: Serializing { /// - topic: Topic that is associated with a symetric key for decrypting particular codable object /// - encodedEnvelope: Envelope to deserialize and decrypt /// - Returns: Deserialized object - public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?) { + public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, rawData: String?) { let envelope = try Envelope(encodedEnvelope) switch envelope.type { case .type0: let deserialisedType: T = try handleType0Envelope(topic, envelope) - return (deserialisedType, nil) + let rawData = try rawData(topic, envelope) + return (deserialisedType, nil, rawData) case .type1(let peerPubKey): return try handleType1Envelope(topic, peerPubKey: peerPubKey, sealbox: envelope.sealbox) } @@ -59,7 +60,7 @@ public class Serializer: Serializing { } } - private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String) { + private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String, String?) { guard let selfPubKey = kms.getPublicKey(for: topic) else { throw Errors.publicKeyForTopicNotFound } @@ -67,11 +68,19 @@ public class Serializer: Serializing { let decodedType: T = try decode(sealbox: sealbox, symmetricKey: agreementKeys.sharedKey.rawRepresentation) let derivedTopic = agreementKeys.derivedTopic() try kms.setAgreementSecret(agreementKeys, topic: derivedTopic) - return (decodedType, derivedTopic) + return (decodedType, derivedTopic, nil) } private func decode(sealbox: Data, symmetricKey: Data) throws -> T { let decryptedData = try codec.decode(sealbox: sealbox, symmetricKey: symmetricKey) return try JSONDecoder().decode(T.self, from: decryptedData) } + + private func rawData(_ topic: String, _ envelope: Envelope) throws -> String? { + if let symmetricKey = kms.getSymmetricKeyRepresentable(for: topic) { + return try String(data: codec.decode(sealbox: envelope.sealbox, symmetricKey: symmetricKey), encoding: .utf8) + } else { + throw Errors.symmetricKeyForTopicNotFound + } + } } diff --git a/Sources/WalletConnectKMS/Serialiser/Serializing.swift b/Sources/WalletConnectKMS/Serialiser/Serializing.swift index 81d37229f..dcf8090e9 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializing.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializing.swift @@ -3,12 +3,12 @@ import Foundation public protocol Serializing { func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key - func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?) + func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, rawData: String?) } public extension Serializing { /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key - func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?)? { + func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?, rawData: String?)? { return try? deserialize(topic: topic, encodedEnvelope: encodedEnvelope) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 8f47c6cc9..ecadf8d6a 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -3,7 +3,7 @@ import Combine public protocol NetworkInteracting { var socketConnectionStatusPublisher: AnyPublisher { get } - var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { get } + var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) func batchSubscribe(topics: [String]) async throws diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 7c092867b..8d0f4e58a 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -9,10 +9,10 @@ public class NetworkingInteractor: NetworkInteracting { private let rpcHistory: RPCHistory private let logger: ConsoleLogging - private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never>() + private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } @@ -71,9 +71,9 @@ public class NetworkingInteractor: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, publishedAt, derivedTopic in + .compactMap { topic, rpcRequest, rawRequest, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt, derivedTopic: derivedTopic) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, rawRequest: rawRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -131,19 +131,19 @@ public class NetworkingInteractor: NetworkInteracting { } private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date) { - if let (deserializedJsonRpcRequest, derivedTopic): (RPCRequest, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleRequest(topic: topic, request: deserializedJsonRpcRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) - } else if let (response, derivedTopic): (RPCResponse, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + if let (deserializedJsonRpcRequest, derivedTopic, rawData): (RPCRequest, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleRequest(topic: topic, request: deserializedJsonRpcRequest, rawRequest: rawData, publishedAt: publishedAt, derivedTopic: derivedTopic) + } else if let (response, derivedTopic, rawData): (RPCResponse, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } else { logger.debug("Networking Interactor - Received unknown object type from networking relay") } } - private func handleRequest(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?) { + private func handleRequest(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?) { do { try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote) - requestPublisherSubject.send((topic, request, publishedAt, derivedTopic)) + requestPublisherSubject.send((topic, request, rawRequest, publishedAt, derivedTopic)) } catch { logger.debug(error) } diff --git a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift index 1f34ceec0..fe1e2c2da 100644 --- a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift @@ -4,13 +4,15 @@ public struct RequestSubscriptionPayload: Codable, Subscriptio public let id: RPCID public let topic: String public let request: Request + public let rawRequest: String? public let publishedAt: Date public let derivedTopic: String? - public init(id: RPCID, topic: String, request: Request, publishedAt: Date, derivedTopic: String?) { + public init(id: RPCID, topic: String, request: Request, rawRequest: String?, publishedAt: Date, derivedTopic: String?) { self.id = id self.topic = topic self.request = request + self.rawRequest = rawRequest self.publishedAt = publishedAt self.derivedTopic = derivedTopic } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index 6291cd1a4..c3327e482 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -28,7 +28,7 @@ public class PairingRequestsSubscriber { .filter { [unowned self] in !pairingProtocolMethods.contains($0.request.method)} .filter { [unowned self] in pairingStorage.hasPairing(forTopic: $0.topic)} .filter { [unowned self] in !registeredProtocolMethods.contains($0.request.method)} - .sink { [unowned self] topic, request, _, _ in + .sink { [unowned self] topic, request, _, _, _ in Task(priority: .high) { let protocolMethod = UnsupportedProtocolMethod(method: request.method) logger.debug("PairingRequestsSubscriber: responding unregistered request method") diff --git a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift index 91248a150..177f4bb76 100644 --- a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift +++ b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift @@ -17,7 +17,7 @@ public class PushDecryptionService { } public func decryptMessage(topic: String, ciphertext: String) throws -> PushMessage { - let (rpcRequest, _): (RPCRequest, String?) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + let (rpcRequest, _, _): (RPCRequest, String?, String?) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) guard let params = rpcRequest.params else { throw Errors.malformedPushMessage } return try params.get(PushMessage.self) } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index a00b62c00..3bb035c63 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -1,6 +1,8 @@ import Foundation import Combine +import WalletConnectVerify + final class ApproveEngine { enum Errors: Error { case wrongRequestParams @@ -11,7 +13,7 @@ final class ApproveEngine { case agreementMissingOrInvalid } - var onSessionProposal: ((Session.Proposal) -> Void)? + var onSessionProposal: ((Session.Proposal, Session.Context?) -> Void)? var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionSettle: ((Session) -> Void)? @@ -160,7 +162,9 @@ private extension ApproveEngine { func setupRequestSubscriptions() { pairingRegisterer.register(method: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in - handleSessionProposeRequest(payload: payload) + Task { + await handleSessionProposeRequest(payload: payload) + } }.store(in: &publishers) networkingInteractor.requestSubscription(on: SessionSettleProtocolMethod()) @@ -270,14 +274,40 @@ private extension ApproveEngine { // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: RequestSubscriptionPayload) { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload) async { logger.debug("Received Session Proposal") let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: SessionProposeProtocolMethod()) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic)) + + if #available(iOS 14.0, *) { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + + let sessionContext = Session.Context( + origin: origin, + validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) + } else { + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) + } + } + + func jsonStringify(value: Any) -> String { + if JSONSerialization.isValidJSONObject(value) { + if let data = try? JSONSerialization.data(withJSONObject: value) { + if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { + return (string as String).rawRepresentation.sha256().toHexString() + } + } + } + return "" } // MARK: SessionSettleRequest @@ -336,3 +366,15 @@ private extension ApproveEngine { onSessionSettle?(session.publicRepresentation()) } } + +extension Encodable { + func asDictionary() throws -> String { + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys, .prettyPrinted] + let data = try encoder.encode(self) + if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { + return (string as String) + } + return "" + } +} diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 420c1a418..f91c5b3bc 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -1,6 +1,8 @@ import Foundation import Combine +import WalletConnectVerify + final class SessionEngine { enum Errors: Error { case sessionNotFound(topic: String) @@ -8,7 +10,7 @@ final class SessionEngine { } var onSessionsUpdate: (([Session]) -> Void)? - var onSessionRequest: ((Request) -> Void)? + var onSessionRequest: ((Request, Session.Context?) -> Void)? var onSessionResponse: ((Response) -> Void)? var onSessionRejected: ((String, SessionType.Reason) -> Void)? var onSessionDelete: ((String, SessionType.Reason) -> Void)? @@ -127,7 +129,9 @@ private extension SessionEngine { networkingInteractor.requestSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in - onSessionRequest(payload: payload) + Task(priority: .high) { + await onSessionRequest(payload: payload) + } }.store(in: &publishers) networkingInteractor.requestSubscription(on: SessionPingProtocolMethod()) @@ -210,7 +214,7 @@ private extension SessionEngine { onSessionDelete?(topic, payload.request) } - func onSessionRequest(payload: RequestSubscriptionPayload) { + func onSessionRequest(payload: RequestSubscriptionPayload) async { let protocolMethod = SessionRequestProtocolMethod() let topic = payload.topic let request = Request( @@ -236,7 +240,21 @@ private extension SessionEngine { return respondError(payload: payload, reason: .sessionRequestExpired, protocolMethod: protocolMethod) } - onSessionRequest?(request) + if #available(iOS 14.0, *) { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + + let sessionContext = Session.Context( + origin: origin, + validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onSessionRequest?(request, sessionContext) + } else { + onSessionRequest?(request, nil) + } } func onSessionPing(payload: SubscriptionPayload) { diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift index a274c8e2b..6d287f9a1 100644 --- a/Sources/WalletConnectSign/Session.swift +++ b/Sources/WalletConnectSign/Session.swift @@ -42,4 +42,15 @@ extension Session { } } + public struct Context: Equatable, Hashable { + public enum ValidationStatus { + case unknown + case valid + case invalid + } + + public let origin: String? + public let validation: ValidationStatus + public let verifyUrl: String + } } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index abf6cad91..cef509c52 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -16,14 +16,14 @@ public final class SignClient: SignClientProtocol { /// Publisher that sends session proposal /// /// event is emited on responder client only - public var sessionProposalPublisher: AnyPublisher { + public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { sessionProposalPublisherSubject.eraseToAnyPublisher() } /// Publisher that sends session request /// /// In most cases event will be emited on wallet - public var sessionRequestPublisher: AnyPublisher { + public var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { sessionRequestPublisherSubject.eraseToAnyPublisher() } @@ -113,8 +113,8 @@ public final class SignClient: SignClientProtocol { private let historyService: HistoryService private let cleanupService: SignCleanupService - private let sessionProposalPublisherSubject = PassthroughSubject() - private let sessionRequestPublisherSubject = PassthroughSubject() + private let sessionProposalPublisherSubject = PassthroughSubject<(proposal: Session.Proposal, context: Session.Context?), Never>() + private let sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: Session.Context?), Never>() private let socketConnectionStatusPublisherSubject = PassthroughSubject() private let sessionSettlePublisherSubject = PassthroughSubject() private let sessionDeletePublisherSubject = PassthroughSubject<(String, Reason), Never>() @@ -371,8 +371,8 @@ public final class SignClient: SignClientProtocol { // MARK: - Private private func setUpEnginesCallbacks() { - approveEngine.onSessionProposal = { [unowned self] proposal in - sessionProposalPublisherSubject.send(proposal) + approveEngine.onSessionProposal = { [unowned self] (proposal, context) in + sessionProposalPublisherSubject.send((proposal, context)) } approveEngine.onSessionRejected = { [unowned self] proposal, reason in sessionRejectionPublisherSubject.send((proposal, reason)) @@ -380,8 +380,8 @@ public final class SignClient: SignClientProtocol { approveEngine.onSessionSettle = { [unowned self] settledSession in sessionSettlePublisherSubject.send(settledSession) } - sessionEngine.onSessionRequest = { [unowned self] sessionRequest in - sessionRequestPublisherSubject.send(sessionRequest) + sessionEngine.onSessionRequest = { [unowned self] (sessionRequest, context) in + sessionRequestPublisherSubject.send((sessionRequest, context)) } sessionEngine.onSessionDelete = { [unowned self] topic, reason in sessionDeletePublisherSubject.send((topic, reason)) diff --git a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift index 342063400..21ce05e8a 100644 --- a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift +++ b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift @@ -2,8 +2,8 @@ import Foundation import Combine public protocol SignClientProtocol { - var sessionProposalPublisher: AnyPublisher { get } - var sessionRequestPublisher: AnyPublisher { get } + var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { get } + var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { get } var sessionsPublisher: AnyPublisher<[Session], Never> { get } var socketConnectionStatusPublisher: AnyPublisher { get } var sessionSettlePublisher: AnyPublisher { get } diff --git a/Sources/WalletConnectVerify/AssertionRegistrer.swift b/Sources/WalletConnectVerify/AssertionRegistrer.swift index 67640c1fa..b96ac6ebe 100644 --- a/Sources/WalletConnectVerify/AssertionRegistrer.swift +++ b/Sources/WalletConnectVerify/AssertionRegistrer.swift @@ -1,5 +1,26 @@ import Foundation +import WalletConnectNetworking -class AssertionRegistrer { - func registerAssertion() async throws {} +public class AssertionRegistrer { + enum Errors: Error { + case registrationFailed + } + + let verifyHost: String + + init(verifyHost: String) { + self.verifyHost = verifyHost + } + + func registerAssertion(attestationId: String) async throws -> String { + let httpClient = HTTPNetworkClient(host: verifyHost) + let response = try await httpClient.request( + VerifyResponse.self, + at: VerifyAPI.resolve(attestationId: attestationId) + ) + guard let origin = response.origin else { + throw Errors.registrationFailed + } + return origin + } } diff --git a/Sources/WalletConnectVerify/Register/VerifyResponse.swift b/Sources/WalletConnectVerify/Register/VerifyResponse.swift new file mode 100644 index 000000000..ed2e59460 --- /dev/null +++ b/Sources/WalletConnectVerify/Register/VerifyResponse.swift @@ -0,0 +1,5 @@ +import Foundation + +struct VerifyResponse: Decodable { + let origin: String? +} diff --git a/Sources/WalletConnectVerify/Register/VerifyService.swift b/Sources/WalletConnectVerify/Register/VerifyService.swift new file mode 100644 index 000000000..c0009f63b --- /dev/null +++ b/Sources/WalletConnectVerify/Register/VerifyService.swift @@ -0,0 +1,34 @@ +import Foundation +import WalletConnectNetworking + +enum VerifyAPI: HTTPService { + case resolve(attestationId: String) + + var path: String { + switch self { + case .resolve(let attestationId): return "/attestation/\(attestationId)" + } + } + + var method: HTTPMethod { + switch self { + case .resolve: return .get + } + } + + var body: Data? { + nil + } + + var queryParameters: [String: String]? { + return nil + } + + var scheme: String { + return "https" + } + + var additionalHeaderFields: [String : String]? { + nil + } +} diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift index aa6919af4..6edf632c8 100644 --- a/Sources/WalletConnectVerify/VerifyClient.swift +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -1,6 +1,7 @@ import DeviceCheck import Foundation import WalletConnectUtils +import WalletConnectNetworking @available(iOS 14.0, *) @available(macOS 11.0, *) @@ -14,7 +15,8 @@ public actor VerifyClient { init(originVerifier: OriginVerifier, assertionRegistrer: AssertionRegistrer, - appAttestationRegistrer: AppAttestationRegistrer) throws { + appAttestationRegistrer: AppAttestationRegistrer + ) throws { if !DCAppAttestService.shared.isSupported { throw Errors.attestationNotSupported } @@ -31,8 +33,7 @@ public actor VerifyClient { try await originVerifier.verifyOrigin() } - public func registerAssertion() async throws { - try await assertionRegistrer.registerAssertion() + public func registerAssertion(attestationId: String) async throws -> String { + return try await assertionRegistrer.registerAssertion(attestationId: attestationId) } - } diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index 7554a3189..b759e3cf3 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -5,20 +5,26 @@ import WalletConnectUtils @available(macOS 11.0, *) public class VerifyClientFactory { - public static func create() throws -> VerifyClient { + public static func create(verifyHost: String) throws -> VerifyClient { let originVerifier = OriginVerifier() - let assertionRegistrer = AssertionRegistrer() + let assertionRegistrer = AssertionRegistrer(verifyHost: verifyHost) let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keyIdStorage = CodableStore(defaults: keyValueStorage, identifier: VerifyStorageIdentifier.keyId) let attestKeyGenerator = AttestKeyGenerator(logger: logger, keyIdStorage: keyIdStorage) let attestChallengeProvider = AttestChallengeProvider() let keyAttestationService = KeyAttestationService() - let appAttestationRegistrer = AppAttestationRegistrer(logger: logger, - keyIdStorage: keyIdStorage, - attestKeyGenerator: attestKeyGenerator, - attestChallengeProvider: attestChallengeProvider, - keyAttestationService: keyAttestationService) - return try VerifyClient(originVerifier: originVerifier, assertionRegistrer: assertionRegistrer, appAttestationRegistrer: appAttestationRegistrer) + let appAttestationRegistrer = AppAttestationRegistrer( + logger: logger, + keyIdStorage: keyIdStorage, + attestKeyGenerator: attestKeyGenerator, + attestChallengeProvider: attestChallengeProvider, + keyAttestationService: keyAttestationService + ) + return try VerifyClient( + originVerifier: originVerifier, + assertionRegistrer: assertionRegistrer, + appAttestationRegistrer: appAttestationRegistrer + ) } } diff --git a/Sources/Web3Wallet/Web3WalletClient.swift b/Sources/Web3Wallet/Web3WalletClient.swift index 58cb53ce6..f67d8ec58 100644 --- a/Sources/Web3Wallet/Web3WalletClient.swift +++ b/Sources/Web3Wallet/Web3WalletClient.swift @@ -12,21 +12,21 @@ public class Web3WalletClient { /// Publisher that sends session proposal /// /// event is emited on responder client only - public var sessionProposalPublisher: AnyPublisher { + public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { signClient.sessionProposalPublisher.eraseToAnyPublisher() } /// Publisher that sends session request /// /// In most cases event will be emited on wallet - public var sessionRequestPublisher: AnyPublisher { + public var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { signClient.sessionRequestPublisher.eraseToAnyPublisher() } /// Publisher that sends authentication requests /// /// Wallet should subscribe on events in order to receive auth requests. - public var authRequestPublisher: AnyPublisher { + public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { authClient.authRequestPublisher.eraseToAnyPublisher() } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 4cb6e3c68..5004df10c 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -41,7 +41,7 @@ class WalletRequestSubscriberTests: XCTestCase { messageExpectation.fulfill() } - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), publishedAt: Date(), derivedTopic: nil) + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil) pairingRegisterer.subject.send(payload) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index ed6edbd63..4d808e791 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -49,7 +49,7 @@ public class NetworkingInteractorMock: NetworkInteracting { } .compactMap { topic, rpcRequest, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt, derivedTopic: derivedTopic) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, rpcRequest: rpcRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } diff --git a/Tests/VerifyTests/AppAttestationRegistrerTests.swift b/Tests/VerifyTests/AppAttestationRegistrerTests.swift index ceab96b01..fb989e664 100644 --- a/Tests/VerifyTests/AppAttestationRegistrerTests.swift +++ b/Tests/VerifyTests/AppAttestationRegistrerTests.swift @@ -3,6 +3,7 @@ import XCTest import WalletConnectUtils import TestingUtils @testable import WalletConnectVerify +@testable import WalletConnectSign @available(iOS 14.0, *) @available(macOS 11.0, *) @@ -29,6 +30,50 @@ class AppAttestationRegistrerTests: XCTestCase { keyAttestationService: keyAttestationService) } + func testHash() { + let metadata = AppMetadata( + name: "React App", + description: "React App for WalletConnect", + url: "https://react-app.walletconnect.com", + icons: ["https://avatars.githubusercontent.com/u/37784886"] + ) + let proposer = Participant( + publicKey: "582554302bfc374c5008315526b8d533f3cffd032b50ff487b537f7e3009f13d", + metadata: metadata + ) + let requiredNamespaces: [String: ProposalNamespace] = [ + "eip155": ProposalNamespace( + chains: [Blockchain("eip155:1")!], + methods: [ + "eth_sendTransaction", + "eth_signTransaction", + "eth_sign", + "personal_sign", + "eth_signTypedData" + ], events: [ + "chainChanged", + "accountsChanged" + ] + ) + ] +// let request = SessionType.ProposeParams( +// requiredNamespaces: requiredNamespaces, +// optionalNamespaces: [:], +// relays: [RelayProtocolOptions(protocol: "irn", data: nil)], +// proposer: proposer, +// sessionProperties: nil +// ) +// let payload = RPCRequest(method: "wc_sessionPropose", params: request, id: 1681824460577019) + + let string = """ +{"id":1681835052048874,"jsonrpc":"2.0","method":"wc_sessionPropose","params":{"requiredNamespaces":{"eip155":{"methods":["eth_sendTransaction","eth_signTransaction","eth_sign","personal_sign","eth_signTypedData"],"chains":["eip155:1"],"events":["chainChanged","accountsChanged"]}},"optionalNamespaces":{},"relays":[{"protocol":"irn"}],"proposer":{"publicKey":"9644bb921f5628ec3325b4027229976172a4ab71043fe0e1174acfa237f0592b","metadata":{"description":"React App for WalletConnect","url":"http://localhost:3000","icons":["https://avatars.githubusercontent.com/u/37784886"],"name":"React App"}}}} +""" + let sha256 = try! string.rawRepresentation.sha256().toHexString() + print(sha256) + XCTAssertEqual("c52ef2f630a172c4a3ae7ef5750b7662a904273fc81d1e892c5dd0c508c09583", sha256) + } + + func testAttestation() async { try! await sut.registerAttestationIfNeeded() XCTAssertTrue(attestKeyGenerator.keysGenerated) @@ -44,3 +89,15 @@ class AppAttestationRegistrerTests: XCTestCase { XCTAssertFalse(keyAttestationService.keyAttested) } } + +extension Encodable { + func asDictionary() throws -> String { + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys , .prettyPrinted] + let data = try encoder.encode(self) + if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { + return (string as String) + } + return "" + } +} diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 3bcc34e01..1b4078813 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -60,7 +60,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -84,7 +84,7 @@ final class ApproveEngineTests: XCTestCase { sessionProposed = true } - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } From 1d36435e4233df98004cbce01edee4b7ae00a222 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Apr 2023 10:06:23 +0200 Subject: [PATCH 28/89] savepoint --- .../PushSubscribeResponseSubscriber.swift | 13 +++++++++++-- .../Client/Wallet/WebDidResolver.swift | 2 +- .../WalletConnectPush/Extensions/Publisher.swift | 12 ++++++++++++ .../Types/NotificationConfig.swift | 9 +++++++++ .../WalletConnectPush/Types/NotificationType.swift | 7 +++++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 Sources/WalletConnectPush/Extensions/Publisher.swift create mode 100644 Sources/WalletConnectPush/Types/NotificationConfig.swift create mode 100644 Sources/WalletConnectPush/Types/NotificationType.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 79456577e..d8ffa0c63 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -5,6 +5,7 @@ import Combine class PushSubscribeResponseSubscriber { enum Errors: Error { case couldNotCreateSubscription + case invalidUrl } private let networkingInteractor: NetworkInteracting @@ -49,13 +50,15 @@ class PushSubscribeResponseSubscriber { let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() var account: Account! - var metadata: AppMetadata? + var metadata: AppMetadata! + var scope: Set! do { try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) + scope = try await getSubscriptionScope(dappUrl: metadata!.url) } catch { logger.debug("PushSubscribeResponseSubscriber: error: \(error)") subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) @@ -68,7 +71,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) - let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: <#Set#>) + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) @@ -80,4 +83,10 @@ class PushSubscribeResponseSubscriber { }.store(in: &publishers) } + + private func getSubscriptionScope(dappUrl: String) async throws -> Set { + guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } + let (data, _) = try await URLSession.shared.data(from: scopeUrl) + + } } diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift index f7e6617ad..65588acde 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -8,7 +8,7 @@ class WebDidResolver { } func resolveDidDoc(domainUrl: String) async throws -> WebDidDoc { - guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/did.json") else { throw Errors.invalidUrl } + guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl } let (data, _) = try await URLSession.shared.data(from: didDocUrl) return try JSONDecoder().decode(WebDidDoc.self, from: data) } diff --git a/Sources/WalletConnectPush/Extensions/Publisher.swift b/Sources/WalletConnectPush/Extensions/Publisher.swift new file mode 100644 index 000000000..52e279c0a --- /dev/null +++ b/Sources/WalletConnectPush/Extensions/Publisher.swift @@ -0,0 +1,12 @@ + +import Foundation + +extension Publisher where Self.Failure == Never { + func sink(receiveValue: @escaping ((Self.Output) async -> Void)) -> AnyCancellable { + sink { value in + Task { + await receiveValue(value) + } + } + } +} diff --git a/Sources/WalletConnectPush/Types/NotificationConfig.swift b/Sources/WalletConnectPush/Types/NotificationConfig.swift new file mode 100644 index 000000000..c53e9b674 --- /dev/null +++ b/Sources/WalletConnectPush/Types/NotificationConfig.swift @@ -0,0 +1,9 @@ + +import Foundation + +struct NotificationConfig: Codable { + let version: Int + let lastModified: TimeInterval + let types: [NotificationType] + +} diff --git a/Sources/WalletConnectPush/Types/NotificationType.swift b/Sources/WalletConnectPush/Types/NotificationType.swift new file mode 100644 index 000000000..95c6a4f70 --- /dev/null +++ b/Sources/WalletConnectPush/Types/NotificationType.swift @@ -0,0 +1,7 @@ + +import Foundation + +public struct NotificationType: Codable { + let name: NotificationScope + let description: String +} From 2abe4168cff18863d500adcabdd1dca111c26b39 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Wed, 26 Apr 2023 11:48:35 +0200 Subject: [PATCH 29/89] Refactor --- .../Wallet/WalletRequestSubscriber.swift | 26 +++++----- .../Serialiser/Serializer.swift | 20 ++++---- .../NetworkingInteractor.swift | 2 +- .../Engine/Common/ApproveEngine.swift | 47 +++++-------------- .../Engine/Common/SessionEngine.swift | 24 +++++----- .../WalletRequestSubscriberTests.swift | 8 ++-- .../NetworkingInteractorMock.swift | 9 ++-- 7 files changed, 59 insertions(+), 77 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 589065fed..dda182252 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -39,18 +39,20 @@ class WalletRequestSubscriber { let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) if #available(iOS 14.0, *) { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() - - Task(priority: .high) { - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - let authContext = AuthContext( - origin: origin, - validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onRequest?((request, authContext)) + if let rawRequest = payload.rawRequest { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + + Task(priority: .high) { + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + let authContext = AuthContext( + origin: origin, + validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onRequest?((request, authContext)) + } } } else { onRequest?((request, nil)) diff --git a/Sources/WalletConnectKMS/Serialiser/Serializer.swift b/Sources/WalletConnectKMS/Serialiser/Serializer.swift index 44b67d242..3b8b2f82d 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializer.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializer.swift @@ -45,8 +45,8 @@ public class Serializer: Serializing { switch envelope.type { case .type0: let deserialisedType: T = try handleType0Envelope(topic, envelope) - let rawData = try rawData(topic, envelope) - return (deserialisedType, nil, rawData) + let rawRequest = try rawRequest(topic, envelope) + return (deserialisedType, nil, rawRequest) case .type1(let peerPubKey): return try handleType1Envelope(topic, peerPubKey: peerPubKey, sealbox: envelope.sealbox) } @@ -60,6 +60,14 @@ public class Serializer: Serializing { } } + private func rawRequest(_ topic: String, _ envelope: Envelope) throws -> String? { + if let symmetricKey = kms.getSymmetricKeyRepresentable(for: topic) { + return try String(data: codec.decode(sealbox: envelope.sealbox, symmetricKey: symmetricKey), encoding: .utf8) + } else { + throw Errors.symmetricKeyForTopicNotFound + } + } + private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String, String?) { guard let selfPubKey = kms.getPublicKey(for: topic) else { throw Errors.publicKeyForTopicNotFound } @@ -75,12 +83,4 @@ public class Serializer: Serializing { let decryptedData = try codec.decode(sealbox: sealbox, symmetricKey: symmetricKey) return try JSONDecoder().decode(T.self, from: decryptedData) } - - private func rawData(_ topic: String, _ envelope: Envelope) throws -> String? { - if let symmetricKey = kms.getSymmetricKeyRepresentable(for: topic) { - return try String(data: codec.decode(sealbox: envelope.sealbox, symmetricKey: symmetricKey), encoding: .utf8) - } else { - throw Errors.symmetricKeyForTopicNotFound - } - } } diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 8d0f4e58a..e65911a22 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -133,7 +133,7 @@ public class NetworkingInteractor: NetworkInteracting { private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date) { if let (deserializedJsonRpcRequest, derivedTopic, rawData): (RPCRequest, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { handleRequest(topic: topic, request: deserializedJsonRpcRequest, rawRequest: rawData, publishedAt: publishedAt, derivedTopic: derivedTopic) - } else if let (response, derivedTopic, rawData): (RPCResponse, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + } else if let (response, derivedTopic, _): (RPCResponse, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } else { logger.debug("Networking Interactor - Received unknown object type from networking relay") diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 3bb035c63..1b59d4db7 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -283,32 +283,23 @@ private extension ApproveEngine { proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) if #available(iOS 14.0, *) { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - - let sessionContext = Session.Context( - origin: origin, - validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) + if let rawRequest = payload.rawRequest { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + + let sessionContext = Session.Context( + origin: origin, + validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) + } } else { onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) } } - - func jsonStringify(value: Any) -> String { - if JSONSerialization.isValidJSONObject(value) { - if let data = try? JSONSerialization.data(withJSONObject: value) { - if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { - return (string as String).rawRepresentation.sha256().toHexString() - } - } - } - return "" - } // MARK: SessionSettleRequest @@ -366,15 +357,3 @@ private extension ApproveEngine { onSessionSettle?(session.publicRepresentation()) } } - -extension Encodable { - func asDictionary() throws -> String { - let encoder = JSONEncoder() - encoder.outputFormatting = [.sortedKeys, .prettyPrinted] - let data = try encoder.encode(self) - if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { - return (string as String) - } - return "" - } -} diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index f91c5b3bc..b00140a49 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -241,17 +241,19 @@ private extension SessionEngine { } if #available(iOS 14.0, *) { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = payload.rawRequest!.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - - let sessionContext = Session.Context( - origin: origin, - validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onSessionRequest?(request, sessionContext) + if let rawRequest = payload.rawRequest { + let verifyUrl = "https://verify.walletconnect.com" + let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) + + let sessionContext = Session.Context( + origin: origin, + validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyUrl + ) + onSessionRequest?(request, sessionContext) + } } else { onSessionRequest?(request, nil) } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 5004df10c..979929ba7 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -35,13 +35,13 @@ class WalletRequestSubscriberTests: XCTestCase { var requestId: RPCID! var requestPayload: AuthPayload! - sut.onRequest = { request in - requestId = request.id - requestPayload = request.payload + sut.onRequest = { result in + requestId = result.0.id + requestPayload = result.0.payload messageExpectation.fulfill() } - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil) + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), rawRequest: "", publishedAt: Date(), derivedTopic: nil) pairingRegisterer.subject.send(payload) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 4d808e791..c669cdea3 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -6,7 +6,6 @@ import WalletConnectKMS import WalletConnectNetworking public class NetworkingInteractorMock: NetworkInteracting { - private(set) var subscriptions: [String] = [] private(set) var unsubscriptions: [String] = [] @@ -30,10 +29,10 @@ public class NetworkingInteractorMock: NetworkInteracting { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } - public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never>() + public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never>() public let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: JSONRPC.RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: JSONRPC.RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } @@ -47,9 +46,9 @@ public class NetworkingInteractorMock: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, publishedAt, derivedTopic in + .compactMap { topic, rpcRequest, rawRequest, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, rpcRequest: rpcRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, rawRequest: rawRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } From 158f9d14b5abdc24106f6ae9a2529b52b69a934c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Apr 2023 11:58:56 +0200 Subject: [PATCH 30/89] savepoint --- .../Client/Dapp/ProposalResponseSubscriber.swift | 2 +- .../wc_notifyUpdate/NotifyUpdateRequester.swift | 2 -- .../PushSubscribeResponseSubscriber.swift | 8 +++----- .../Client/Wallet/WalletPushClientFactory.swift | 5 ++++- Sources/WalletConnectPush/Extensions/Publisher.swift | 1 + 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 67e46fe5a..1e9cb58b5 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -57,7 +57,7 @@ class ProposalResponseSubscriber { guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } - let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata) + let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: <#Set#>) logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) try await networkingInteractor.subscribe(topic: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift index 54be6fe3a..9bbe90e80 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift @@ -2,7 +2,6 @@ import Foundation class NotifyUpdateRequester { - enum Errors: Error { case noSubscriptionForGivenTopic } @@ -13,7 +12,6 @@ class NotifyUpdateRequester { private let logger: ConsoleLogging private let subscriptionsStore: CodableStore - init(keyserverURL: URL, identityClient: IdentityClient, networkingInteractor: NetworkInteracting, diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index d8ffa0c63..3bb0bcb5d 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -61,6 +61,7 @@ class PushSubscribeResponseSubscriber { scope = try await getSubscriptionScope(dappUrl: metadata!.url) } catch { logger.debug("PushSubscribeResponseSubscriber: error: \(error)") + Task { try await networkingInteractor.unsubscribe(topic: pushSubscriptionTopic) } subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } @@ -77,16 +78,13 @@ class PushSubscribeResponseSubscriber { subscriptionPublisherSubject.send(.success(pushSubscription)) - logger.debug("Subscribing to push topic: \(pushSubscriptionTopic)") - - Task { try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) } - }.store(in: &publishers) } private func getSubscriptionScope(dappUrl: String) async throws -> Set { guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } let (data, _) = try await URLSession.shared.data(from: scopeUrl) - + let config = try JSONDecoder().decode(NotificationConfig.self, from: data) + return Set(config.types.map { $0.name }) } } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 758c2bd28..13bb21d82 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -61,6 +61,8 @@ public struct WalletPushClientFactory { let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore) + let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) + return WalletPushClient( logger: logger, kms: kms, @@ -75,7 +77,8 @@ public struct WalletPushClientFactory { resubscribeService: resubscribeService, pushSubscriptionsObserver: pushSubscriptionsObserver, pushSubscribeRequester: pushSubscribeRequester, - pushSubscribeResponseSubscriber: pushSubscribeResponseSubscriber + pushSubscribeResponseSubscriber: pushSubscribeResponseSubscriber, + notifyUpdateRequester: notifyUpdateRequester ) } } diff --git a/Sources/WalletConnectPush/Extensions/Publisher.swift b/Sources/WalletConnectPush/Extensions/Publisher.swift index 52e279c0a..e0a750f71 100644 --- a/Sources/WalletConnectPush/Extensions/Publisher.swift +++ b/Sources/WalletConnectPush/Extensions/Publisher.swift @@ -1,5 +1,6 @@ import Foundation +import Combine extension Publisher where Self.Failure == Never { func sink(receiveValue: @escaping ((Self.Output) async -> Void)) -> AnyCancellable { From f174bacbebcf67145c2d99316bfec9df67ed9bef Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Apr 2023 14:43:45 +0200 Subject: [PATCH 31/89] add subscription scope provider --- .../IntegrationTests/Stubs/PushMessage.swift | 2 +- .../Dapp/ProposalResponseSubscriber.swift | 2 +- .../NotifyUpdateResponseSubscriber.swift | 37 ++++----- .../wc_pushRequest/PushRequestResponder.swift | 2 +- .../PushSubscribeRequester.swift | 2 +- .../PushSubscribeResponseSubscriber.swift | 78 +++++++++---------- .../Wallet/SubscriptionScopeProvider.swift | 15 ++++ .../Wallet/WalletPushClientFactory.swift | 3 +- .../Extensions/Publisher.swift | 13 ---- 9 files changed, 76 insertions(+), 78 deletions(-) create mode 100644 Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift delete mode 100644 Sources/WalletConnectPush/Extensions/Publisher.swift diff --git a/Example/IntegrationTests/Stubs/PushMessage.swift b/Example/IntegrationTests/Stubs/PushMessage.swift index db9f5d870..18bc68107 100644 --- a/Example/IntegrationTests/Stubs/PushMessage.swift +++ b/Example/IntegrationTests/Stubs/PushMessage.swift @@ -3,6 +3,6 @@ import WalletConnectPush extension PushMessage { static func stub() -> PushMessage { - return PushMessage(title: "test_push_message", body: "", icon: "", url: "") + return PushMessage(title: "test_push_message", body: "", icon: "", url: "", type: "") } } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 1e9cb58b5..7ee903ba6 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -57,7 +57,7 @@ class ProposalResponseSubscriber { guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } - let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: <#Set#>) + let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: []) logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) try await networkingInteractor.subscribe(topic: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index e763f89f9..11613cceb 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -39,32 +39,33 @@ class NotifyUpdateResponseSubscriber { let protocolMethod = NotifyUpdateProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) .sink {[unowned self] (payload: ResponseSubscriptionPayload) in - logger.debug("Received Push Update response") + Task(priority: .high) { + logger.debug("Received Push Update response") - let subscriptionTopic = payload.topic + let subscriptionTopic = payload.topic - // force unwrap is safe because jwt has been signed by self peer - let (_, claims) = try! SubscriptionJWTPayload.decodeAndVerify(from: payload.request) - let updatedScopeString = claims.scp + // force unwrap is safe because jwt has been signed by self peer + let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) + let updatedScopeString = claims.scp - let scope = updatedScopeString - .components(separatedBy: " ") - .compactMap { NotificationScope(rawValue: $0) } + let scope = updatedScopeString + .components(separatedBy: " ") + .compactMap { NotificationScope(rawValue: $0) } - guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else { - logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist") - subscriptionPublisherSubject.send(.failure(Errors.subscriptionDoesNotExist)) - return - } - - let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope)) + guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else { + logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist") + subscriptionPublisherSubject.send(.failure(Errors.subscriptionDoesNotExist)) + return + } - subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) + let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope)) - subscriptionPublisherSubject.send(.success(updatedSubscription)) + subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) - logger.debug("Updated Subscription") + subscriptionPublisherSubject.send(.success(updatedSubscription)) + logger.debug("Updated Subscription") + } }.store(in: &publishers) } } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index c86deab5d..515aed245 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -65,7 +65,7 @@ class PushRequestResponder { let response = try createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) - let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) + let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: []) subscriptionsStore.set(pushSubscription, forKey: pushTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 94ec28388..e800ef53d 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -25,7 +25,7 @@ class PushSubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, groupKeychainStorage: KeychainStorageProtocol, - webDidResolver: WebDidResolver, + webDidResolver: WebDidResolver = WebDidResolver(), dappsMetadataStore: CodableStore ) { self.keyserverURL = keyserverURL diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 3bb0bcb5d..0e6ca66f5 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -5,7 +5,6 @@ import Combine class PushSubscribeResponseSubscriber { enum Errors: Error { case couldNotCreateSubscription - case invalidUrl } private let networkingInteractor: NetworkInteracting @@ -15,6 +14,7 @@ class PushSubscribeResponseSubscriber { private let subscriptionsStore: CodableStore private let groupKeychainStorage: KeychainStorageProtocol private let dappsMetadataStore: CodableStore + private let subscriptionScopeProvider: SubscriptionScopeProvider private var subscriptionPublisherSubject = PassthroughSubject, Never>() var subscriptionPublisher: AnyPublisher, Never> { return subscriptionPublisherSubject.eraseToAnyPublisher() @@ -25,7 +25,8 @@ class PushSubscribeResponseSubscriber { logger: ConsoleLogging, groupKeychainStorage: KeychainStorageProtocol, subscriptionsStore: CodableStore, - dappsMetadataStore: CodableStore + dappsMetadataStore: CodableStore, + subscriptionScopeProvider: SubscriptionScopeProvider = SubscriptionScopeProvider() ) { self.networkingInteractor = networkingInteractor self.kms = kms @@ -33,6 +34,7 @@ class PushSubscribeResponseSubscriber { self.groupKeychainStorage = groupKeychainStorage self.subscriptionsStore = subscriptionsStore self.dappsMetadataStore = dappsMetadataStore + self.subscriptionScopeProvider = subscriptionScopeProvider subscribeForSubscriptionResponse() } @@ -40,51 +42,45 @@ class PushSubscribeResponseSubscriber { let protocolMethod = PushSubscribeProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) .sink {[unowned self] (payload: ResponseSubscriptionPayload) in - logger.debug("Received Push Subscribe response") + Task(priority: .high) { + logger.debug("Received Push Subscribe response") - guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { - logger.debug("PushSubscribeResponseSubscriber: no symmetric key for topic \(payload.topic)") - subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) - return - } - let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() - - var account: Account! - var metadata: AppMetadata! - var scope: Set! - do { - try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) - try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) - let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) - account = try Account(DIDPKHString: claims.sub) - metadata = try dappsMetadataStore.get(key: payload.topic) - scope = try await getSubscriptionScope(dappUrl: metadata!.url) - } catch { - logger.debug("PushSubscribeResponseSubscriber: error: \(error)") - Task { try await networkingInteractor.unsubscribe(topic: pushSubscriptionTopic) } - subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) - return - } + guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { + logger.debug("PushSubscribeResponseSubscriber: no symmetric key for topic \(payload.topic)") + subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) + return + } + let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() - guard let metadata = metadata else { - logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(payload.topic)") - return - } - dappsMetadataStore.delete(forKey: payload.topic) + var account: Account! + var metadata: AppMetadata! + var scope: Set! + do { + try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) + try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) + let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) + account = try Account(DIDPKHString: claims.sub) + metadata = try dappsMetadataStore.get(key: payload.topic) + scope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) + } catch { + logger.debug("PushSubscribeResponseSubscriber: error: \(error)") + networkingInteractor.unsubscribe(topic: pushSubscriptionTopic) + subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) + return + } - let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope) + guard let metadata = metadata else { + logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(payload.topic)") + return + } + dappsMetadataStore.delete(forKey: payload.topic) - subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope) - subscriptionPublisherSubject.send(.success(pushSubscription)) + subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) + subscriptionPublisherSubject.send(.success(pushSubscription)) + } }.store(in: &publishers) } - - private func getSubscriptionScope(dappUrl: String) async throws -> Set { - guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } - let (data, _) = try await URLSession.shared.data(from: scopeUrl) - let config = try JSONDecoder().decode(NotificationConfig.self, from: data) - return Set(config.types.map { $0.name }) - } } diff --git a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift new file mode 100644 index 000000000..8020baafd --- /dev/null +++ b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift @@ -0,0 +1,15 @@ + +import Foundation + +class SubscriptionScopeProvider { + enum Errors: Error { + case invalidUrl + } + + func getSubscriptionScope(dappUrl: String) async throws -> Set { + guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } + let (data, _) = try await URLSession.shared.data(from: scopeUrl) + let config = try JSONDecoder().decode(NotificationConfig.self, from: data) + return Set(config.types.map { $0.name }) + } +} diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 13bb21d82..5de9c2c19 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -53,11 +53,10 @@ public struct WalletPushClientFactory { let resubscribeService = PushResubscribeService(networkInteractor: networkInteractor, subscriptionsStorage: subscriptionStore) let pushSubscriptionsObserver = PushSubscriptionsObserver(store: subscriptionStore) - let webDidResolver = WebDidResolver() let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.dappsMetadataStore) - let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, webDidResolver: webDidResolver, dappsMetadataStore: dappsMetadataStore) + let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, dappsMetadataStore: dappsMetadataStore) let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore) diff --git a/Sources/WalletConnectPush/Extensions/Publisher.swift b/Sources/WalletConnectPush/Extensions/Publisher.swift deleted file mode 100644 index e0a750f71..000000000 --- a/Sources/WalletConnectPush/Extensions/Publisher.swift +++ /dev/null @@ -1,13 +0,0 @@ - -import Foundation -import Combine - -extension Publisher where Self.Failure == Never { - func sink(receiveValue: @escaping ((Self.Output) async -> Void)) -> AnyCancellable { - sink { value in - Task { - await receiveValue(value) - } - } - } -} From 6166c1eb4468071db4add7e18d8b667a23b9013d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Apr 2023 14:46:13 +0200 Subject: [PATCH 32/89] fix did path --- Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift index 65588acde..f7e6617ad 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift @@ -8,7 +8,7 @@ class WebDidResolver { } func resolveDidDoc(domainUrl: String) async throws -> WebDidDoc { - guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl } + guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/did.json") else { throw Errors.invalidUrl } let (data, _) = try await URLSession.shared.data(from: didDocUrl) return try JSONDecoder().decode(WebDidDoc.self, from: data) } From d5d92510f96f333cefe38cc00e4b1aa670de2555 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Thu, 27 Apr 2023 10:22:52 +0200 Subject: [PATCH 33/89] Refactor --- Example/IntegrationTests/Auth/AuthTests.swift | 13 ++--- .../Sign/Helpers/ClientDelegate.swift | 8 +-- Sources/Auth/AuthClientFactory.swift | 5 +- .../Wallet/WalletRequestSubscriber.swift | 50 +++++++++---------- .../Engine/Common/ApproveEngine.swift | 31 ++++++------ .../Engine/Common/SessionEngine.swift | 31 ++++++------ .../Sign/SignClientFactory.swift | 7 ++- .../AppAttestationRegistrer.swift | 2 - .../AttestKeyGenerator.swift | 12 ++--- .../KeyAttestationService.swift | 10 ++-- .../WalletConnectVerify/VerifyClient.swift | 16 +++--- .../VerifyClientFactory.swift | 7 ++- .../WalletRequestSubscriberTests.swift | 3 +- .../AppAttestationRegistrerTests.swift | 40 ++------------- .../SerialiserTests.swift | 4 +- .../AppProposalServiceTests.swift | 3 +- .../ApproveEngineTests.swift | 11 ++-- ...onControllerSessionStateMachineTests.swift | 14 +++--- .../SessionEngineTests.swift | 3 +- .../Mocks/AuthClientMock.swift | 4 +- .../Mocks/SignClientMock.swift | 8 +-- 21 files changed, 124 insertions(+), 158 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 992c9f1c5..092f26404 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -7,6 +7,7 @@ import Combine @testable import Auth import WalletConnectPairing import WalletConnectNetworking +import WalletConnectVerify final class AuthTests: XCTestCase { var appPairingClient: PairingClient! @@ -83,9 +84,9 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let signerFactory = DefaultSignerFactory() let signer = MessageSignerFactory(signerFactory: signerFactory).create(projectId: InputConfig.projectId) - let payload = try! request.payload.cacaoPayload(address: walletAccount.address) + let payload = try! request.0.payload.cacaoPayload(address: walletAccount.address) let signature = try! signer.sign(payload: payload, privateKey: prvKey, type: .eip191) - try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: walletAccount) + try! await walletAuthClient.respond(requestId: request.0.id, signature: signature, from: walletAccount) } } .store(in: &publishers) @@ -120,7 +121,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) - try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: account) + try! await walletAuthClient.respond(requestId: request.0.id, signature: signature, from: account) } } .store(in: &publishers) @@ -141,7 +142,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) - try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: walletAccount) + try! await walletAuthClient.respond(requestId: request.0.id, signature: signature, from: walletAccount) } } .store(in: &publishers) @@ -161,7 +162,7 @@ final class AuthTests: XCTestCase { try! await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { - try! await walletAuthClient.reject(requestId: request.id) + try! await walletAuthClient.reject(requestId: request.0.id) } } .store(in: &publishers) @@ -184,7 +185,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" let cacaoSignature = CacaoSignature(t: .eip191, s: invalidSignature) - try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature, from: walletAccount) + try! await walletAuthClient.respond(requestId: request.0.id, signature: cacaoSignature, from: walletAccount) } } .store(in: &publishers) diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index e299d0316..e812661e2 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -33,12 +33,12 @@ class ClientDelegate { self.onConnected?() }.store(in: &publishers) - client.sessionProposalPublisher.sink { proposal in - self.onSessionProposal?(proposal) + client.sessionProposalPublisher.sink { result in + self.onSessionProposal?(result.proposal) }.store(in: &publishers) - client.sessionRequestPublisher.sink { request in - self.onSessionRequest?(request) + client.sessionRequestPublisher.sink { result in + self.onSessionRequest?(result.request) }.store(in: &publishers) client.sessionResponsePublisher.sink { response in diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 51b973a37..c5bf7031d 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -1,5 +1,7 @@ import Foundation +import WalletConnectVerify + public struct AuthClientFactory { public static func create( @@ -44,11 +46,12 @@ public struct AuthClientFactory { let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWECacaoFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider) + let verifyClient = try? VerifyClientFactory.create() 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 = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer, verifyClient: verifyClient) let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index dda182252..63cdaa6ca 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -10,6 +10,7 @@ class WalletRequestSubscriber { private var publishers = [AnyCancellable]() private let walletErrorResponder: WalletErrorResponder private let pairingRegisterer: PairingRegisterer + private let verifyClient: VerifyClient? var onRequest: (((AuthRequest, AuthContext?)) -> Void)? init( @@ -17,14 +18,17 @@ class WalletRequestSubscriber { logger: ConsoleLogging, kms: KeyManagementServiceProtocol, walletErrorResponder: WalletErrorResponder, - pairingRegisterer: PairingRegisterer) { - self.networkingInteractor = networkingInteractor - self.logger = logger - self.kms = kms - self.walletErrorResponder = walletErrorResponder - self.pairingRegisterer = pairingRegisterer - subscribeForRequest() - } + pairingRegisterer: PairingRegisterer, + verifyClient: VerifyClient? + ) { + self.networkingInteractor = networkingInteractor + self.logger = logger + self.kms = kms + self.walletErrorResponder = walletErrorResponder + self.pairingRegisterer = pairingRegisterer + self.verifyClient = verifyClient + subscribeForRequest() + } private func subscribeForRequest() { pairingRegisterer.register(method: AuthRequestProtocolMethod()) @@ -38,26 +42,20 @@ class WalletRequestSubscriber { let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) - if #available(iOS 14.0, *) { - if let rawRequest = payload.rawRequest { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - - Task(priority: .high) { - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - let authContext = AuthContext( - origin: origin, - validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onRequest?((request, authContext)) - } + + if let rawRequest = payload.rawRequest, let verifyClient { + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + + Task(priority: .high) { + let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) + let authContext = await AuthContext( + origin: origin, + validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyClient.verifyHost + ) + onRequest?((request, authContext)) } - } else { - onRequest?((request, nil)) } - }.store(in: &publishers) } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 1b59d4db7..64002b793 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -20,6 +20,7 @@ final class ApproveEngine { private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage + private let verifyClient: VerifyClient? private let proposalPayloadsStore: CodableStore> private let sessionTopicToProposal: CodableStore private let pairingRegisterer: PairingRegisterer @@ -38,7 +39,8 @@ final class ApproveEngine { kms: KeyManagementServiceProtocol, logger: ConsoleLogging, pairingStore: WCPairingStorage, - sessionStore: WCSessionStorage + sessionStore: WCSessionStorage, + verifyClient: VerifyClient? ) { self.networkingInteractor = networkingInteractor self.proposalPayloadsStore = proposalPayloadsStore @@ -49,6 +51,7 @@ final class ApproveEngine { self.logger = logger self.pairingStore = pairingStore self.sessionStore = sessionStore + self.verifyClient = verifyClient setupRequestSubscriptions() setupResponseSubscriptions() @@ -282,22 +285,16 @@ private extension ApproveEngine { } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) - if #available(iOS 14.0, *) { - if let rawRequest = payload.rawRequest { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - - let sessionContext = Session.Context( - origin: origin, - validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) - } - } else { - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) + if let rawRequest = payload.rawRequest, let verifyClient { + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) + + let sessionContext = await Session.Context( + origin: origin, + validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyClient.verifyHost + ) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) } } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index b00140a49..9be2b4574 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -19,6 +19,7 @@ final class SessionEngine { private let sessionStore: WCSessionStorage private let networkingInteractor: NetworkInteracting private let historyService: HistoryService + private let verifyClient: VerifyClient? private let kms: KeyManagementServiceProtocol private var publishers = [AnyCancellable]() private let logger: ConsoleLogging @@ -26,12 +27,14 @@ final class SessionEngine { init( networkingInteractor: NetworkInteracting, historyService: HistoryService, + verifyClient: VerifyClient?, kms: KeyManagementServiceProtocol, sessionStore: WCSessionStorage, logger: ConsoleLogging ) { self.networkingInteractor = networkingInteractor self.historyService = historyService + self.verifyClient = verifyClient self.kms = kms self.sessionStore = sessionStore self.logger = logger @@ -239,23 +242,17 @@ private extension SessionEngine { guard !request.isExpired() else { return respondError(payload: payload, reason: .sessionRequestExpired, protocolMethod: protocolMethod) } - - if #available(iOS 14.0, *) { - if let rawRequest = payload.rawRequest { - let verifyUrl = "https://verify.walletconnect.com" - let verifyClient = try? VerifyClientFactory.create(verifyHost: verifyUrl) - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient?.registerAssertion(attestationId: attestationId) - - let sessionContext = Session.Context( - origin: origin, - validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyUrl - ) - onSessionRequest?(request, sessionContext) - } - } else { - onSessionRequest?(request, nil) + + if let rawRequest = payload.rawRequest, let verifyClient { + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) + + let sessionContext = await Session.Context( + origin: origin, + validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyClient.verifyHost + ) + onSessionRequest?(request, sessionContext) } } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 16ff60a7c..fb761db8c 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -1,5 +1,7 @@ import Foundation +import WalletConnectVerify + public struct SignClientFactory { /// Initializes and returns newly created WalletConnect Client Instance @@ -25,11 +27,12 @@ public struct SignClientFactory { let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.sessions.rawValue))) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.proposals.rawValue) let historyService = HistoryService(history: rpcHistory) - let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, kms: kms, sessionStore: sessionStore, logger: logger) + let verifyClient = try? VerifyClientFactory.create() + let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let sessionTopicToProposal = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.sessionTopicToProposal.rawValue) - let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) + let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionTopicToProposal: sessionTopicToProposal, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore, verifyClient: verifyClient) let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionTopicToProposal: sessionTopicToProposal, networkInteractor: networkingClient) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) diff --git a/Sources/WalletConnectVerify/AppAttestationRegistrer.swift b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift index 8f5b918c5..84b5d5dd1 100644 --- a/Sources/WalletConnectVerify/AppAttestationRegistrer.swift +++ b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift @@ -3,8 +3,6 @@ import DeviceCheck import WalletConnectUtils import CryptoKit -@available(iOS 14.0, *) -@available(macOS 11.0, *) class AppAttestationRegistrer { private let logger: ConsoleLogging private let keyIdStorage: CodableStore diff --git a/Sources/WalletConnectVerify/AttestKeyGenerator.swift b/Sources/WalletConnectVerify/AttestKeyGenerator.swift index 1826526e0..a53b15c6d 100644 --- a/Sources/WalletConnectVerify/AttestKeyGenerator.swift +++ b/Sources/WalletConnectVerify/AttestKeyGenerator.swift @@ -6,10 +6,9 @@ protocol AttestKeyGenerating { func generateKeys() async throws -> String } -@available(iOS 14.0, *) -@available(macOS 11.0, *) class AttestKeyGenerator: AttestKeyGenerating { - private let service = DCAppAttestService.shared + // TODO - Add DCAppAttestService handling for iOS 14.0 + // private let service = DCAppAttestService.shared private let logger: ConsoleLogging private let keyIdStorage: CodableStore @@ -21,8 +20,9 @@ class AttestKeyGenerator: AttestKeyGenerating { } func generateKeys() async throws -> String { - let keyId = try await service.generateKey() - keyIdStorage.set(keyId, forKey: Constants.keyIdStorageKey) - return keyId + // TODO - Add DCAppAttestService handling for iOS 14.0 + // let keyId = try await service.generateKey() + keyIdStorage.set("keyId", forKey: Constants.keyIdStorageKey) + return "keyId" } } diff --git a/Sources/WalletConnectVerify/KeyAttestationService.swift b/Sources/WalletConnectVerify/KeyAttestationService.swift index 54109ef01..d57bfbc4f 100644 --- a/Sources/WalletConnectVerify/KeyAttestationService.swift +++ b/Sources/WalletConnectVerify/KeyAttestationService.swift @@ -5,17 +5,17 @@ protocol KeyAttesting { func attestKey(keyId: String, clientDataHash: Data) async throws } -@available(iOS 14.0, *) -@available(macOS 11.0, *) class KeyAttestationService: KeyAttesting { - private let service = DCAppAttestService.shared + // TODO - Add DCAppAttestService handling for iOS 14.0 + // private let service = DCAppAttestService.shared + // If the method, which accesses a remote Apple server, returns the serverUnavailable error, // try attestation again later with the same key. For any other error, // discard the key identifier and create a new key when you want to try again. // Otherwise, send the completion handler’s attestation object and the keyId to your server for processing. - func attestKey(keyId: String, clientDataHash: Data) async throws { + func attestKey(keyId: String, clientDataHash: Data) { - try await service.attestKey(keyId, clientDataHash: clientDataHash) + // try await service.attestKey(keyId, clientDataHash: clientDataHash) // TODO - Send the attestation object to your server for verification. handle errors } diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift index 6edf632c8..f035864fc 100644 --- a/Sources/WalletConnectVerify/VerifyClient.swift +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -3,23 +3,23 @@ import Foundation import WalletConnectUtils import WalletConnectNetworking -@available(iOS 14.0, *) -@available(macOS 11.0, *) public actor VerifyClient { enum Errors: Error { case attestationNotSupported } + + public let verifyHost: String let originVerifier: OriginVerifier let assertionRegistrer: AssertionRegistrer let appAttestationRegistrer: AppAttestationRegistrer - init(originVerifier: OriginVerifier, - assertionRegistrer: AssertionRegistrer, - appAttestationRegistrer: AppAttestationRegistrer + init( + verifyHost: String, + originVerifier: OriginVerifier, + assertionRegistrer: AssertionRegistrer, + appAttestationRegistrer: AppAttestationRegistrer ) throws { - if !DCAppAttestService.shared.isSupported { - throw Errors.attestationNotSupported - } + self.verifyHost = verifyHost self.originVerifier = originVerifier self.assertionRegistrer = assertionRegistrer self.appAttestationRegistrer = appAttestationRegistrer diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index b759e3cf3..c0128c5ed 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -1,11 +1,9 @@ import Foundation import WalletConnectUtils -@available(iOS 14.0, *) -@available(macOS 11.0, *) public class VerifyClientFactory { - - public static func create(verifyHost: String) throws -> VerifyClient { + public static func create() throws -> VerifyClient { + let verifyHost = "verify.walletconnect.com" let originVerifier = OriginVerifier() let assertionRegistrer = AssertionRegistrer(verifyHost: verifyHost) let logger = ConsoleLogger(loggingLevel: .off) @@ -22,6 +20,7 @@ public class VerifyClientFactory { keyAttestationService: keyAttestationService ) return try VerifyClient( + verifyHost: verifyHost, originVerifier: originVerifier, assertionRegistrer: assertionRegistrer, appAttestationRegistrer: appAttestationRegistrer diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 979929ba7..3e703a6db 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -24,7 +24,8 @@ class WalletRequestSubscriberTests: XCTestCase { logger: ConsoleLoggerMock(), kms: KeyManagementServiceMock(), walletErrorResponder: walletErrorResponder, - pairingRegisterer: pairingRegisterer) + pairingRegisterer: pairingRegisterer, + verifyClient: nil) } func testSubscribeRequest() { diff --git a/Tests/VerifyTests/AppAttestationRegistrerTests.swift b/Tests/VerifyTests/AppAttestationRegistrerTests.swift index fb989e664..11c3a41a3 100644 --- a/Tests/VerifyTests/AppAttestationRegistrerTests.swift +++ b/Tests/VerifyTests/AppAttestationRegistrerTests.swift @@ -31,44 +31,10 @@ class AppAttestationRegistrerTests: XCTestCase { } func testHash() { - let metadata = AppMetadata( - name: "React App", - description: "React App for WalletConnect", - url: "https://react-app.walletconnect.com", - icons: ["https://avatars.githubusercontent.com/u/37784886"] - ) - let proposer = Participant( - publicKey: "582554302bfc374c5008315526b8d533f3cffd032b50ff487b537f7e3009f13d", - metadata: metadata - ) - let requiredNamespaces: [String: ProposalNamespace] = [ - "eip155": ProposalNamespace( - chains: [Blockchain("eip155:1")!], - methods: [ - "eth_sendTransaction", - "eth_signTransaction", - "eth_sign", - "personal_sign", - "eth_signTypedData" - ], events: [ - "chainChanged", - "accountsChanged" - ] - ) - ] -// let request = SessionType.ProposeParams( -// requiredNamespaces: requiredNamespaces, -// optionalNamespaces: [:], -// relays: [RelayProtocolOptions(protocol: "irn", data: nil)], -// proposer: proposer, -// sessionProperties: nil -// ) -// let payload = RPCRequest(method: "wc_sessionPropose", params: request, id: 1681824460577019) - let string = """ -{"id":1681835052048874,"jsonrpc":"2.0","method":"wc_sessionPropose","params":{"requiredNamespaces":{"eip155":{"methods":["eth_sendTransaction","eth_signTransaction","eth_sign","personal_sign","eth_signTypedData"],"chains":["eip155:1"],"events":["chainChanged","accountsChanged"]}},"optionalNamespaces":{},"relays":[{"protocol":"irn"}],"proposer":{"publicKey":"9644bb921f5628ec3325b4027229976172a4ab71043fe0e1174acfa237f0592b","metadata":{"description":"React App for WalletConnect","url":"http://localhost:3000","icons":["https://avatars.githubusercontent.com/u/37784886"],"name":"React App"}}}} -""" - let sha256 = try! string.rawRepresentation.sha256().toHexString() + {"id":1681835052048874,"jsonrpc":"2.0","method":"wc_sessionPropose","params":{"requiredNamespaces":{"eip155":{"methods":["eth_sendTransaction","eth_signTransaction","eth_sign","personal_sign","eth_signTypedData"],"chains":["eip155:1"],"events":["chainChanged","accountsChanged"]}},"optionalNamespaces":{},"relays":[{"protocol":"irn"}],"proposer":{"publicKey":"9644bb921f5628ec3325b4027229976172a4ab71043fe0e1174acfa237f0592b","metadata":{"description":"React App for WalletConnect","url":"http://localhost:3000","icons":["https://avatars.githubusercontent.com/u/37784886"],"name":"React App"}}}} + """ + let sha256 = try string.rawRepresentation.sha256().toHexString() print(sha256) XCTAssertEqual("c52ef2f630a172c4a3ae7ef5750b7662a904273fc81d1e892c5dd0c508c09583", sha256) } diff --git a/Tests/WalletConnectKMSTests/SerialiserTests.swift b/Tests/WalletConnectKMSTests/SerialiserTests.swift index 02822ff25..cc3bca7bf 100644 --- a/Tests/WalletConnectKMSTests/SerialiserTests.swift +++ b/Tests/WalletConnectKMSTests/SerialiserTests.swift @@ -23,7 +23,7 @@ final class SerializerTests: XCTestCase { _ = try! myKms.createSymmetricKey(topic) let messageToSerialize = "todo - change for request object" let serializedMessage = try! mySerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type0) - let (deserializedMessage, _): (String, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! + let (deserializedMessage, _, _): (String, String?, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } @@ -39,7 +39,7 @@ final class SerializerTests: XCTestCase { let serializedMessage = try! peerSerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type1(pubKey: peerPubKey.rawRepresentation)) print(agreementKeys.sharedKey.hexRepresentation) // -----------Me Deserialising ------------------- - let (deserializedMessage, _): (String, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! + let (deserializedMessage, _, _): (String, String?, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } } diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift index 27a48066d..670b253e4 100644 --- a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift +++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift @@ -68,7 +68,8 @@ final class AppProposalServiceTests: XCTestCase { kms: cryptoMock, logger: logger, pairingStore: storageMock, - sessionStore: WCSessionStorageMock() + sessionStore: WCSessionStorageMock(), + verifyClient: nil ) } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 1b4078813..72ad88ad6 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -40,7 +40,8 @@ final class ApproveEngineTests: XCTestCase { kms: cryptoMock, logger: ConsoleLoggerMock(), pairingStore: pairingStorageMock, - sessionStore: sessionStorageMock + sessionStore: sessionStorageMock, + verifyClient: nil ) } @@ -60,7 +61,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: "", publishedAt: Date(), derivedTopic: nil)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -80,11 +81,11 @@ final class ApproveEngineTests: XCTestCase { let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - engine.onSessionProposal = { _ in + engine.onSessionProposal = { _, _ in sessionProposed = true } - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rpcRequest: RPCRequest(method: "", id: ""), publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: "", publishedAt: Date(), derivedTopic: nil)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } @@ -108,7 +109,7 @@ final class ApproveEngineTests: XCTestCase { didCallBackOnSessionApproved = true } sessionTopicToProposal.set(SessionProposal.stub().publicRepresentation(pairingTopic: ""), forKey: sessionTopic) - networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), "", Date(), "")) usleep(100) diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index ceca38ac3..e097d2cb0 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -35,7 +35,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { didCallbackUpdatMethods = true XCTAssertEqual(topic, session.topic) } - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) XCTAssertTrue(didCallbackUpdatMethods) usleep(100) XCTAssertTrue(networkingInteractor.didRespondSuccess) @@ -51,7 +51,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { // } func testUpdateMethodPeerErrorSessionNotFound() { - networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), Date(), nil)) + networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) @@ -60,7 +60,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { func testUpdateMethodPeerErrorUnauthorized() { let session = WCSession.stub(isSelfController: true) // Peer is not a controller storageMock.setSession(session) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) @@ -74,7 +74,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), "", Date(), nil)) let extendedSession = storageMock.getAll().first {$0.topic == session.topic}! print(extendedSession.expiryDate) @@ -87,7 +87,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), "", Date(), nil)) let potentiallyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentiallyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended for peer non controller request ") @@ -98,7 +98,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow) storageMock.setSession(session) let tenDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), "", Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to high") @@ -110,7 +110,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let oneDayFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), "", Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to low") } diff --git a/Tests/WalletConnectSignTests/SessionEngineTests.swift b/Tests/WalletConnectSignTests/SessionEngineTests.swift index c5a2d36e6..b6ce1b40e 100644 --- a/Tests/WalletConnectSignTests/SessionEngineTests.swift +++ b/Tests/WalletConnectSignTests/SessionEngineTests.swift @@ -22,6 +22,7 @@ final class SessionEngineTests: XCTestCase { ) ) ), + verifyClient: nil, kms: KeyManagementServiceMock(), sessionStore: sessionStorage, logger: ConsoleLoggerMock() @@ -47,7 +48,7 @@ final class SessionEngineTests: XCTestCase { expiry: UInt64(Date().timeIntervalSince1970) ) - networkingInteractor.requestPublisherSubject.send(("topic", request, Date(), nil)) + networkingInteractor.requestPublisherSubject.send(("topic", request, "request", Date(), "")) wait(for: [expectation], timeout: 0.5) } diff --git a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift index 4f3e50c39..d55a25968 100644 --- a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift @@ -26,8 +26,8 @@ final class AuthClientMock: AuthClientProtocol { ) } - var authRequestPublisher: AnyPublisher { - return Result.Publisher(authRequest).eraseToAnyPublisher() + var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + return Result.Publisher((authRequest, nil)).eraseToAnyPublisher() } func formatMessage(payload: AuthPayload, address: String) throws -> String { diff --git a/Tests/Web3WalletTests/Mocks/SignClientMock.swift b/Tests/Web3WalletTests/Mocks/SignClientMock.swift index bf0fd1f03..36bd10bec 100644 --- a/Tests/Web3WalletTests/Mocks/SignClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/SignClientMock.swift @@ -19,7 +19,7 @@ final class SignClientMock: SignClientProtocol { private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!, expiry: nil) private let response = WalletConnectSign.Response(id: RPCID(1234567890123456789), topic: "", chainId: "", result: .response(AnyCodable(any: ""))) - var sessionProposalPublisher: AnyPublisher { + var sessionProposalPublisher: AnyPublisher<(proposal: WalletConnectSign.Session.Proposal, context: WalletConnectSign.Session.Context?), Never> { let proposer = Participant(publicKey: "", metadata: metadata) let sessionProposal = WalletConnectSign.SessionProposal( relays: [], @@ -29,12 +29,12 @@ final class SignClientMock: SignClientProtocol { sessionProperties: nil ).publicRepresentation(pairingTopic: "") - return Result.Publisher(sessionProposal) + return Result.Publisher((sessionProposal, nil)) .eraseToAnyPublisher() } - var sessionRequestPublisher: AnyPublisher { - return Result.Publisher(request) + var sessionRequestPublisher: AnyPublisher<(request: WalletConnectSign.Request, context: WalletConnectSign.Session.Context?), Never> { + return Result.Publisher((request, nil)) .eraseToAnyPublisher() } From 36ba483be8c83579d270b2ef2133506ed09a69e7 Mon Sep 17 00:00:00 2001 From: Alex Lin Date: Thu, 27 Apr 2023 11:44:19 +0300 Subject: [PATCH 34/89] [W3W] make Auto Namespaces Error Public When I use AutoNamespaces's build function, I can't catch the error detail. --- Sources/WalletConnectSign/Namespace.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectSign/Namespace.swift b/Sources/WalletConnectSign/Namespace.swift index 5e398757b..43ba9518a 100644 --- a/Sources/WalletConnectSign/Namespace.swift +++ b/Sources/WalletConnectSign/Namespace.swift @@ -1,4 +1,4 @@ -enum AutoNamespacesError: Error { +public enum AutoNamespacesError: Error { case requiredChainsNotSatisfied case requiredAccountsNotSatisfied case requiredMethodsNotSatisfied From 19e1364a05de3bc56b0ea3ae5821d0237053fbe6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Apr 2023 11:18:38 +0200 Subject: [PATCH 35/89] add notify update test --- Example/IntegrationTests/Push/PushTests.swift | 23 ++++++++++++++++++- .../Dapp/ProposalResponseSubscriber.swift | 5 ++-- .../NotifyUpdateRequester.swift | 1 + .../NotifyUpdateResponseSubscriber.swift | 15 +++--------- .../wc_pushRequest/PushRequestResponder.swift | 4 +++- .../PushSubscribeResponseSubscriber.swift | 6 ++--- .../Client/Wallet/WalletPushClient.swift | 5 +++- .../Wallet/WalletPushClientFactory.swift | 5 +++- .../NotifyUpdateProtocolMethod.swift | 2 +- .../Types/PushSubscription.swift | 2 +- 10 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 66dfa5306..03f21acf5 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -237,7 +237,6 @@ final class PushTests: XCTestCase { // Push Subscribe func testWalletCreatesSubscription() async { let expectation = expectation(description: "expects to create push subscription") - let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) walletPushClient.subscriptionsPublisher.sink { subscriptions in @@ -247,6 +246,28 @@ final class PushTests: XCTestCase { await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) } + func testWalletUpdatesSubscription() async { + let expectation = expectation(description: "expects to create and update push subscription") + let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) + let updateScope: Set = [NotificationScope.alerts] + try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) + walletPushClient.subscriptionsPublisher + .first() + .sink { [unowned self] subscriptions in + Task { try! await walletPushClient.update(topic: subscriptions.first!.topic, scope: updateScope) } + } + .store(in: &publishers) + + walletPushClient.subscriptionsPublisher + .dropFirst() + .sink { + XCTAssertEqual($0.first!.scope, updateScope) + expectation.fulfill() + }.store(in: &publishers) + + await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) + } + } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 7ee903ba6..3456cecfb 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -52,12 +52,13 @@ class ProposalResponseSubscriber { private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) { let jwt = payload.response.jwtString - _ = try SubscriptionJWTPayload.decodeAndVerify(from: payload.response) + let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.response) logger.debug("subscriptionAuth JWT validated") guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } + let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: []) + let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: [], expiry: expiry) logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) try await networkingInteractor.subscribe(topic: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift index 9bbe90e80..d7bfb5dc4 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift @@ -27,6 +27,7 @@ class NotifyUpdateRequester { func update(topic: String, scope: Set) async throws { + logger.debug("NotifyUpdateRequester: updating subscription for topic: \(topic)") guard let subscription = try subscriptionsStore.get(key: topic) else { throw Errors.noSubscriptionForGivenTopic } let request = try createJWTRequest(subscriptionAccount: subscription.account, dappUrl: subscription.metadata.url, scope: scope) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 11613cceb..a785bc64a 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -8,30 +8,21 @@ class NotifyUpdateResponseSubscriber { } private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let subscriptionsStore: CodableStore - private let groupKeychainStorage: KeychainStorageProtocol - private let dappsMetadataStore: CodableStore private var subscriptionPublisherSubject = PassthroughSubject, Never>() var subscriptionPublisher: AnyPublisher, Never> { return subscriptionPublisherSubject.eraseToAnyPublisher() } init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, logger: ConsoleLogging, - groupKeychainStorage: KeychainStorageProtocol, - subscriptionsStore: CodableStore, - dappsMetadataStore: CodableStore + subscriptionsStore: CodableStore ) { self.networkingInteractor = networkingInteractor - self.kms = kms self.logger = logger - self.groupKeychainStorage = groupKeychainStorage self.subscriptionsStore = subscriptionsStore - self.dappsMetadataStore = dappsMetadataStore subscribeForUpdateResponse() } @@ -44,7 +35,6 @@ class NotifyUpdateResponseSubscriber { let subscriptionTopic = payload.topic - // force unwrap is safe because jwt has been signed by self peer let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) let updatedScopeString = claims.scp @@ -57,8 +47,9 @@ class NotifyUpdateResponseSubscriber { subscriptionPublisherSubject.send(.failure(Errors.subscriptionDoesNotExist)) return } + let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope)) + let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope), expiry: expiry) subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index 515aed245..b2c7382f1 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -65,7 +65,9 @@ class PushRequestResponder { let response = try createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) - let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: []) + // will be changed in stage 2 refactor + let expiry = Date() + let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: [], expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 0e6ca66f5..3fadd8ab0 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -55,10 +55,10 @@ class PushSubscribeResponseSubscriber { var account: Account! var metadata: AppMetadata! var scope: Set! + let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) do { try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) - let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) scope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) @@ -74,8 +74,8 @@ class PushSubscribeResponseSubscriber { return } dappsMetadataStore.delete(forKey: payload.topic) - - let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope) + let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) + let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 9907c2928..aa40d0247 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -53,6 +53,7 @@ public class WalletPushClient { private let resubscribeService: PushResubscribeService private let pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber private let notifyUpdateRequester: NotifyUpdateRequester + private let notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber init(logger: ConsoleLogging, kms: KeyManagementServiceProtocol, @@ -68,7 +69,8 @@ public class WalletPushClient { pushSubscriptionsObserver: PushSubscriptionsObserver, pushSubscribeRequester: PushSubscribeRequester, pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber, - notifyUpdateRequester: NotifyUpdateRequester + notifyUpdateRequester: NotifyUpdateRequester, + notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber ) { self.logger = logger self.pairingRegisterer = pairingRegisterer @@ -84,6 +86,7 @@ public class WalletPushClient { self.pushSubscribeRequester = pushSubscribeRequester self.pushSubscribeResponseSubscriber = pushSubscribeResponseSubscriber self.notifyUpdateRequester = notifyUpdateRequester + self.notifyUpdateResponseSubscriber = notifyUpdateResponseSubscriber setupSubscriptions() } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 5de9c2c19..e2bde4f41 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -62,6 +62,8 @@ public struct WalletPushClientFactory { let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) + let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) + return WalletPushClient( logger: logger, kms: kms, @@ -77,7 +79,8 @@ public struct WalletPushClientFactory { pushSubscriptionsObserver: pushSubscriptionsObserver, pushSubscribeRequester: pushSubscribeRequester, pushSubscribeResponseSubscriber: pushSubscribeResponseSubscriber, - notifyUpdateRequester: notifyUpdateRequester + notifyUpdateRequester: notifyUpdateRequester, + notifyUpdateResponseSubscriber: notifyUpdateResponseSubscriber ) } } diff --git a/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift index b95a67cc5..198ec859f 100644 --- a/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift +++ b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift @@ -2,7 +2,7 @@ import Foundation struct NotifyUpdateProtocolMethod: ProtocolMethod { - let method: String = "wc_notifyUpdate" + let method: String = "wc_pushUpdate" let requestConfig: RelayConfig = RelayConfig(tag: 4008, prompt: true, ttl: 86400) diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index e3c640ee8..7ff5ba604 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -8,5 +8,5 @@ public struct PushSubscription: Codable, Equatable { public let relay: RelayProtocolOptions public let metadata: AppMetadata public let scope: Set - // Add expiry? + public let expiry: Date } From 3930fb94bf7d614dde2f533644b883599bf3ba59 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Apr 2023 12:23:32 +0200 Subject: [PATCH 36/89] Add scope map to subscryption --- Example/IntegrationTests/Push/PushTests.swift | 3 ++- .../Dapp/ProposalResponseSubscriber.swift | 2 +- .../NotifyUpdateResponseSubscriber.swift | 22 ++++++++++++++----- .../wc_pushRequest/PushRequestResponder.swift | 2 +- .../PushSubscribeResponseSubscriber.swift | 10 ++++++--- .../Wallet/SubscriptionScopeProvider.swift | 9 +++++++- .../Wallet/WalletPushClientFactory.swift | 5 +++-- .../Types/PushSubscription.swift | 2 +- Sources/Web3Inbox/Web3InboxClient.swift | 2 +- 9 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 03f21acf5..f29d9fe1f 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -261,7 +261,8 @@ final class PushTests: XCTestCase { walletPushClient.subscriptionsPublisher .dropFirst() .sink { - XCTAssertEqual($0.first!.scope, updateScope) + let updatedScope = Set($0.first!.scope.filter{ $0.value == true }.keys) + XCTAssertEqual(updatedScope, updateScope) expectation.fulfill() }.store(in: &publishers) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 3456cecfb..e131f075b 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -58,7 +58,7 @@ class ProposalResponseSubscriber { guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: [], expiry: expiry) + let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: [:], expiry: expiry) logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) try await networkingInteractor.subscribe(topic: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index a785bc64a..09ed56393 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -11,6 +11,7 @@ class NotifyUpdateResponseSubscriber { private var publishers = [AnyCancellable]() private let logger: ConsoleLogging private let subscriptionsStore: CodableStore + private let subscriptionScopeProvider: SubscriptionScopeProvider private var subscriptionPublisherSubject = PassthroughSubject, Never>() var subscriptionPublisher: AnyPublisher, Never> { return subscriptionPublisherSubject.eraseToAnyPublisher() @@ -18,11 +19,13 @@ class NotifyUpdateResponseSubscriber { init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, + subscriptionScopeProvider: SubscriptionScopeProvider, subscriptionsStore: CodableStore ) { self.networkingInteractor = networkingInteractor self.logger = logger self.subscriptionsStore = subscriptionsStore + self.subscriptionScopeProvider = subscriptionScopeProvider subscribeForUpdateResponse() } @@ -36,11 +39,7 @@ class NotifyUpdateResponseSubscriber { let subscriptionTopic = payload.topic let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) - let updatedScopeString = claims.scp - - let scope = updatedScopeString - .components(separatedBy: " ") - .compactMap { NotificationScope(rawValue: $0) } + let scope = try await builScope(selected: claims.scp, dappUrl: claims.aud) guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else { logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist") @@ -49,7 +48,7 @@ class NotifyUpdateResponseSubscriber { } let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: Set(scope), expiry: expiry) + let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: scope, expiry: expiry) subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) @@ -59,4 +58,15 @@ class NotifyUpdateResponseSubscriber { } }.store(in: &publishers) } + + private func builScope(selected: String, dappUrl: String) async throws -> [NotificationScope: Bool] { + let selectedScope = selected + .components(separatedBy: " ") + .compactMap { NotificationScope(rawValue: $0) } + + let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: dappUrl) + return availableScope.reduce(into: [:]) { $0[$1] = selectedScope.contains($1) } + } + + // TODO: handle error response } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index b2c7382f1..48faa378b 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -67,7 +67,7 @@ class PushRequestResponder { // will be changed in stage 2 refactor let expiry = Date() - let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: [], expiry: expiry) + let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: [:], expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 3fadd8ab0..9356729fd 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -26,7 +26,7 @@ class PushSubscribeResponseSubscriber { groupKeychainStorage: KeychainStorageProtocol, subscriptionsStore: CodableStore, dappsMetadataStore: CodableStore, - subscriptionScopeProvider: SubscriptionScopeProvider = SubscriptionScopeProvider() + subscriptionScopeProvider: SubscriptionScopeProvider ) { self.networkingInteractor = networkingInteractor self.kms = kms @@ -54,14 +54,14 @@ class PushSubscribeResponseSubscriber { var account: Account! var metadata: AppMetadata! - var scope: Set! + var availableScope: Set! let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) do { try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) - scope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) + availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) } catch { logger.debug("PushSubscribeResponseSubscriber: error: \(error)") networkingInteractor.unsubscribe(topic: pushSubscriptionTopic) @@ -75,6 +75,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) + let scope: [NotificationScope: Bool] = availableScope.reduce(into: [:]) { $0[$1] = true } let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) @@ -83,4 +84,7 @@ class PushSubscribeResponseSubscriber { } }.store(in: &publishers) } + + // TODO: handle error response + } diff --git a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift index 8020baafd..a2c55c63a 100644 --- a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift +++ b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift @@ -6,10 +6,17 @@ class SubscriptionScopeProvider { case invalidUrl } + private var cache = [String: Set]() + func getSubscriptionScope(dappUrl: String) async throws -> Set { + if let availableScope = cache[dappUrl] { + return availableScope + } guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } let (data, _) = try await URLSession.shared.data(from: scopeUrl) let config = try JSONDecoder().decode(NotificationConfig.self, from: data) - return Set(config.types.map { $0.name }) + let availableScope = Set(config.types.map { $0.name }) + cache[dappUrl] = availableScope + return availableScope } } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index e2bde4f41..c0dded01e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -58,11 +58,12 @@ public struct WalletPushClientFactory { let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, dappsMetadataStore: dappsMetadataStore) - let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore) + let subscriptionScopeProvider = SubscriptionScopeProvider() + let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore, subscriptionScopeProvider: subscriptionScopeProvider) let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) - let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) + let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, subscriptionScopeProvider: subscriptionScopeProvider, subscriptionsStore: subscriptionStore) return WalletPushClient( logger: logger, diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 7ff5ba604..cbca2b60b 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -7,6 +7,6 @@ public struct PushSubscription: Codable, Equatable { public let account: Account public let relay: RelayProtocolOptions public let metadata: AppMetadata - public let scope: Set + public let scope: [NotificationScope: Bool] public let expiry: Date } diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift index 0b2af1637..730180633 100644 --- a/Sources/Web3Inbox/Web3InboxClient.swift +++ b/Sources/Web3Inbox/Web3InboxClient.swift @@ -19,7 +19,7 @@ public final class Web3InboxClient { private let chatWebviewSubscriber: WebViewRequestSubscriber private let pushWebviewSubscriber: WebViewRequestSubscriber - init( +init( webView: WKWebView, account: Account, logger: ConsoleLogging, From a565dd1981649b52afebb3673aa91a221bfc1435 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Apr 2023 12:57:19 +0200 Subject: [PATCH 37/89] remove subscribe test --- Example/IntegrationTests/Push/PushTests.swift | 14 +------------- Example/SmokeTests.xctestplan | 1 + 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index f29d9fe1f..96cd7a998 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -234,19 +234,7 @@ final class PushTests: XCTestCase { wait(for: [expectation], timeout: InputConfig.defaultTimeout) } - // Push Subscribe - func testWalletCreatesSubscription() async { - let expectation = expectation(description: "expects to create push subscription") - let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) - try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) - walletPushClient.subscriptionsPublisher.sink { subscriptions in - XCTAssertNotNil(subscriptions.first) - expectation.fulfill() - }.store(in: &publishers) - await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) - } - - func testWalletUpdatesSubscription() async { + func testWalletCreatesAndUpdatesSubscription() async { let expectation = expectation(description: "expects to create and update push subscription") let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) let updateScope: Set = [NotificationScope.alerts] diff --git a/Example/SmokeTests.xctestplan b/Example/SmokeTests.xctestplan index 3bc6a711c..17a0cedb9 100644 --- a/Example/SmokeTests.xctestplan +++ b/Example/SmokeTests.xctestplan @@ -47,6 +47,7 @@ "PushTests\/testWalletCreatesSubscription()", "PushTests\/testWalletDeletePushSubscription()", "PushTests\/testWalletRejectsPushRequest()", + "PushTests\/testWalletUpdatesSubscription()", "RegistryTests", "RelayClientEndToEndTests", "SignClientTests\/testCaip25SatisfyAllRequiredAllOptionalNamespacesSuccessful()", From fbbec8a483584096e4099c52e2cd099ad90d7ae5 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Fri, 28 Apr 2023 15:04:47 +0200 Subject: [PATCH 38/89] Update tests --- Example/Shared/Tests/InputConfig.swift | 2 +- Package.swift | 4 +-- Sources/Auth/AuthClientFactory.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 8 +++--- .../Types/AppMetadata.swift | 17 +++++++++++-- .../Engine/Common/ApproveEngine.swift | 25 ++++++++++--------- .../Engine/Common/SessionEngine.swift | 23 +++++++++-------- .../Sign/SignClientFactory.swift | 2 +- .../VerifyClientFactory.swift | 3 +-- .../WalletRequestSubscriberTests.swift | 4 +-- .../AppAttestationRegistrerTests.swift | 14 +---------- .../ApproveEngineTests.swift | 6 ++--- 12 files changed, 57 insertions(+), 53 deletions(-) diff --git a/Example/Shared/Tests/InputConfig.swift b/Example/Shared/Tests/InputConfig.swift index 0a71a0bed..df25b0fa7 100644 --- a/Example/Shared/Tests/InputConfig.swift +++ b/Example/Shared/Tests/InputConfig.swift @@ -11,7 +11,7 @@ struct InputConfig { } static var defaultTimeout: TimeInterval { - return 45 + return 5 } private static func config(for key: String) -> String? { diff --git a/Package.swift b/Package.swift index d2636a694..2669c1379 100644 --- a/Package.swift +++ b/Package.swift @@ -114,7 +114,7 @@ let package = Package( dependencies: ["WalletConnectUtils", "WalletConnectNetworking"]), .testTarget( name: "WalletConnectSignTests", - dependencies: ["WalletConnectSign", "WalletConnectUtils", "TestingUtils"]), + dependencies: ["WalletConnectSign", "WalletConnectUtils", "TestingUtils", "WalletConnectVerify"]), .testTarget( name: "Web3WalletTests", dependencies: ["Web3Wallet", "TestingUtils"]), @@ -126,7 +126,7 @@ let package = Package( dependencies: ["WalletConnectChat", "WalletConnectUtils", "TestingUtils"]), .testTarget( name: "AuthTests", - dependencies: ["Auth", "WalletConnectUtils", "TestingUtils"]), + dependencies: ["Auth", "WalletConnectUtils", "TestingUtils", "WalletConnectVerify"]), .testTarget( name: "RelayerTests", dependencies: ["WalletConnectRelay", "WalletConnectUtils", "TestingUtils"]), diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index c5bf7031d..3b4a430be 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -46,7 +46,7 @@ public struct AuthClientFactory { let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWECacaoFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider) - let verifyClient = try? VerifyClientFactory.create() + let verifyClient = try? VerifyClientFactory.create(verifyHost: metadata.verifyUrl) 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) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 63cdaa6ca..d69d9a142 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -11,7 +11,7 @@ class WalletRequestSubscriber { private let walletErrorResponder: WalletErrorResponder private let pairingRegisterer: PairingRegisterer private let verifyClient: VerifyClient? - var onRequest: (((AuthRequest, AuthContext?)) -> Void)? + var onRequest: (((request: AuthRequest, context: AuthContext?)) -> Void)? init( networkingInteractor: NetworkInteracting, @@ -42,11 +42,9 @@ class WalletRequestSubscriber { let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) - if let rawRequest = payload.rawRequest, let verifyClient { - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - Task(priority: .high) { + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) let authContext = await AuthContext( origin: origin, @@ -55,6 +53,8 @@ class WalletRequestSubscriber { ) onRequest?((request, authContext)) } + } else { + onRequest?((request, nil)) } }.store(in: &publishers) } diff --git a/Sources/WalletConnectPairing/Types/AppMetadata.swift b/Sources/WalletConnectPairing/Types/AppMetadata.swift index 5a039740d..56c72ee3f 100644 --- a/Sources/WalletConnectPairing/Types/AppMetadata.swift +++ b/Sources/WalletConnectPairing/Types/AppMetadata.swift @@ -42,8 +42,11 @@ public struct AppMetadata: Codable, Equatable { /// An array of URL strings pointing to the icon assets on the web. public let icons: [String] + + /// The URL which used by VerifyClient. + public let verifyUrl: String - /// Redirect links which could be manually used on wallet side + /// Redirect links which could be manually used on wallet side. public let redirect: Redirect? /** @@ -54,12 +57,22 @@ public struct AppMetadata: Codable, Equatable { - description: A brief textual description of the app that can be displayed to peers. - url: The URL string that identifies the official domain of the app. - icons: An array of URL strings pointing to the icon assets on the web. + - verifyUrl: The URL which used by VerifyClient. + - redirect: Redirect links which could be manually used on wallet side. */ - public init(name: String, description: String, url: String, icons: [String], redirect: Redirect? = nil) { + public init( + name: String, + description: String, + url: String, + icons: [String], + verifyUrl: String = "verify.walletconnect.com", + redirect: Redirect? = nil + ) { self.name = name self.description = description self.url = url self.icons = icons + self.verifyUrl = verifyUrl self.redirect = redirect } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 64002b793..f0ccc7bfb 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -165,9 +165,7 @@ private extension ApproveEngine { func setupRequestSubscriptions() { pairingRegisterer.register(method: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in - Task { - await handleSessionProposeRequest(payload: payload) - } + handleSessionProposeRequest(payload: payload) }.store(in: &publishers) networkingInteractor.requestSubscription(on: SessionSettleProtocolMethod()) @@ -277,7 +275,7 @@ private extension ApproveEngine { // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: RequestSubscriptionPayload) async { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload) { logger.debug("Received Session Proposal") let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { @@ -286,15 +284,18 @@ private extension ApproveEngine { proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) if let rawRequest = payload.rawRequest, let verifyClient { + Task(priority: .high) { let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) - - let sessionContext = await Session.Context( - origin: origin, - validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyClient.verifyHost - ) - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) + let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) + let sessionContext = await Session.Context( + origin: origin, + validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyClient.verifyHost + ) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) + } + } else { + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) } } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 9be2b4574..7153f093c 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -217,7 +217,7 @@ private extension SessionEngine { onSessionDelete?(topic, payload.request) } - func onSessionRequest(payload: RequestSubscriptionPayload) async { + func onSessionRequest(payload: RequestSubscriptionPayload) { let protocolMethod = SessionRequestProtocolMethod() let topic = payload.topic let request = Request( @@ -244,15 +244,18 @@ private extension SessionEngine { } if let rawRequest = payload.rawRequest, let verifyClient { - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) - - let sessionContext = await Session.Context( - origin: origin, - validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyClient.verifyHost - ) - onSessionRequest?(request, sessionContext) + Task(priority: .high) { + let attestationId = rawRequest.rawRepresentation.sha256().toHexString() + let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) + let sessionContext = await Session.Context( + origin: origin, + validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyClient.verifyHost + ) + onSessionRequest?(request, sessionContext) + } + } else { + onSessionRequest?(request, nil) } } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index fb761db8c..ecacb5e6e 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -27,7 +27,7 @@ public struct SignClientFactory { let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: SignStorageIdentifiers.sessions.rawValue))) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: SignStorageIdentifiers.proposals.rawValue) let historyService = HistoryService(history: rpcHistory) - let verifyClient = try? VerifyClientFactory.create() + let verifyClient = try? VerifyClientFactory.create(verifyHost: metadata.verifyUrl) let sessionEngine = SessionEngine(networkingInteractor: networkingClient, historyService: historyService, verifyClient: verifyClient, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index c0128c5ed..35a495673 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -2,8 +2,7 @@ import Foundation import WalletConnectUtils public class VerifyClientFactory { - public static func create() throws -> VerifyClient { - let verifyHost = "verify.walletconnect.com" + public static func create(verifyHost: String) throws -> VerifyClient { let originVerifier = OriginVerifier() let assertionRegistrer = AssertionRegistrer(verifyHost: verifyHost) let logger = ConsoleLogger(loggingLevel: .off) diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 3e703a6db..a51869620 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -37,8 +37,8 @@ class WalletRequestSubscriberTests: XCTestCase { var requestId: RPCID! var requestPayload: AuthPayload! sut.onRequest = { result in - requestId = result.0.id - requestPayload = result.0.payload + requestId = result.request.id + requestPayload = result.request.payload messageExpectation.fulfill() } diff --git a/Tests/VerifyTests/AppAttestationRegistrerTests.swift b/Tests/VerifyTests/AppAttestationRegistrerTests.swift index 11c3a41a3..d50edd182 100644 --- a/Tests/VerifyTests/AppAttestationRegistrerTests.swift +++ b/Tests/VerifyTests/AppAttestationRegistrerTests.swift @@ -34,7 +34,7 @@ class AppAttestationRegistrerTests: XCTestCase { let string = """ {"id":1681835052048874,"jsonrpc":"2.0","method":"wc_sessionPropose","params":{"requiredNamespaces":{"eip155":{"methods":["eth_sendTransaction","eth_signTransaction","eth_sign","personal_sign","eth_signTypedData"],"chains":["eip155:1"],"events":["chainChanged","accountsChanged"]}},"optionalNamespaces":{},"relays":[{"protocol":"irn"}],"proposer":{"publicKey":"9644bb921f5628ec3325b4027229976172a4ab71043fe0e1174acfa237f0592b","metadata":{"description":"React App for WalletConnect","url":"http://localhost:3000","icons":["https://avatars.githubusercontent.com/u/37784886"],"name":"React App"}}}} """ - let sha256 = try string.rawRepresentation.sha256().toHexString() + let sha256 = string.rawRepresentation.sha256().toHexString() print(sha256) XCTAssertEqual("c52ef2f630a172c4a3ae7ef5750b7662a904273fc81d1e892c5dd0c508c09583", sha256) } @@ -55,15 +55,3 @@ class AppAttestationRegistrerTests: XCTestCase { XCTAssertFalse(keyAttestationService.keyAttested) } } - -extension Encodable { - func asDictionary() throws -> String { - let encoder = JSONEncoder() - encoder.outputFormatting = [.sortedKeys , .prettyPrinted] - let data = try encoder.encode(self) - if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { - return (string as String) - } - return "" - } -} diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 72ad88ad6..6420fe82a 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -61,8 +61,8 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: "", publishedAt: Date(), derivedTopic: nil)) - + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: nil, publishedAt: Date(), derivedTopic: nil)) + try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) let topicB = networkingInteractor.subscriptions.last! @@ -85,7 +85,7 @@ final class ApproveEngineTests: XCTestCase { sessionProposed = true } - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: "", publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: nil, publishedAt: Date(), derivedTopic: nil)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } From f6a1d9c0b9d83069235938f74f52e5c480381cb1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 10:03:54 +0200 Subject: [PATCH 39/89] add response topic --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 12 ++++++------ .../PushSubscribeResponseSubscriber.swift | 2 +- .../RPCRequests/SubscribeResponseParams.swift | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index e800ef53d..ff4679751 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -49,25 +49,25 @@ class PushSubscribeRequester { let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) - let subscriptionTopic = keys.derivedTopic() + let responseTopic = keys.derivedTopic() - dappsMetadataStore.set(metadata, forKey: subscriptionTopic) + dappsMetadataStore.set(metadata, forKey: responseTopic) try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) _ = try await identityClient.register(account: account, onSign: onSign) - try kms.setAgreementSecret(keys, topic: subscriptionTopic) + try kms.setAgreementSecret(keys, topic: responseTopic) - logger.debug("setting symm key for topic \(subscriptionTopic)") + logger.debug("setting symm key for topic \(responseTopic)") let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) let protocolMethod = PushSubscribeProtocolMethod() - logger.debug("PushSubscribeRequester: subscribing to subscription topic: \(subscriptionTopic)") + logger.debug("PushSubscribeRequester: subscribing to response topic: \(responseTopic)") - try await networkingInteractor.subscribe(topic: subscriptionTopic) + try await networkingInteractor.subscribe(topic: responseTopic) try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 9356729fd..c6135be7e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -41,7 +41,7 @@ class PushSubscribeResponseSubscriber { private func subscribeForSubscriptionResponse() { let protocolMethod = PushSubscribeProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) - .sink {[unowned self] (payload: ResponseSubscriptionPayload) in + .sink {[unowned self] (payload: ResponseSubscriptionPayload) in Task(priority: .high) { logger.debug("Received Push Subscribe response") diff --git a/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift b/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift new file mode 100644 index 000000000..51e75148f --- /dev/null +++ b/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift @@ -0,0 +1,6 @@ + +import Foundation + +struct SubscribeResponseParams: Codable { + let publicKey: String +} From d35ad3ea053ed472e6fb98bc4568f05c103285bd Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 10:12:18 +0200 Subject: [PATCH 40/89] savepoint --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 10 +++++----- .../PushSubscribeResponseSubscriber.swift | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index ff4679751..ed3f740ff 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -47,17 +47,17 @@ class PushSubscribeRequester { let peerPublicKey = try await resolvePublicKey(dappUrl: metadata.url) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() - let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) + let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey) - let responseTopic = keys.derivedTopic() + let responseTopic = keysY.derivedTopic() dappsMetadataStore.set(metadata, forKey: responseTopic) - try kms.setSymmetricKey(keys.sharedKey, for: subscribeTopic) + try kms.setSymmetricKey(keysY.sharedKey, for: subscribeTopic) _ = try await identityClient.register(account: account, onSign: onSign) - try kms.setAgreementSecret(keys, topic: responseTopic) + try kms.setAgreementSecret(keysY, topic: responseTopic) logger.debug("setting symm key for topic \(responseTopic)") @@ -69,7 +69,7 @@ class PushSubscribeRequester { try await networkingInteractor.subscribe(topic: responseTopic) - try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keysY.publicKey.rawRepresentation)) } private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey { diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index c6135be7e..d7fe743ae 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -50,6 +50,12 @@ class PushSubscribeResponseSubscriber { subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } + + // get keypair Y + // generate symm key P + // subscribe to push topic + + let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() var account: Account! From df3add0691a137b80d793c62c734bc23effabd03 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 11:25:05 +0200 Subject: [PATCH 41/89] complete new subscribe --- Example/IntegrationTests/Push/PushTests.swift | 5 +++-- .../PushSubscribeRequester.swift | 2 +- .../PushSubscribeResponseSubscriber.swift | 20 ++++++++++++------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 96cd7a998..4f707d355 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -248,9 +248,10 @@ final class PushTests: XCTestCase { walletPushClient.subscriptionsPublisher .dropFirst() - .sink { - let updatedScope = Set($0.first!.scope.filter{ $0.value == true }.keys) + .sink { [unowned self] subscriptions in + let updatedScope = Set(subscriptions.first!.scope.filter{ $0.value == true }.keys) XCTAssertEqual(updatedScope, updateScope) + Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } expectation.fulfill() }.store(in: &publishers) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index ed3f740ff..b25a6f065 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -59,7 +59,7 @@ class PushSubscribeRequester { try kms.setAgreementSecret(keysY, topic: responseTopic) - logger.debug("setting symm key for topic \(responseTopic)") + logger.debug("setting symm key for response topic \(responseTopic)") let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index d7fe743ae..443f79f3b 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -45,32 +45,36 @@ class PushSubscribeResponseSubscriber { Task(priority: .high) { logger.debug("Received Push Subscribe response") - guard let pushSubscryptionKey = kms.getAgreementSecret(for: payload.topic) else { + guard let responseKeys = kms.getAgreementSecret(for: payload.topic) else { logger.debug("PushSubscribeResponseSubscriber: no symmetric key for topic \(payload.topic)") subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } // get keypair Y - // generate symm key P - // subscribe to push topic + let pubKeyY = responseKeys.publicKey + let peerPubKeyZ = payload.response.publicKey - let pushSubscriptionTopic = pushSubscryptionKey.derivedTopic() var account: Account! var metadata: AppMetadata! + var pushSubscriptionTopic: String! var availableScope: Set! let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) do { - try kms.setAgreementSecret(pushSubscryptionKey, topic: pushSubscriptionTopic) - try groupKeychainStorage.add(pushSubscryptionKey, forKey: pushSubscriptionTopic) + // generate symm key P + let agreementKeysP = try kms.performKeyAgreement(selfPublicKey: pubKeyY, peerPublicKey: peerPubKeyZ) + pushSubscriptionTopic = agreementKeysP.derivedTopic() + try kms.setAgreementSecret(agreementKeysP, topic: pushSubscriptionTopic) + try groupKeychainStorage.add(agreementKeysP, forKey: pushSubscriptionTopic) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) + logger.debug("PushSubscribeResponseSubscriber: subscribing push subscription topic: \(payload.topic)") + try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) } catch { logger.debug("PushSubscribeResponseSubscriber: error: \(error)") - networkingInteractor.unsubscribe(topic: pushSubscriptionTopic) subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription)) return } @@ -86,6 +90,8 @@ class PushSubscribeResponseSubscriber { subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) + logger.debug("PushSubscribeResponseSubscriber: unsubscribing response topic: \(payload.topic)") + networkingInteractor.unsubscribe(topic: payload.topic) subscriptionPublisherSubject.send(.success(pushSubscription)) } }.store(in: &publishers) From 1037aecde9667bec021f0050955be6ccaf358f8e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 12:12:47 +0200 Subject: [PATCH 42/89] update tests, verified with updated cast server --- Example/IntegrationTests/Push/PushTests.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 4f707d355..564878470 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -234,6 +234,21 @@ final class PushTests: XCTestCase { wait(for: [expectation], timeout: InputConfig.defaultTimeout) } + // Push Subscribe + func testWalletCreatesSubscription() async { + let expectation = expectation(description: "expects to create push subscription") + let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) + try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) + walletPushClient.subscriptionsPublisher + .first() + .sink { [unowned self] subscriptions in + XCTAssertNotNil(subscriptions.first) + Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } + expectation.fulfill() + }.store(in: &publishers) + await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) + } + func testWalletCreatesAndUpdatesSubscription() async { let expectation = expectation(description: "expects to create and update push subscription") let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) @@ -249,6 +264,7 @@ final class PushTests: XCTestCase { walletPushClient.subscriptionsPublisher .dropFirst() .sink { [unowned self] subscriptions in + guard !subscriptions.isEmpty else {return} let updatedScope = Set(subscriptions.first!.scope.filter{ $0.value == true }.keys) XCTAssertEqual(updatedScope, updateScope) Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } From dece71843d836c79655202d6791d041e80688c74 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 13:05:17 +0200 Subject: [PATCH 43/89] fix post merge build error --- Sources/Web3Inbox/WebView/WebViewFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index fd4aa3ee0..67fc5372c 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -31,7 +31,7 @@ final class WebViewFactory { let webview = WKWebView(frame: .zero, configuration: configuration) let request = URLRequest(url: URL(string: host)!) webview.load(request) - webview.uiDelegate = webviewSubscriber + webview.uiDelegate = chatWebviewSubscriber return webview } } From dfe34165f94f025edf8023a4067eb68b5baedec0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 13:40:54 +0200 Subject: [PATCH 44/89] make PushMessage backward compatible --- Sources/WalletConnectPush/Types/PushMessage.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectPush/Types/PushMessage.swift b/Sources/WalletConnectPush/Types/PushMessage.swift index 393f8b6af..d48604937 100644 --- a/Sources/WalletConnectPush/Types/PushMessage.swift +++ b/Sources/WalletConnectPush/Types/PushMessage.swift @@ -5,9 +5,9 @@ public struct PushMessage: Codable, Equatable { public let body: String public let icon: String public let url: String - public let type: String + public let type: String? - public init(title: String, body: String, icon: String, url: String, type: String) { + public init(title: String, body: String, icon: String, url: String, type: String? = nil) { self.title = title self.body = body self.icon = icon From 81f749e1aa377cd35accda8052859af15bf3856a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 2 May 2023 13:58:58 +0200 Subject: [PATCH 45/89] savepoint --- .../wc_pushSubscribe/PushSubscribeResponseSubscriber.swift | 2 +- Sources/WalletConnectPush/Types/PushSubscription.swift | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 443f79f3b..bad26b958 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -85,7 +85,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let scope: [NotificationScope: Bool] = availableScope.reduce(into: [:]) { $0[$1] = true } + let scope: [NotificationScope: Bool] = availableScope.reduce(into: [:]) { $0[$1] = ScopeValue(description: $1., enabled: true) } let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index cbca2b60b..6cbebdd18 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -3,10 +3,14 @@ import WalletConnectUtils import WalletConnectPairing public struct PushSubscription: Codable, Equatable { + public struct ScopeValue: Codable, Equatable { + let description: String + let enabled: Bool + } public let topic: String public let account: Account public let relay: RelayProtocolOptions public let metadata: AppMetadata - public let scope: [NotificationScope: Bool] + public let scope: [NotificationScope: ScopeValue] public let expiry: Date } From b7c0faa96b6c9b6a8e48cdbdd5bbc0edc1b1eabf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 4 May 2023 13:05:02 +0200 Subject: [PATCH 46/89] Add enabled flag to scope --- .../wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift | 4 ++-- .../PushSubscribeResponseSubscriber.swift | 6 ++---- .../Client/Wallet/SubscriptionScopeProvider.swift | 6 +++--- Sources/WalletConnectPush/Types/NotificationType.swift | 2 +- Sources/WalletConnectPush/Types/PushSubscription.swift | 9 +++++---- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 09ed56393..97aa27553 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -59,13 +59,13 @@ class NotifyUpdateResponseSubscriber { }.store(in: &publishers) } - private func builScope(selected: String, dappUrl: String) async throws -> [NotificationScope: Bool] { + private func builScope(selected: String, dappUrl: String) async throws -> [NotificationScope: ScopeValue] { let selectedScope = selected .components(separatedBy: " ") .compactMap { NotificationScope(rawValue: $0) } let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: dappUrl) - return availableScope.reduce(into: [:]) { $0[$1] = selectedScope.contains($1) } + return availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: selectedScope.contains($1.name)) } } // TODO: handle error response diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index bad26b958..4afb83d4a 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -55,12 +55,10 @@ class PushSubscribeResponseSubscriber { let pubKeyY = responseKeys.publicKey let peerPubKeyZ = payload.response.publicKey - - var account: Account! var metadata: AppMetadata! var pushSubscriptionTopic: String! - var availableScope: Set! + var availableScope: Set! let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) do { // generate symm key P @@ -85,7 +83,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let scope: [NotificationScope: Bool] = availableScope.reduce(into: [:]) { $0[$1] = ScopeValue(description: $1., enabled: true) } + let scope: [NotificationScope: ScopeValue] = availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) } let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift index a2c55c63a..a2140addd 100644 --- a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift +++ b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift @@ -6,16 +6,16 @@ class SubscriptionScopeProvider { case invalidUrl } - private var cache = [String: Set]() + private var cache = [String: Set]() - func getSubscriptionScope(dappUrl: String) async throws -> Set { + func getSubscriptionScope(dappUrl: String) async throws -> Set { if let availableScope = cache[dappUrl] { return availableScope } guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl } let (data, _) = try await URLSession.shared.data(from: scopeUrl) let config = try JSONDecoder().decode(NotificationConfig.self, from: data) - let availableScope = Set(config.types.map { $0.name }) + let availableScope = Set(config.types) cache[dappUrl] = availableScope return availableScope } diff --git a/Sources/WalletConnectPush/Types/NotificationType.swift b/Sources/WalletConnectPush/Types/NotificationType.swift index 95c6a4f70..0e6b2df35 100644 --- a/Sources/WalletConnectPush/Types/NotificationType.swift +++ b/Sources/WalletConnectPush/Types/NotificationType.swift @@ -1,7 +1,7 @@ import Foundation -public struct NotificationType: Codable { +public struct NotificationType: Codable, Hashable { let name: NotificationScope let description: String } diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 6cbebdd18..57522f2d8 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -3,10 +3,6 @@ import WalletConnectUtils import WalletConnectPairing public struct PushSubscription: Codable, Equatable { - public struct ScopeValue: Codable, Equatable { - let description: String - let enabled: Bool - } public let topic: String public let account: Account public let relay: RelayProtocolOptions @@ -14,3 +10,8 @@ public struct PushSubscription: Codable, Equatable { public let scope: [NotificationScope: ScopeValue] public let expiry: Date } + +public struct ScopeValue: Codable, Equatable { + let description: String + let enabled: Bool +} From 46ac8028371f6fe065b0d8531813018c97412163 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 4 May 2023 13:26:56 +0200 Subject: [PATCH 47/89] fix expiry in push update and subscribe --- Example/IntegrationTests/Push/PushTests.swift | 2 +- .../wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift | 2 +- .../wc_pushRequest/PushRequestResponder.swift | 2 +- .../ProtocolMethods/PushMessageProtocolMethod.swift | 4 ++-- .../RPCRequests/SubscriptionJWTPayload.swift | 7 +++---- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index c821fd8e7..79e005a37 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -268,7 +268,7 @@ final class PushTests: XCTestCase { .dropFirst() .sink { [unowned self] subscriptions in guard !subscriptions.isEmpty else {return} - let updatedScope = Set(subscriptions.first!.scope.filter{ $0.value == true }.keys) + let updatedScope = Set(subscriptions.first!.scope.filter{ $0.value.enabled == true }.keys) XCTAssertEqual(updatedScope, updateScope) Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } expectation.fulfill() diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 97aa27553..47299f1d5 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -47,7 +47,7 @@ class NotifyUpdateResponseSubscriber { return } let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - + let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: scope, expiry: expiry) subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index 48faa378b..5317d44ee 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -65,7 +65,7 @@ class PushRequestResponder { let response = try createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) - // will be changed in stage 2 refactor + // will be changed in stage 2 refactor, this method will depricate let expiry = Date() let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: [:], expiry: expiry) diff --git a/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift index 287b2a3bb..808817290 100644 --- a/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift +++ b/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift @@ -4,7 +4,7 @@ import WalletConnectPairing struct PushMessageProtocolMethod: ProtocolMethod { let method: String = "wc_pushMessage" - let requestConfig: RelayConfig = RelayConfig(tag: 4002, prompt: true, ttl: 86400) + let requestConfig: RelayConfig = RelayConfig(tag: 4002, prompt: true, ttl: 2592000) - let responseConfig: RelayConfig = RelayConfig(tag: 4003, prompt: true, ttl: 86400) + let responseConfig: RelayConfig = RelayConfig(tag: 4003, prompt: true, ttl: 2592000) } diff --git a/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift index e094ddd14..d45ac558b 100644 --- a/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift +++ b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift @@ -54,8 +54,8 @@ struct SubscriptionJWTPayload: JWTClaimsCodable { func encode(iss: String) throws -> Claims { return Claims( - iat: expiry(days: 1), - exp: defaultIatMilliseconds(), + iat: defaultIatMilliseconds(), + exp: expiry(days: 30), iss: iss, ksu: keyserver.absoluteString, aud: dappUrl, @@ -65,5 +65,4 @@ struct SubscriptionJWTPayload: JWTClaimsCodable { ) } } -//🦋 Wallet: [Push] PushSubscribeResponseSubscriber: no sym key for topic - 11:59:04.1430 -//add scope to sunscriptionAuth + From 358318ad3401a11c9d54cad1237b06247df0a674 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 5 May 2023 12:48:01 +0200 Subject: [PATCH 48/89] fix build issue on CI --- Example/IntegrationTests/Push/PushTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 79e005a37..24e2a1921 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -249,7 +249,7 @@ final class PushTests: XCTestCase { Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } expectation.fulfill() }.store(in: &publishers) - await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testWalletCreatesAndUpdatesSubscription() async { @@ -274,7 +274,7 @@ final class PushTests: XCTestCase { expectation.fulfill() }.store(in: &publishers) - await fulfillment(of: [expectation], timeout: InputConfig.defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } } From 0533bebb978bb1ba5e6996471951397fe3f8bc87 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 8 May 2023 08:11:17 +0200 Subject: [PATCH 49/89] remove unused group keychain --- .../wc_pushSubscribe/PushSubscribeRequester.swift | 4 ---- .../Client/Wallet/WalletPushClientFactory.swift | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index b25a6f065..49217c554 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -14,8 +14,6 @@ class PushSubscribeRequester { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementService private let logger: ConsoleLogging - // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs - private let groupKeychainStorage: KeychainStorageProtocol private let webDidResolver: WebDidResolver private let dappsMetadataStore: CodableStore @@ -24,7 +22,6 @@ class PushSubscribeRequester { identityClient: IdentityClient, logger: ConsoleLogging, kms: KeyManagementService, - groupKeychainStorage: KeychainStorageProtocol, webDidResolver: WebDidResolver = WebDidResolver(), dappsMetadataStore: CodableStore ) { @@ -33,7 +30,6 @@ class PushSubscribeRequester { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms - self.groupKeychainStorage = groupKeychainStorage self.webDidResolver = webDidResolver self.dappsMetadataStore = dappsMetadataStore } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index c0dded01e..3756647ee 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -56,7 +56,7 @@ public struct WalletPushClientFactory { let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.dappsMetadataStore) - let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, dappsMetadataStore: dappsMetadataStore) + let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, dappsMetadataStore: dappsMetadataStore) let subscriptionScopeProvider = SubscriptionScopeProvider() let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore, subscriptionScopeProvider: subscriptionScopeProvider) From 0831bb69a657a65ba93a0285ab048966605fd406 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Mon, 8 May 2023 10:27:57 +0200 Subject: [PATCH 50/89] set lower timeout time --- .github/workflows/build_artifacts.yml | 1 + .github/workflows/ci.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index c9da83f45..b51a09e9f 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -16,6 +16,7 @@ on: jobs: build: runs-on: macos-12 + timeout-minutes: 15 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 532bc731b..0cbc04dc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,7 @@ jobs: test: needs: prepare runs-on: macos-12 + timeout-minutes: 20 strategy: fail-fast: false matrix: From af3b1fe60e4a8c506b860e846f1049ee11f34c71 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 8 May 2023 10:54:26 +0200 Subject: [PATCH 51/89] expose update publisher at wallet push client --- Example/IntegrationTests/Push/PushTests.swift | 11 +++++------ .../NotifyUpdateResponseSubscriber.swift | 2 +- .../Client/Wallet/WalletPushClient.swift | 4 ++++ .../Web3Inbox/PushClientProxy/PushClientProxy.swift | 9 +++++++++ Sources/Web3Inbox/WebView/PushWebViewEvent.swift | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 24e2a1921..3be34d4f2 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -264,13 +264,12 @@ final class PushTests: XCTestCase { } .store(in: &publishers) - walletPushClient.subscriptionsPublisher - .dropFirst() - .sink { [unowned self] subscriptions in - guard !subscriptions.isEmpty else {return} - let updatedScope = Set(subscriptions.first!.scope.filter{ $0.value.enabled == true }.keys) + walletPushClient.updateSubscriptionPublisher + .sink { [unowned self] result in + guard case .success(let subscription) = result else { XCTFail(); return } + let updatedScope = Set(subscription.scope.filter{ $0.value.enabled == true }.keys) XCTAssertEqual(updatedScope, updateScope) - Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) } + Task { try! await walletPushClient.deleteSubscription(topic: subscription.topic) } expectation.fulfill() }.store(in: &publishers) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 47299f1d5..9fa6f5b99 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -13,7 +13,7 @@ class NotifyUpdateResponseSubscriber { private let subscriptionsStore: CodableStore private let subscriptionScopeProvider: SubscriptionScopeProvider private var subscriptionPublisherSubject = PassthroughSubject, Never>() - var subscriptionPublisher: AnyPublisher, Never> { + var updateSubscriptionPublisher: AnyPublisher, Never> { return subscriptionPublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index aa40d0247..1422bcc6c 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -38,6 +38,10 @@ public class WalletPushClient { deleteSubscriptionPublisherSubject.eraseToAnyPublisher() } + public var updateSubscriptionPublisher: AnyPublisher, Never> { + return notifyUpdateResponseSubscriber.updateSubscriptionPublisher + } + private let deletePushSubscriptionService: DeletePushSubscriptionService private let deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber private let pushSubscribeRequester: PushSubscribeRequester diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index 5414c34f2..f1203d75b 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -21,6 +21,10 @@ final class PushClientProxy { let params = try parse(ApproveRequest.self, params: request.params) try await client.reject(id: params.id) try await respond(request: request) + case .update: + let params = try parse(UpdateRequest.self, params: request.params) + try await client.update(topic: params.topic, scope: params.scope) + try await respond(request: request) case .reject: let params = try parse(RejectRequest.self, params: request.params) try await client.reject(id: params.id) @@ -61,6 +65,11 @@ private extension PushClientProxy { let id: RPCID } + struct UpdateRequest: Codable { + let topic: String + let scope: Set + } + struct RejectRequest: Codable { let id: RPCID } diff --git a/Sources/Web3Inbox/WebView/PushWebViewEvent.swift b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift index 59793f77b..5f898fa98 100644 --- a/Sources/Web3Inbox/WebView/PushWebViewEvent.swift +++ b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift @@ -2,6 +2,7 @@ import Foundation enum PushWebViewEvent: String { case approve + case update case reject case subscribe case getActiveSubscriptions From 3f3108d36075b9b8c0325ba34bc9c6b8b9092955 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 8 May 2023 10:56:45 +0200 Subject: [PATCH 52/89] exclude eip55 tests from smoke tests --- Example/SmokeTests.xctestplan | 1 + 1 file changed, 1 insertion(+) diff --git a/Example/SmokeTests.xctestplan b/Example/SmokeTests.xctestplan index 4f3dc854e..34685f639 100644 --- a/Example/SmokeTests.xctestplan +++ b/Example/SmokeTests.xctestplan @@ -42,6 +42,7 @@ "ChatTests\/testInvite()", "EIP1271VerifierTests", "EIP191VerifierTests", + "EIP55Tests", "ENSResolverTests", "PairingTests", "PushTests\/testDappDeletePushSubscription()", From 3ed7b6e8f4008689dabd1277dd1e928c217744d6 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Mon, 8 May 2023 11:16:06 +0200 Subject: [PATCH 53/89] remove .infinite timeout --- .github/workflows/ci.yml | 2 +- Example/IntegrationTests/Sign/SignClientTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cbc04dc1..fd3aa5788 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: test: needs: prepare runs-on: macos-12 - timeout-minutes: 20 + timeout-minutes: 15 strategy: fail-fast: false matrix: diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index bc71628cf..583cd886a 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -164,7 +164,7 @@ final class SignClientTests: XCTestCase { let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces)! try await wallet.client.pair(uri: uri) - wait(for: [expectation], timeout: .infinity) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testSessionRequest() async throws { From ad978e870e3a7f1b940ba89a5f3d676d6431530e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 8 May 2023 13:15:47 +0200 Subject: [PATCH 54/89] fix w3i push proxy --- Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index f1203d75b..d4b30fd80 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -19,7 +19,7 @@ final class PushClientProxy { switch event { case .approve: let params = try parse(ApproveRequest.self, params: request.params) - try await client.reject(id: params.id) + try await client.approve(id: params.id, onSign: onSign) try await respond(request: request) case .update: let params = try parse(UpdateRequest.self, params: request.params) From 9e5b78c50e1116cab7b1309ac117fc2e9065c267 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 9 May 2023 12:53:29 +0200 Subject: [PATCH 55/89] allow to configure web3inbox for push chat settings --- .../Web3Inbox/Web3InboxViewController.swift | 2 +- .../Web3Inbox/Web3InboxViewController.swift | 2 +- Sources/Web3Inbox/ConfigParam.swift | 8 +++++++ Sources/Web3Inbox/Web3Inbox.swift | 8 ++++--- .../Web3Inbox/Web3InboxClientFactory.swift | 21 +++++++++++++++---- .../Web3Inbox/WebView/WebViewFactory.swift | 8 +++---- 6 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 Sources/Web3Inbox/ConfigParam.swift diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index b31c33fb0..9a5a23f1e 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -18,7 +18,7 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - Web3Inbox.configure(account: importAccount.account, onSign: onSing) + Web3Inbox.configure(account: importAccount.account, config: [:], onSign: onSing) edgesForExtendedLayout = [] navigationItem.title = "Web3Inbox SDK" diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift index 1b4a16f5f..868c2f7c1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift @@ -16,7 +16,7 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let account = Account(blockchain: Blockchain("eip155:1")!, address: EthKeyStore.shared.address)! - Web3Inbox.configure(account: account, onSign: onSing) + Web3Inbox.configure(account: account, config: [.chatEnabled: false, .settingsEnabled: false], onSign: onSing) edgesForExtendedLayout = [] navigationItem.title = "Web3Inbox SDK" diff --git a/Sources/Web3Inbox/ConfigParam.swift b/Sources/Web3Inbox/ConfigParam.swift new file mode 100644 index 000000000..429be268d --- /dev/null +++ b/Sources/Web3Inbox/ConfigParam.swift @@ -0,0 +1,8 @@ + +import Foundation + +public enum ConfigParam { + case chatEnabled + case pushEnabled + case settingsEnabled +} diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index 9e8b803cf..bbfd2b89c 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -4,13 +4,14 @@ public final class Web3Inbox { /// Web3Inbox client instance public static var instance: Web3InboxClient = { - guard let account, let onSign else { + guard let account, let config = config, let onSign else { fatalError("Error - you must call Web3Inbox.configure(_:) before accessing the shared instance.") } - return Web3InboxClientFactory.create(chatClient: Chat.instance, pushClient: Push.wallet, account: account, onSign: onSign) + return Web3InboxClientFactory.create(chatClient: Chat.instance, pushClient: Push.wallet, account: account, config: config, onSign: onSign) }() private static var account: Account? + private static var config: [ConfigParam: Bool]? private static var onSign: SigningCallback? private init() { } @@ -18,8 +19,9 @@ public final class Web3Inbox { /// Web3Inbox instance config method /// - Parameters: /// - account: Web3Inbox initial account - static public func configure(account: Account, onSign: @escaping SigningCallback) { + static public func configure(account: Account, config: [ConfigParam: Bool], onSign: @escaping SigningCallback) { Web3Inbox.account = account + Web3Inbox.config = config Web3Inbox.onSign = onSign Chat.configure() } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index f5cdb3126..cacaa8402 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -7,13 +7,14 @@ final class Web3InboxClientFactory { chatClient: ChatClient, pushClient: WalletPushClient, account: Account, + config: [ConfigParam: Bool], onSign: @escaping SigningCallback ) -> Web3InboxClient { - let host = hostUrlString(account: account) + let url = buildUrl(account: account, config: config) let logger = ConsoleLogger(suffix: "📬") let chatWebviewSubscriber = WebViewRequestSubscriber(logger: logger) let pushWebviewSubscriber = WebViewRequestSubscriber(logger: logger) - let webView = WebViewFactory(host: host, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create() + let webView = WebViewFactory(url: url, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create() let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter()) let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter()) @@ -38,7 +39,19 @@ final class Web3InboxClientFactory { ) } - private static func hostUrlString(account: Account) -> String { - return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&pushProvider=ios&account=\(account.address)" + private static func buildUrl(account: Account, config: [ConfigParam: Bool]) -> URL { + var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden.vercel.app/")! + let defaultQueryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios")] + let accountQueryItem = URLQueryItem(name: "account", value: account.address) + var queryItems = [URLQueryItem]() + queryItems.append(accountQueryItem) + queryItems.append(contentsOf: defaultQueryItems) + + for param in config.filter({ $0.value == false}) { + queryItems.append(URLQueryItem(name: "\(param.key)", value: "false")) + } + urlComponents.queryItems = queryItems + print(urlComponents.url!.absoluteString) + return urlComponents.url! } } diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index 67fc5372c..da8c96adf 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -3,16 +3,16 @@ import WebKit final class WebViewFactory { - private let host: String + private let url: URL private let chatWebviewSubscriber: WebViewRequestSubscriber private let pushWebviewSubscriber: WebViewRequestSubscriber init( - host: String, + url: URL, chatWebviewSubscriber: WebViewRequestSubscriber, pushWebviewSubscriber: WebViewRequestSubscriber ) { - self.host = host + self.url = url self.chatWebviewSubscriber = chatWebviewSubscriber self.pushWebviewSubscriber = pushWebviewSubscriber } @@ -29,7 +29,7 @@ final class WebViewFactory { name: WebViewRequestSubscriber.push ) let webview = WKWebView(frame: .zero, configuration: configuration) - let request = URLRequest(url: URL(string: host)!) + let request = URLRequest(url: url) webview.load(request) webview.uiDelegate = chatWebviewSubscriber return webview From 849534675fe57d8dab5276bc26dd4f67bcb8e023 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 9 May 2023 14:33:43 +0200 Subject: [PATCH 56/89] fix subscription decoding issue --- .../WalletConnectPush/Types/NotificationScope.swift | 2 +- .../PushClientProxy/PushClientRequest.swift | 3 +++ .../PushClientRequestSubscriber.swift | 13 +++++++++++++ Sources/Web3Inbox/Web3InboxClientFactory.swift | 3 +-- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift index 4426725bb..250bdb71c 100644 --- a/Sources/WalletConnectPush/Types/NotificationScope.swift +++ b/Sources/WalletConnectPush/Types/NotificationScope.swift @@ -1,7 +1,7 @@ import Foundation -public enum NotificationScope: String, Hashable, Codable { +public enum NotificationScope: String, Hashable, Codable, CodingKeyRepresentable { case promotional case transactional case `private` diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift index bbc39b4ae..e03e3378c 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift @@ -3,6 +3,9 @@ import Foundation enum PushClientRequest: String { case pushRequest = "push_request" case pushMessage = "push_message" + case pushUpdate = "push_update" + case pushDelete = "push_delete" + case pushSubscription = "push_subscription" var method: String { return rawValue diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift index 0d14ede5e..3f01970f7 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift @@ -22,9 +22,22 @@ final class PushClientRequestSubscriber { let params = RequestPayload(id: id, account: account, metadata: metadata) handle(event: .pushRequest, params: params) }.store(in: &publishers) + client.pushMessagePublisher.sink { [unowned self] record in handle(event: .pushMessage, params: record) }.store(in: &publishers) + + client.deleteSubscriptionPublisher.sink { [unowned self] record in + handle(event: .pushDelete, params: record) + }.store(in: &publishers) + +// client.subscriptionPublisher.sink { [unowned self] record in +// handle(event: .pushSubscription, params: record) +// }.store(in: &publishers) +// +// client.updateSubscriptionPublisher.sink { [unowned self] record in +// handle(event: .pushUpdate, params: record) +// }.store(in: &publishers) } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index cacaa8402..fe3bd53b0 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -40,7 +40,7 @@ final class Web3InboxClientFactory { } private static func buildUrl(account: Account, config: [ConfigParam: Bool]) -> URL { - var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden.vercel.app/")! + var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden-git-feat-preferences-modal-walletconnect1.vercel.app/")! let defaultQueryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios")] let accountQueryItem = URLQueryItem(name: "account", value: account.address) var queryItems = [URLQueryItem]() @@ -51,7 +51,6 @@ final class Web3InboxClientFactory { queryItems.append(URLQueryItem(name: "\(param.key)", value: "false")) } urlComponents.queryItems = queryItems - print(urlComponents.url!.absoluteString) return urlComponents.url! } } From e6a3ea627f6602b7ffb929bddb44b2a92d4bfc5b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 May 2023 11:41:42 +0200 Subject: [PATCH 57/89] savepoint debugging w3i --- .../Wallet/Main/MainPresenter.swift | 3 ++- .../NotifyUpdateRequester.swift | 1 + .../wc_pushRequest/PushRequestResponder.swift | 3 ++- .../PushSubscribeResponseSubscriber.swift | 4 +-- .../Types/NotificationScope.swift | 2 +- Sources/WalletConnectRelay/RelayClient.swift | 2 +- .../PushClientProxy/PushClientProxy.swift | 1 + .../PushClientRequestSubscriber.swift | 27 ++++++++++++++----- .../Web3Inbox/Web3InboxClientFactory.swift | 6 ++--- .../Web3Inbox/WebView/WebViewFactory.swift | 3 +++ Sources/Web3Inbox/WebView/WebViewProxy.swift | 2 ++ .../WebView/WebViewRequestSubscriber.swift | 4 +-- 12 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index e41869a12..0936daa39 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -35,7 +35,8 @@ extension MainPresenter { interactor.pushRequestPublisher .receive(on: DispatchQueue.main) .sink { [weak self] request in - self?.router.present(pushRequest: request) + +// self?.router.present(pushRequest: request) }.store(in: &disposeBag) interactor.sessionProposalPublisher diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift index d7bfb5dc4..402d8084c 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift @@ -45,6 +45,7 @@ class NotifyUpdateRequester { payload: jwtPayload, account: subscriptionAccount ) + print(wrapper.subscriptionAuth) return RPCRequest(method: protocolMethod, params: wrapper) } } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index 5317d44ee..4af6dd11c 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -85,7 +85,8 @@ class PushRequestResponder { } private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse { - let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") + let scope = NotificationScope.allCases.map {$0.rawValue}.joined(separator: " ") + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scope) let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 4afb83d4a..63e8e0fa3 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -69,7 +69,7 @@ class PushSubscribeResponseSubscriber { account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) - logger.debug("PushSubscribeResponseSubscriber: subscribing push subscription topic: \(payload.topic)") + logger.debug("PushSubscribeResponseSubscriber: subscribing push subscription topic: \(pushSubscriptionTopic!)") try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) } catch { logger.debug("PushSubscribeResponseSubscriber: error: \(error)") @@ -78,7 +78,7 @@ class PushSubscribeResponseSubscriber { } guard let metadata = metadata else { - logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(payload.topic)") + logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(pushSubscriptionTopic)") return } dappsMetadataStore.delete(forKey: payload.topic) diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift index 250bdb71c..b4c74e4be 100644 --- a/Sources/WalletConnectPush/Types/NotificationScope.swift +++ b/Sources/WalletConnectPush/Types/NotificationScope.swift @@ -1,7 +1,7 @@ import Foundation -public enum NotificationScope: String, Hashable, Codable, CodingKeyRepresentable { +public enum NotificationScope: String, Hashable, Codable, CodingKeyRepresentable, CaseIterable { case promotional case transactional case `private` diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 59846a852..6831041bb 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -77,7 +77,7 @@ public final class RelayClient { keychainStorage: KeychainStorageProtocol = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk"), socketFactory: WebSocketFactory, socketConnectionType: SocketConnectionType = .automatic, - logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off) + logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug) ) { let clientIdStorage = ClientIdStorage(keychain: keychainStorage) let socketAuthenticator = SocketAuthenticator( diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index d4b30fd80..29184d935 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -35,6 +35,7 @@ final class PushClientProxy { try await respond(request: request) case .getActiveSubscriptions: let subscriptions = client.getActiveSubscriptions() + print(subscriptions) try await respond(with: subscriptions, request: request) case .getMessageHistory: let params = try parse(GetMessageHistoryRequest.self, params: request.params) diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift index 3f01970f7..050ac5212 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift @@ -31,13 +31,26 @@ final class PushClientRequestSubscriber { handle(event: .pushDelete, params: record) }.store(in: &publishers) -// client.subscriptionPublisher.sink { [unowned self] record in -// handle(event: .pushSubscription, params: record) -// }.store(in: &publishers) -// -// client.updateSubscriptionPublisher.sink { [unowned self] record in -// handle(event: .pushUpdate, params: record) -// }.store(in: &publishers) + client.subscriptionPublisher.sink { [unowned self] record in + switch record { + case .success(let subscription): + handle(event: .pushSubscription, params: subscription) + case .failure: + //TODO - handle error + break + + } + }.store(in: &publishers) + + client.updateSubscriptionPublisher.sink { [unowned self] record in + switch record { + case .success(let subscription): + handle(event: .pushSubscription, params: subscription) + case .failure: + //TODO - handle error + break + } + }.store(in: &publishers) } } diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index fe3bd53b0..25609b68d 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -11,7 +11,7 @@ final class Web3InboxClientFactory { onSign: @escaping SigningCallback ) -> Web3InboxClient { let url = buildUrl(account: account, config: config) - let logger = ConsoleLogger(suffix: "📬") + let logger = ConsoleLogger(suffix: "📬", loggingLevel: .debug) let chatWebviewSubscriber = WebViewRequestSubscriber(logger: logger) let pushWebviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(url: url, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create() @@ -27,7 +27,7 @@ final class Web3InboxClientFactory { return Web3InboxClient( webView: webView, account: account, - logger: ConsoleLogger(), + logger: logger, chatClientProxy: clientProxy, clientSubscriber: clientSubscriber, chatWebviewProxy: chatWebViewProxy, @@ -40,7 +40,7 @@ final class Web3InboxClientFactory { } private static func buildUrl(account: Account, config: [ConfigParam: Bool]) -> URL { - var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden-git-feat-preferences-modal-walletconnect1.vercel.app/")! + var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden.vercel.app/")! let defaultQueryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios")] let accountQueryItem = URLQueryItem(name: "account", value: account.address) var queryItems = [URLQueryItem]() diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index da8c96adf..5063c35e5 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -29,6 +29,9 @@ final class WebViewFactory { name: WebViewRequestSubscriber.push ) let webview = WKWebView(frame: .zero, configuration: configuration) + if #available(iOS 16.4, *) { + webview.isInspectable = true + } let request = URLRequest(url: url) webview.load(request) webview.uiDelegate = chatWebviewSubscriber diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index 435ceabea..1ed94be85 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -14,6 +14,7 @@ actor WebViewProxy { @MainActor func respond(_ response: RPCResponse) async throws { let body = try response.json() + print("resonding to w3i with \(body)") let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } @@ -21,6 +22,7 @@ actor WebViewProxy { @MainActor func request(_ request: RPCRequest) async throws { let body = try request.json() + print("requesting w3i with \(body)") let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift index acdcd9ce9..7df7480c4 100644 --- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift +++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift @@ -18,12 +18,12 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler { _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { - + logger.debug("WebViewRequestSubscriber: received request from w3i") guard let body = message.body as? String, let data = body.data(using: .utf8), let request = try? JSONDecoder().decode(RPCRequest.self, from: data) else { return } - + logger.debug("request method: \(request.method)") Task { do { try await onRequest?(request) From fad9031eab7c16b53da4a92730567b92b5674a39 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 May 2023 12:46:12 +0200 Subject: [PATCH 58/89] savepoint --- .../PresentationLayer/Wallet/Main/MainPresenter.swift | 2 +- .../ProtocolEngine/wc_pushRequest/PushRequestResponder.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index 0936daa39..208143f34 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -36,7 +36,7 @@ extension MainPresenter { .receive(on: DispatchQueue.main) .sink { [weak self] request in -// self?.router.present(pushRequest: request) + self?.router.present(pushRequest: request) }.store(in: &disposeBag) interactor.sessionProposalPublisher diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index 4af6dd11c..cc398a533 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -85,8 +85,7 @@ class PushRequestResponder { } private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse { - let scope = NotificationScope.allCases.map {$0.rawValue}.joined(separator: " ") - let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scope) + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "v1") let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount From 094466b49173becd4e6b8f951373eff464445126 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 10 May 2023 14:12:53 +0300 Subject: [PATCH 59/89] Force new profiles --- fastlane/Fastfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 76a63d5f8..b7e71fc34 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -86,7 +86,9 @@ platform :ios do git_url: "https://github.com/WalletConnect/match-swift.git", git_basic_authorization: options[:token], api_key: api_key, - include_all_certificates: true + include_all_certificates: true, + force_for_new_devices: true, + force_for_new_certificates: true ) number = latest_testflight_build_number( app_identifier: ENV["APP_IDENTIFIER"], From 8929f5e535d70eb62df8b92afda2536f72d42851 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Wed, 10 May 2023 20:42:06 +0200 Subject: [PATCH 60/89] PR fixes --- Example/Shared/Tests/InputConfig.swift | 2 +- .../Configurator/ThirdPartyConfigurator.swift | 3 +- .../AuthRequest/AuthRequestModule.swift | 3 +- .../AuthRequest/AuthRequestPresenter.swift | 2 +- .../ConnectionDetailsInteractor.swift | 2 +- .../ConnectionDetailsRouter.swift | 3 +- .../Wallet/Main/MainInteractor.swift | 5 ++-- .../Wallet/Main/MainRouter.swift | 8 +++-- .../SessionProposalModule.swift | 2 +- .../SessionProposalPresenter.swift | 2 +- .../SessionRequest/SessionRequestModule.swift | 3 +- .../SessionRequestPresenter.swift | 2 +- .../Wallet/Wallet/WalletInteractor.swift | 4 +-- .../Wallet/Wallet/WalletRouter.swift | 5 ++-- Package.swift | 2 +- Sources/Auth/Auth.swift | 3 ++ Sources/Auth/AuthClient.swift | 4 +-- Sources/Auth/AuthClientProtocol.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 25 ++++++++-------- Sources/Auth/Types/Public/AuthContext.swift | 11 ------- .../Serialiser/Serializer.swift | 27 ++++++----------- .../Serialiser/Serializing.swift | 4 +-- .../NetworkInteracting.swift | 2 +- .../NetworkingInteractor.swift | 18 +++++------ .../RequestSubscriptionPayload.swift | 6 ++-- .../Types/AppMetadata.swift | 4 +-- .../Client/Common/PushDecryptionService.swift | 4 ++- .../Engine/Common/ApproveEngine.swift | 25 ++++++++-------- .../Engine/Common/SessionEngine.swift | 30 ++++++++----------- Sources/WalletConnectSign/Session.swift | 12 -------- Sources/WalletConnectSign/Sign/Sign.swift | 4 +++ .../WalletConnectSign/Sign/SignClient.swift | 8 ++--- .../Sign/SignClientProtocol.swift | 4 +-- .../AssertionRegistrer.swift | 27 ++--------------- .../WalletConnectVerify/OriginVerifier.swift | 26 ++++++++++++++-- .../Register/VerifyService.swift | 4 +-- .../WalletConnectVerify/VerifyClient.swift | 20 +++++++++---- .../VerifyClientFactory.swift | 9 ++++-- .../WalletConnectVerify/VerifyContext.swift | 17 +++++++++++ Sources/Web3Wallet/Web3Wallet.swift | 3 ++ Sources/Web3Wallet/Web3WalletClient.swift | 8 ++--- .../WalletRequestSubscriberTests.swift | 2 +- .../NetworkingInteractorMock.swift | 8 ++--- .../SerialiserTests.swift | 4 +-- .../ApproveEngineTests.swift | 6 ++-- ...onControllerSessionStateMachineTests.swift | 14 ++++----- .../SessionEngineTests.swift | 2 +- .../Mocks/AuthClientMock.swift | 4 +-- .../Mocks/SignClientMock.swift | 5 ++-- 49 files changed, 205 insertions(+), 195 deletions(-) delete mode 100644 Sources/Auth/Types/Public/AuthContext.swift create mode 100644 Sources/WalletConnectVerify/VerifyContext.swift diff --git a/Example/Shared/Tests/InputConfig.swift b/Example/Shared/Tests/InputConfig.swift index df25b0fa7..0a71a0bed 100644 --- a/Example/Shared/Tests/InputConfig.swift +++ b/Example/Shared/Tests/InputConfig.swift @@ -11,7 +11,7 @@ struct InputConfig { } static var defaultTimeout: TimeInterval { - return 5 + return 45 } private static func config(for key: String) -> String? { diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index dec54cc78..e06467da7 100644 --- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -11,7 +11,8 @@ struct ThirdPartyConfigurator: Configurator { name: "Example Wallet", description: "wallet description", url: "example.wallet", - icons: ["https://avatars.githubusercontent.com/u/37784886"] + icons: ["https://avatars.githubusercontent.com/u/37784886"], + verifyUrl: "verify.walletconnect.com" ) Web3Wallet.configure(metadata: metadata, crypto: DefaultCryptoProvider(), environment: BuildConfiguration.shared.apnsEnvironment) diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift index dea4664ef..f5bba4ea4 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestModule.swift @@ -1,9 +1,10 @@ import SwiftUI + import Web3Wallet final class AuthRequestModule { @discardableResult - static func create(app: Application, request: AuthRequest, context: AuthContext?) -> UIViewController { + static func create(app: Application, request: AuthRequest, context: VerifyContext?) -> UIViewController { let router = AuthRequestRouter(app: app) let interactor = AuthRequestInteractor() let presenter = AuthRequestPresenter(interactor: interactor, router: router, request: request, context: context) diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift index 2b7254949..257787ee4 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift @@ -20,7 +20,7 @@ final class AuthRequestPresenter: ObservableObject { interactor: AuthRequestInteractor, router: AuthRequestRouter, request: AuthRequest, - context: AuthContext? + context: VerifyContext? ) { defer { setupInitialState() } self.interactor = interactor diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift index b693b01f1..27e2cf3d1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsInteractor.swift @@ -3,7 +3,7 @@ import Combine import Web3Wallet final class ConnectionDetailsInteractor { - var requestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + var requestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { return Web3Wallet.instance.authRequestPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift index 6007b767c..fd20f8d21 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/ConnectionDetails/ConnectionDetailsRouter.swift @@ -1,4 +1,5 @@ import UIKit + import Web3Wallet final class ConnectionDetailsRouter { @@ -10,7 +11,7 @@ final class ConnectionDetailsRouter { self.app = app } - func present(request: AuthRequest, context: AuthContext) { + func present(request: AuthRequest, context: VerifyContext) { AuthRequestModule.create(app: app, request: request, context: context) .wrapToNavigationController() .present(from: viewController) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift index a11b3c92a..a736d11d1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainInteractor.swift @@ -1,11 +1,12 @@ +import Foundation import Combine + import Web3Wallet import WalletConnectPush -import Foundation final class MainInteractor { - var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { + var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never> { return Web3Wallet.instance.sessionProposalPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift index 3decadff3..b8ea7765b 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift @@ -1,6 +1,8 @@ import UIKit -import WalletConnectPush + import Web3Wallet +import WalletConnectPush + final class MainRouter { weak var viewController: UIViewController! @@ -11,8 +13,8 @@ final class MainRouter { return WalletModule.create(app: app) .wrapToNavigationController() } - - func present(proposal: Session.Proposal, context: Session.Context?) { + + func present(proposal: Session.Proposal, context: VerifyContext?) { SessionProposalModule.create(app: app, proposal: proposal, context: context) .presentFullScreen(from: viewController, transparentBackground: true) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift index db63282ad..b0edc7232 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalModule.swift @@ -4,7 +4,7 @@ import Web3Wallet final class SessionProposalModule { @discardableResult - static func create(app: Application, proposal: Session.Proposal, context: Session.Context?) -> UIViewController { + static func create(app: Application, proposal: Session.Proposal, context: VerifyContext?) -> UIViewController { let router = SessionProposalRouter(app: app) let interactor = SessionProposalInteractor() let presenter = SessionProposalPresenter( diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift index 5ec61140e..a13d932c2 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionProposal/SessionProposalPresenter.swift @@ -16,7 +16,7 @@ final class SessionProposalPresenter: ObservableObject { interactor: SessionProposalInteractor, router: SessionProposalRouter, proposal: Session.Proposal, - context: Session.Context? + context: VerifyContext? ) { defer { setupInitialState() } self.interactor = interactor diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift index f48de1900..8a6b51fe2 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestModule.swift @@ -1,9 +1,10 @@ import SwiftUI + import Web3Wallet final class SessionRequestModule { @discardableResult - static func create(app: Application, sessionRequest: Request, sessionContext: Session.Context?) -> UIViewController { + static func create(app: Application, sessionRequest: Request, sessionContext: VerifyContext?) -> UIViewController { let router = SessionRequestRouter(app: app) let interactor = SessionRequestInteractor() let presenter = SessionRequestPresenter(interactor: interactor, router: router, sessionRequest: sessionRequest, context: sessionContext) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift index f216ff368..968687208 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift @@ -23,7 +23,7 @@ final class SessionRequestPresenter: ObservableObject { interactor: SessionRequestInteractor, router: SessionRequestRouter, sessionRequest: Request, - context: Session.Context? + context: VerifyContext? ) { defer { setupInitialState() } self.interactor = interactor diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift index 4b0f24b08..d6eabca65 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletInteractor.swift @@ -4,11 +4,11 @@ import Web3Wallet import WalletConnectPush final class WalletInteractor { - var requestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + var requestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { return Web3Wallet.instance.authRequestPublisher } - var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { + var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> { return Web3Wallet.instance.sessionRequestPublisher } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift index 3c5e2a31f..9d6342d79 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletRouter.swift @@ -1,4 +1,5 @@ import UIKit + import Web3Wallet final class WalletRouter { @@ -10,12 +11,12 @@ final class WalletRouter { self.app = app } - func present(request: AuthRequest, context: AuthContext?) { + func present(request: AuthRequest, context: VerifyContext?) { AuthRequestModule.create(app: app, request: request, context: context) .presentFullScreen(from: viewController, transparentBackground: true) } - func present(sessionRequest: Request, sessionContext: Session.Context?) { + func present(sessionRequest: Request, sessionContext: VerifyContext?) { SessionRequestModule.create(app: app, sessionRequest: sessionRequest, sessionContext: sessionContext) .presentFullScreen(from: viewController, transparentBackground: true) } diff --git a/Package.swift b/Package.swift index 2669c1379..1800221e1 100644 --- a/Package.swift +++ b/Package.swift @@ -60,7 +60,7 @@ let package = Package( path: "Sources/Auth"), .target( name: "Web3Wallet", - dependencies: ["Auth", "WalletConnectSign", "WalletConnectEcho"], + dependencies: ["Auth", "WalletConnectSign", "WalletConnectEcho", "WalletConnectVerify"], path: "Sources/Web3Wallet"), .target( name: "WalletConnectPush", diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index c3261ad34..8240dff20 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,6 +1,9 @@ import Foundation import Combine +import WalletConnectVerify + +public typealias VerifyContext = WalletConnectVerify.VerifyContext /// Auth instatnce wrapper /// /// ```Swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index dc66e9de3..26f3797be 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -13,7 +13,7 @@ public class AuthClient: AuthClientProtocol { /// Publisher that sends authentication requests /// /// Wallet should subscribe on events in order to receive auth requests. - public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { authRequestPublisherSubject.eraseToAnyPublisher() } @@ -37,7 +37,7 @@ public class AuthClient: AuthClientProtocol { private let pairingRegisterer: PairingRegisterer private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - private var authRequestPublisherSubject = PassthroughSubject<(request: AuthRequest, context: AuthContext?), Never>() + private var authRequestPublisherSubject = PassthroughSubject<(request: AuthRequest, context: VerifyContext?), Never>() private let appRequestService: AppRequestService private let appRespondSubscriber: AppRespondSubscriber private let walletRequestSubscriber: WalletRequestSubscriber diff --git a/Sources/Auth/AuthClientProtocol.swift b/Sources/Auth/AuthClientProtocol.swift index 520154a26..d79048ca8 100644 --- a/Sources/Auth/AuthClientProtocol.swift +++ b/Sources/Auth/AuthClientProtocol.swift @@ -2,7 +2,7 @@ import Foundation import Combine public protocol AuthClientProtocol { - var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { get } + var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { get } func formatMessage(payload: AuthPayload, address: String) throws -> String func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index d69d9a142..84640faf9 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -11,7 +11,7 @@ class WalletRequestSubscriber { private let walletErrorResponder: WalletErrorResponder private let pairingRegisterer: PairingRegisterer private let verifyClient: VerifyClient? - var onRequest: (((request: AuthRequest, context: AuthContext?)) -> Void)? + var onRequest: (((request: AuthRequest, context: VerifyContext?)) -> Void)? init( networkingInteractor: NetworkInteracting, @@ -42,19 +42,18 @@ class WalletRequestSubscriber { let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) - if let rawRequest = payload.rawRequest, let verifyClient { - Task(priority: .high) { - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) - let authContext = await AuthContext( - origin: origin, - validation: (origin == payload.request.payloadParams.domain) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyClient.verifyHost - ) - onRequest?((request, authContext)) - } - } else { + guard let verifyClient else { onRequest?((request, nil)) + return + } + Task(priority: .high) { + let assertionId = payload.decryptedPayload.sha256().toHexString() + let origin = try? await verifyClient.verifyOrigin(assertionId: assertionId) + let verifyContext = await verifyClient.createVerifyContext( + origin: origin, + domain: payload.request.payloadParams.domain + ) + onRequest?((request, verifyContext)) } }.store(in: &publishers) } diff --git a/Sources/Auth/Types/Public/AuthContext.swift b/Sources/Auth/Types/Public/AuthContext.swift deleted file mode 100644 index 82c189564..000000000 --- a/Sources/Auth/Types/Public/AuthContext.swift +++ /dev/null @@ -1,11 +0,0 @@ -public struct AuthContext: Equatable, Hashable { - public enum ValidationStatus { - case unknown - case valid - case invalid - } - - public let origin: String? - public let validation: ValidationStatus - public let verifyUrl: String -} diff --git a/Sources/WalletConnectKMS/Serialiser/Serializer.swift b/Sources/WalletConnectKMS/Serialiser/Serializer.swift index 3b8b2f82d..dcd8f984e 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializer.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializer.swift @@ -40,47 +40,38 @@ public class Serializer: Serializing { /// - topic: Topic that is associated with a symetric key for decrypting particular codable object /// - encodedEnvelope: Envelope to deserialize and decrypt /// - Returns: Deserialized object - public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, rawData: String?) { + public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, decryptedPayload: Data) { let envelope = try Envelope(encodedEnvelope) switch envelope.type { case .type0: - let deserialisedType: T = try handleType0Envelope(topic, envelope) - let rawRequest = try rawRequest(topic, envelope) - return (deserialisedType, nil, rawRequest) + let deserialisedType: (object: T, data: Data) = try handleType0Envelope(topic, envelope) + return (deserialisedType.object, nil, deserialisedType.data) case .type1(let peerPubKey): return try handleType1Envelope(topic, peerPubKey: peerPubKey, sealbox: envelope.sealbox) } } - private func handleType0Envelope(_ topic: String, _ envelope: Envelope) throws -> T { + private func handleType0Envelope(_ topic: String, _ envelope: Envelope) throws -> (T, Data) { if let symmetricKey = kms.getSymmetricKeyRepresentable(for: topic) { return try decode(sealbox: envelope.sealbox, symmetricKey: symmetricKey) } else { throw Errors.symmetricKeyForTopicNotFound } } - - private func rawRequest(_ topic: String, _ envelope: Envelope) throws -> String? { - if let symmetricKey = kms.getSymmetricKeyRepresentable(for: topic) { - return try String(data: codec.decode(sealbox: envelope.sealbox, symmetricKey: symmetricKey), encoding: .utf8) - } else { - throw Errors.symmetricKeyForTopicNotFound - } - } - private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String, String?) { + private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String, Data) { guard let selfPubKey = kms.getPublicKey(for: topic) else { throw Errors.publicKeyForTopicNotFound } let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.toHexString()) - let decodedType: T = try decode(sealbox: sealbox, symmetricKey: agreementKeys.sharedKey.rawRepresentation) + let decodedType: (object: T, data: Data) = try decode(sealbox: sealbox, symmetricKey: agreementKeys.sharedKey.rawRepresentation) let derivedTopic = agreementKeys.derivedTopic() try kms.setAgreementSecret(agreementKeys, topic: derivedTopic) - return (decodedType, derivedTopic, nil) + return (decodedType.object, derivedTopic, decodedType.data) } - private func decode(sealbox: Data, symmetricKey: Data) throws -> T { + private func decode(sealbox: Data, symmetricKey: Data) throws -> (T, Data) { let decryptedData = try codec.decode(sealbox: sealbox, symmetricKey: symmetricKey) - return try JSONDecoder().decode(T.self, from: decryptedData) + return (try JSONDecoder().decode(T.self, from: decryptedData), decryptedData) } } diff --git a/Sources/WalletConnectKMS/Serialiser/Serializing.swift b/Sources/WalletConnectKMS/Serialiser/Serializing.swift index dcf8090e9..5982ce660 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializing.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializing.swift @@ -3,12 +3,12 @@ import Foundation public protocol Serializing { func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key - func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, rawData: String?) + func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?, decryptedPayload: Data) } public extension Serializing { /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key - func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?, rawData: String?)? { + func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?, decryptedPayload: Data)? { return try? deserialize(topic: topic, encodedEnvelope: encodedEnvelope) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index ecadf8d6a..ca15160a8 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -3,7 +3,7 @@ import Combine public protocol NetworkInteracting { var socketConnectionStatusPublisher: AnyPublisher { get } - var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { get } + var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) func batchSubscribe(topics: [String]) async throws diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index e65911a22..ede99be10 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -9,10 +9,10 @@ public class NetworkingInteractor: NetworkInteracting { private let rpcHistory: RPCHistory private let logger: ConsoleLogging - private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never>() + private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } @@ -71,9 +71,9 @@ public class NetworkingInteractor: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, rawRequest, publishedAt, derivedTopic in + .compactMap { topic, rpcRequest, decryptedPayload, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, rawRequest: rawRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, decryptedPayload: decryptedPayload, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -131,19 +131,19 @@ public class NetworkingInteractor: NetworkInteracting { } private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date) { - if let (deserializedJsonRpcRequest, derivedTopic, rawData): (RPCRequest, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleRequest(topic: topic, request: deserializedJsonRpcRequest, rawRequest: rawData, publishedAt: publishedAt, derivedTopic: derivedTopic) - } else if let (response, derivedTopic, _): (RPCResponse, String?, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + if let (deserializedJsonRpcRequest, derivedTopic, decryptedPayload): (RPCRequest, String?, Data) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleRequest(topic: topic, request: deserializedJsonRpcRequest, decryptedPayload: decryptedPayload, publishedAt: publishedAt, derivedTopic: derivedTopic) + } else if let (response, derivedTopic, _): (RPCResponse, String?, Data) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } else { logger.debug("Networking Interactor - Received unknown object type from networking relay") } } - private func handleRequest(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?) { + private func handleRequest(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) { do { try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote) - requestPublisherSubject.send((topic, request, rawRequest, publishedAt, derivedTopic)) + requestPublisherSubject.send((topic, request, decryptedPayload, publishedAt, derivedTopic)) } catch { logger.debug(error) } diff --git a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift index fe1e2c2da..135378a99 100644 --- a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift @@ -4,15 +4,15 @@ public struct RequestSubscriptionPayload: Codable, Subscriptio public let id: RPCID public let topic: String public let request: Request - public let rawRequest: String? + public let decryptedPayload: Data public let publishedAt: Date public let derivedTopic: String? - public init(id: RPCID, topic: String, request: Request, rawRequest: String?, publishedAt: Date, derivedTopic: String?) { + public init(id: RPCID, topic: String, request: Request, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) { self.id = id self.topic = topic self.request = request - self.rawRequest = rawRequest + self.decryptedPayload = decryptedPayload self.publishedAt = publishedAt self.derivedTopic = derivedTopic } diff --git a/Sources/WalletConnectPairing/Types/AppMetadata.swift b/Sources/WalletConnectPairing/Types/AppMetadata.swift index 56c72ee3f..e30e8c71c 100644 --- a/Sources/WalletConnectPairing/Types/AppMetadata.swift +++ b/Sources/WalletConnectPairing/Types/AppMetadata.swift @@ -44,7 +44,7 @@ public struct AppMetadata: Codable, Equatable { public let icons: [String] /// The URL which used by VerifyClient. - public let verifyUrl: String + public let verifyUrl: String? /// Redirect links which could be manually used on wallet side. public let redirect: Redirect? @@ -65,7 +65,7 @@ public struct AppMetadata: Codable, Equatable { description: String, url: String, icons: [String], - verifyUrl: String = "verify.walletconnect.com", + verifyUrl: String? = nil, redirect: Redirect? = nil ) { self.name = name diff --git a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift index 177f4bb76..e2179fc95 100644 --- a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift +++ b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift @@ -1,3 +1,5 @@ +import Foundation + import WalletConnectKMS public class PushDecryptionService { @@ -17,7 +19,7 @@ public class PushDecryptionService { } public func decryptMessage(topic: String, ciphertext: String) throws -> PushMessage { - let (rpcRequest, _, _): (RPCRequest, String?, String?) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) guard let params = rpcRequest.params else { throw Errors.malformedPushMessage } return try params.get(PushMessage.self) } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index f0ccc7bfb..f56ffd908 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -13,7 +13,7 @@ final class ApproveEngine { case agreementMissingOrInvalid } - var onSessionProposal: ((Session.Proposal, Session.Context?) -> Void)? + var onSessionProposal: ((Session.Proposal, VerifyContext?) -> Void)? var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionSettle: ((Session) -> Void)? @@ -283,19 +283,18 @@ private extension ApproveEngine { } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) - if let rawRequest = payload.rawRequest, let verifyClient { - Task(priority: .high) { - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) - let sessionContext = await Session.Context( - origin: origin, - validation: (origin == payload.request.proposer.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyClient.verifyHost - ) - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), sessionContext) - } - } else { + guard let verifyClient else { onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) + return + } + Task(priority: .high) { + let assertionId = payload.decryptedPayload.sha256().toHexString() + let origin = try? await verifyClient.verifyOrigin(assertionId: assertionId) + let verifyContext = await verifyClient.createVerifyContext( + origin: origin, + domain: payload.request.proposer.metadata.url + ) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) } } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 7153f093c..27aae0941 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -10,7 +10,7 @@ final class SessionEngine { } var onSessionsUpdate: (([Session]) -> Void)? - var onSessionRequest: ((Request, Session.Context?) -> Void)? + var onSessionRequest: ((Request, VerifyContext?) -> Void)? var onSessionResponse: ((Response) -> Void)? var onSessionRejected: ((String, SessionType.Reason) -> Void)? var onSessionDelete: ((String, SessionType.Reason) -> Void)? @@ -133,7 +133,7 @@ private extension SessionEngine { networkingInteractor.requestSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in Task(priority: .high) { - await onSessionRequest(payload: payload) + onSessionRequest(payload: payload) } }.store(in: &publishers) @@ -228,7 +228,6 @@ private extension SessionEngine { chainId: payload.request.chainId, expiry: payload.request.request.expiry ) - guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } @@ -238,24 +237,21 @@ private extension SessionEngine { guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { return respondError(payload: payload, reason: .unauthorizedMethod(request.method), protocolMethod: protocolMethod) } - guard !request.isExpired() else { return respondError(payload: payload, reason: .sessionRequestExpired, protocolMethod: protocolMethod) } - - if let rawRequest = payload.rawRequest, let verifyClient { - Task(priority: .high) { - let attestationId = rawRequest.rawRepresentation.sha256().toHexString() - let origin = try? await verifyClient.registerAssertion(attestationId: attestationId) - let sessionContext = await Session.Context( - origin: origin, - validation: (origin == session.peerParticipant.metadata.url) ? .valid : (origin == nil ? .unknown : .invalid), - verifyUrl: verifyClient.verifyHost - ) - onSessionRequest?(request, sessionContext) - } - } else { + guard let verifyClient else { onSessionRequest?(request, nil) + return + } + Task(priority: .high) { + let assertionId = payload.decryptedPayload.sha256().toHexString() + let origin = try? await verifyClient.verifyOrigin(assertionId: assertionId) + let verifyContext = await verifyClient.createVerifyContext( + origin: origin, + domain: session.peerParticipant.metadata.url + ) + onSessionRequest?(request, verifyContext) } } diff --git a/Sources/WalletConnectSign/Session.swift b/Sources/WalletConnectSign/Session.swift index 6d287f9a1..c880f6cd0 100644 --- a/Sources/WalletConnectSign/Session.swift +++ b/Sources/WalletConnectSign/Session.swift @@ -41,16 +41,4 @@ extension Session { SessionType.EventParams.Event(name: name, data: data) } } - - public struct Context: Equatable, Hashable { - public enum ValidationStatus { - case unknown - case valid - case invalid - } - - public let origin: String? - public let validation: ValidationStatus - public let verifyUrl: String - } } diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 923b6fe82..40ba5b16a 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -1,6 +1,10 @@ import Foundation import Combine +import WalletConnectVerify + +public typealias VerifyContext = WalletConnectVerify.VerifyContext + /// Sign instatnce wrapper /// /// ```swift diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index cef509c52..7d18d08ed 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -16,14 +16,14 @@ public final class SignClient: SignClientProtocol { /// Publisher that sends session proposal /// /// event is emited on responder client only - public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { + public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never> { sessionProposalPublisherSubject.eraseToAnyPublisher() } /// Publisher that sends session request /// /// In most cases event will be emited on wallet - public var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { + public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> { sessionRequestPublisherSubject.eraseToAnyPublisher() } @@ -113,8 +113,8 @@ public final class SignClient: SignClientProtocol { private let historyService: HistoryService private let cleanupService: SignCleanupService - private let sessionProposalPublisherSubject = PassthroughSubject<(proposal: Session.Proposal, context: Session.Context?), Never>() - private let sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: Session.Context?), Never>() + private let sessionProposalPublisherSubject = PassthroughSubject<(proposal: Session.Proposal, context: VerifyContext?), Never>() + private let sessionRequestPublisherSubject = PassthroughSubject<(request: Request, context: VerifyContext?), Never>() private let socketConnectionStatusPublisherSubject = PassthroughSubject() private let sessionSettlePublisherSubject = PassthroughSubject() private let sessionDeletePublisherSubject = PassthroughSubject<(String, Reason), Never>() diff --git a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift index 21ce05e8a..a1dcfc3f3 100644 --- a/Sources/WalletConnectSign/Sign/SignClientProtocol.swift +++ b/Sources/WalletConnectSign/Sign/SignClientProtocol.swift @@ -2,8 +2,8 @@ import Foundation import Combine public protocol SignClientProtocol { - var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { get } - var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { get } + var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never> { get } + var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> { get } var sessionsPublisher: AnyPublisher<[Session], Never> { get } var socketConnectionStatusPublisher: AnyPublisher { get } var sessionSettlePublisher: AnyPublisher { get } diff --git a/Sources/WalletConnectVerify/AssertionRegistrer.swift b/Sources/WalletConnectVerify/AssertionRegistrer.swift index b96ac6ebe..616368bd7 100644 --- a/Sources/WalletConnectVerify/AssertionRegistrer.swift +++ b/Sources/WalletConnectVerify/AssertionRegistrer.swift @@ -1,26 +1,3 @@ -import Foundation -import WalletConnectNetworking - -public class AssertionRegistrer { - enum Errors: Error { - case registrationFailed - } - - let verifyHost: String - - init(verifyHost: String) { - self.verifyHost = verifyHost - } - - func registerAssertion(attestationId: String) async throws -> String { - let httpClient = HTTPNetworkClient(host: verifyHost) - let response = try await httpClient.request( - VerifyResponse.self, - at: VerifyAPI.resolve(attestationId: attestationId) - ) - guard let origin = response.origin else { - throw Errors.registrationFailed - } - return origin - } +class AssertionRegistrer { + func registerAssertion() async throws {} } diff --git a/Sources/WalletConnectVerify/OriginVerifier.swift b/Sources/WalletConnectVerify/OriginVerifier.swift index 7ad8c2d33..424a45ac0 100644 --- a/Sources/WalletConnectVerify/OriginVerifier.swift +++ b/Sources/WalletConnectVerify/OriginVerifier.swift @@ -1,5 +1,27 @@ import Foundation +import WalletConnectNetworking -class OriginVerifier { - func verifyOrigin() async throws {} +public final class OriginVerifier { + enum Errors: Error { + case registrationFailed + } + + private let verifyHost: String + + init(verifyHost: String) { + self.verifyHost = verifyHost + } + + func verifyOrigin(assertionId: String) async throws -> String { + let httpClient = HTTPNetworkClient(host: verifyHost) + let response = try await httpClient.request( + VerifyResponse.self, + at: VerifyAPI.resolve(assertionId: assertionId) + ) + guard let origin = response.origin else { + throw Errors.registrationFailed + } + return origin + } } + diff --git a/Sources/WalletConnectVerify/Register/VerifyService.swift b/Sources/WalletConnectVerify/Register/VerifyService.swift index c0009f63b..cec111182 100644 --- a/Sources/WalletConnectVerify/Register/VerifyService.swift +++ b/Sources/WalletConnectVerify/Register/VerifyService.swift @@ -2,11 +2,11 @@ import Foundation import WalletConnectNetworking enum VerifyAPI: HTTPService { - case resolve(attestationId: String) + case resolve(assertionId: String) var path: String { switch self { - case .resolve(let attestationId): return "/attestation/\(attestationId)" + case .resolve(let assertionId): return "/attestation/\(assertionId)" } } diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift index f035864fc..74d01217b 100644 --- a/Sources/WalletConnectVerify/VerifyClient.swift +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -1,5 +1,6 @@ import DeviceCheck import Foundation + import WalletConnectUtils import WalletConnectNetworking @@ -8,10 +9,11 @@ public actor VerifyClient { case attestationNotSupported } - public let verifyHost: String let originVerifier: OriginVerifier let assertionRegistrer: AssertionRegistrer let appAttestationRegistrer: AppAttestationRegistrer + + private let verifyHost: String init( verifyHost: String, @@ -29,11 +31,19 @@ public actor VerifyClient { try await appAttestationRegistrer.registerAttestationIfNeeded() } - public func verifyOrigin() async throws { - try await originVerifier.verifyOrigin() + public func verifyOrigin(assertionId: String) async throws -> String { + return try await originVerifier.verifyOrigin(assertionId: assertionId) + } + + public func createVerifyContext(origin: String?, domain: String) -> VerifyContext { + return VerifyContext( + origin: origin, + validation: (origin == domain) ? .valid : (origin == nil ? .unknown : .invalid), + verifyUrl: verifyHost + ) } - public func registerAssertion(attestationId: String) async throws -> String { - return try await assertionRegistrer.registerAssertion(attestationId: attestationId) + public func registerAssertion() async throws { + try await assertionRegistrer.registerAssertion() } } diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index 35a495673..b07648532 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -2,9 +2,12 @@ import Foundation import WalletConnectUtils public class VerifyClientFactory { - public static func create(verifyHost: String) throws -> VerifyClient { - let originVerifier = OriginVerifier() - let assertionRegistrer = AssertionRegistrer(verifyHost: verifyHost) + public static func create(verifyHost: String?) throws -> VerifyClient? { + guard let verifyHost else { + return nil + } + let originVerifier = OriginVerifier(verifyHost: verifyHost) + let assertionRegistrer = AssertionRegistrer() let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keyIdStorage = CodableStore(defaults: keyValueStorage, identifier: VerifyStorageIdentifier.keyId) diff --git a/Sources/WalletConnectVerify/VerifyContext.swift b/Sources/WalletConnectVerify/VerifyContext.swift new file mode 100644 index 000000000..094ea95ab --- /dev/null +++ b/Sources/WalletConnectVerify/VerifyContext.swift @@ -0,0 +1,17 @@ +public struct VerifyContext: Equatable, Hashable { + public enum ValidationStatus { + case unknown + case valid + case invalid + } + + public let origin: String? + public let validation: ValidationStatus + public let verifyUrl: String + + public init(origin: String?, validation: ValidationStatus, verifyUrl: String) { + self.origin = origin + self.validation = validation + self.verifyUrl = verifyUrl + } +} diff --git a/Sources/Web3Wallet/Web3Wallet.swift b/Sources/Web3Wallet/Web3Wallet.swift index c016d47d1..ffdf09f16 100644 --- a/Sources/Web3Wallet/Web3Wallet.swift +++ b/Sources/Web3Wallet/Web3Wallet.swift @@ -1,6 +1,9 @@ import Foundation import Combine +import WalletConnectVerify + +public typealias VerifyContext = WalletConnectVerify.VerifyContext /// Web3Wallet instance wrapper /// /// ```Swift diff --git a/Sources/Web3Wallet/Web3WalletClient.swift b/Sources/Web3Wallet/Web3WalletClient.swift index f67d8ec58..0f117f0a8 100644 --- a/Sources/Web3Wallet/Web3WalletClient.swift +++ b/Sources/Web3Wallet/Web3WalletClient.swift @@ -8,25 +8,25 @@ import Combine /// Access via `Web3Wallet.instance` public class Web3WalletClient { // MARK: - Public Properties - + /// Publisher that sends session proposal /// /// event is emited on responder client only - public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: Session.Context?), Never> { + public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never> { signClient.sessionProposalPublisher.eraseToAnyPublisher() } /// Publisher that sends session request /// /// In most cases event will be emited on wallet - public var sessionRequestPublisher: AnyPublisher<(request: Request, context: Session.Context?), Never> { + public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never> { signClient.sessionRequestPublisher.eraseToAnyPublisher() } /// Publisher that sends authentication requests /// /// Wallet should subscribe on events in order to receive auth requests. - public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { authClient.authRequestPublisher.eraseToAnyPublisher() } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index a51869620..8690a69da 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -42,7 +42,7 @@ class WalletRequestSubscriberTests: XCTestCase { messageExpectation.fulfill() } - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), rawRequest: "", publishedAt: Date(), derivedTopic: nil) + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), decryptedPayload: Data(), publishedAt: Date(), derivedTopic: nil) pairingRegisterer.subject.send(payload) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index c669cdea3..14f7f9d24 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -29,10 +29,10 @@ public class NetworkingInteractorMock: NetworkInteracting { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } - public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never>() + public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never>() public let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: JSONRPC.RPCRequest, rawRequest: String?, publishedAt: Date, derivedTopic: String?), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: JSONRPC.RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } @@ -46,9 +46,9 @@ public class NetworkingInteractorMock: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, rawRequest, publishedAt, derivedTopic in + .compactMap { topic, rpcRequest, decryptedPayload, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, rawRequest: rawRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, decryptedPayload: decryptedPayload, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } diff --git a/Tests/WalletConnectKMSTests/SerialiserTests.swift b/Tests/WalletConnectKMSTests/SerialiserTests.swift index cc3bca7bf..627171b5a 100644 --- a/Tests/WalletConnectKMSTests/SerialiserTests.swift +++ b/Tests/WalletConnectKMSTests/SerialiserTests.swift @@ -23,7 +23,7 @@ final class SerializerTests: XCTestCase { _ = try! myKms.createSymmetricKey(topic) let messageToSerialize = "todo - change for request object" let serializedMessage = try! mySerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type0) - let (deserializedMessage, _, _): (String, String?, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! + let (deserializedMessage, _, _): (String, String?, Data) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } @@ -39,7 +39,7 @@ final class SerializerTests: XCTestCase { let serializedMessage = try! peerSerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type1(pubKey: peerPubKey.rawRepresentation)) print(agreementKeys.sharedKey.hexRepresentation) // -----------Me Deserialising ------------------- - let (deserializedMessage, _, _): (String, String?, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! + let (deserializedMessage, _, _): (String, String?, Data) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 6420fe82a..a41b1cd46 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -61,7 +61,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: nil, publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, decryptedPayload: Data(), publishedAt: Date(), derivedTopic: nil)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -85,7 +85,7 @@ final class ApproveEngineTests: XCTestCase { sessionProposed = true } - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, rawRequest: nil, publishedAt: Date(), derivedTopic: nil)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, decryptedPayload: Data(), publishedAt: Date(), derivedTopic: nil)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } @@ -109,7 +109,7 @@ final class ApproveEngineTests: XCTestCase { didCallBackOnSessionApproved = true } sessionTopicToProposal.set(SessionProposal.stub().publicRepresentation(pairingTopic: ""), forKey: sessionTopic) - networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), "", Date(), "")) + networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), Data(), Date(), "")) usleep(100) diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index e097d2cb0..aa4c917ca 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -35,7 +35,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { didCallbackUpdatMethods = true XCTAssertEqual(topic, session.topic) } - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Data(), Date(), nil)) XCTAssertTrue(didCallbackUpdatMethods) usleep(100) XCTAssertTrue(networkingInteractor.didRespondSuccess) @@ -51,7 +51,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { // } func testUpdateMethodPeerErrorSessionNotFound() { - networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), Data(), Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) @@ -60,7 +60,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { func testUpdateMethodPeerErrorUnauthorized() { let session = WCSession.stub(isSelfController: true) // Peer is not a controller storageMock.setSession(session) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Data(), Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) @@ -74,7 +74,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Data(), Date(), nil)) let extendedSession = storageMock.getAll().first {$0.topic == session.topic}! print(extendedSession.expiryDate) @@ -87,7 +87,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Data(), Date(), nil)) let potentiallyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentiallyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended for peer non controller request ") @@ -98,7 +98,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow) storageMock.setSession(session) let tenDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), Data(), Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to high") @@ -110,7 +110,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let oneDayFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), "", Date(), nil)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), Data(), Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to low") } diff --git a/Tests/WalletConnectSignTests/SessionEngineTests.swift b/Tests/WalletConnectSignTests/SessionEngineTests.swift index b6ce1b40e..4bd18ff2b 100644 --- a/Tests/WalletConnectSignTests/SessionEngineTests.swift +++ b/Tests/WalletConnectSignTests/SessionEngineTests.swift @@ -48,7 +48,7 @@ final class SessionEngineTests: XCTestCase { expiry: UInt64(Date().timeIntervalSince1970) ) - networkingInteractor.requestPublisherSubject.send(("topic", request, "request", Date(), "")) + networkingInteractor.requestPublisherSubject.send(("topic", request, Data(), Date(), "")) wait(for: [expectation], timeout: 0.5) } diff --git a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift index d55a25968..535ace60a 100644 --- a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift @@ -1,7 +1,7 @@ import Foundation import Combine -@testable import Auth +@testable import Auth final class AuthClientMock: AuthClientProtocol { var respondCalled = false @@ -26,7 +26,7 @@ final class AuthClientMock: AuthClientProtocol { ) } - var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: AuthContext?), Never> { + var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never> { return Result.Publisher((authRequest, nil)).eraseToAnyPublisher() } diff --git a/Tests/Web3WalletTests/Mocks/SignClientMock.swift b/Tests/Web3WalletTests/Mocks/SignClientMock.swift index 36bd10bec..3a9b6336d 100644 --- a/Tests/Web3WalletTests/Mocks/SignClientMock.swift +++ b/Tests/Web3WalletTests/Mocks/SignClientMock.swift @@ -3,7 +3,6 @@ import Combine @testable import WalletConnectSign - final class SignClientMock: SignClientProtocol { var approveCalled = false var rejectCalled = false @@ -19,7 +18,7 @@ final class SignClientMock: SignClientProtocol { private let request = WalletConnectSign.Request(id: .left(""), topic: "", method: "", params: "", chainId: Blockchain("eip155:1")!, expiry: nil) private let response = WalletConnectSign.Response(id: RPCID(1234567890123456789), topic: "", chainId: "", result: .response(AnyCodable(any: ""))) - var sessionProposalPublisher: AnyPublisher<(proposal: WalletConnectSign.Session.Proposal, context: WalletConnectSign.Session.Context?), Never> { + var sessionProposalPublisher: AnyPublisher<(proposal: WalletConnectSign.Session.Proposal, context: VerifyContext?), Never> { let proposer = Participant(publicKey: "", metadata: metadata) let sessionProposal = WalletConnectSign.SessionProposal( relays: [], @@ -33,7 +32,7 @@ final class SignClientMock: SignClientProtocol { .eraseToAnyPublisher() } - var sessionRequestPublisher: AnyPublisher<(request: WalletConnectSign.Request, context: WalletConnectSign.Session.Context?), Never> { + var sessionRequestPublisher: AnyPublisher<(request: WalletConnectSign.Request, context: VerifyContext?), Never> { return Result.Publisher((request, nil)) .eraseToAnyPublisher() } From d0f3a795b6812e9874471a94d26fa3032106bc44 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Mon, 1 May 2023 20:53:34 +0200 Subject: [PATCH 61/89] Add basic UI --- Example/DApp/Auth/AuthView.swift | 66 ++++++++---- Example/ExampleApp.xcodeproj/project.pbxproj | 7 ++ Package.swift | 11 +- Sources/Web3Modal/QRCodeView.swift | 69 ++++++++++++ .../Resources/Assets.xcassets/Contents.json | 6 ++ .../help/Browser.imageset/Browser.pdf | Bin 0 -> 14173 bytes .../help/Browser.imageset/Contents.json | 12 +++ .../Assets.xcassets/help/Contents.json | 6 ++ .../help/DAO.imageset/Contents.json | 12 +++ .../Assets.xcassets/help/DAO.imageset/DAO.pdf | Bin 0 -> 15022 bytes .../help/DeFi.imageset/Contents.json | 12 +++ .../help/DeFi.imageset/DeFi.pdf | Bin 0 -> 12023 bytes .../help/ETH.imageset/Contents.json | 12 +++ .../Assets.xcassets/help/ETH.imageset/ETH.pdf | Bin 0 -> 6369 bytes .../help/Layers.imageset/Contents.json | 12 +++ .../help/Layers.imageset/Layers.pdf | Bin 0 -> 17239 bytes .../help/Lock.imageset/Contents.json | 12 +++ .../help/Lock.imageset/Lock.pdf | Bin 0 -> 9067 bytes .../help/Login.imageset/Contents.json | 12 +++ .../help/Login.imageset/Login.pdf | Bin 0 -> 19249 bytes .../help/NFT.imageset/Contents.json | 12 +++ .../Assets.xcassets/help/NFT.imageset/NFT.pdf | Bin 0 -> 13768 bytes .../help/Network.imageset/Contents.json | 12 +++ .../help/Network.imageset/Network.pdf | Bin 0 -> 12677 bytes .../help/Noun.imageset/Contents.json | 12 +++ .../help/Noun.imageset/Noun.pdf | Bin 0 -> 9725 bytes .../help/Profile.imageset/Contents.json | 12 +++ .../help/Profile.imageset/Profile.pdf | Bin 0 -> 10773 bytes .../help/System.imageset/Contents.json | 12 +++ .../help/System.imageset/System.pdf | Bin 0 -> 45833 bytes .../Assets.xcassets/icons/Contents.json | 6 ++ .../icons/Wallet.imageset/Contents.json | 12 +++ .../icons/Wallet.imageset/Wallet.pdf | Bin 0 -> 3113 bytes .../external_link.imageset/Contents.json | 12 +++ .../external_link.imageset/External Link.pdf | Bin 0 -> 1427 bytes .../wc_logo.imageset/Contents.json | 12 +++ .../wc_logo.imageset/WCLOGO.pdf | Bin 0 -> 46930 bytes Sources/Web3Modal/Web3ModalSheet.swift | 88 ++++++++++++++++ Sources/Web3Modal/WhatIsWalletView.swift | 98 ++++++++++++++++++ 39 files changed, 513 insertions(+), 24 deletions(-) create mode 100644 Sources/Web3Modal/QRCodeView.swift create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Browser.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/DAO.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/DeFi.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/DeFi.imageset/DeFi.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/ETH.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/ETH.imageset/ETH.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Layers.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Layers.imageset/Layers.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Lock.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Login.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/NFT.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Network.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Noun.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Profile.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/Profile.imageset/Profile.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/System.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/help/System.imageset/System.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Wallet.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/External Link.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/WCLOGO.pdf create mode 100644 Sources/Web3Modal/Web3ModalSheet.swift create mode 100644 Sources/Web3Modal/WhatIsWalletView.swift diff --git a/Example/DApp/Auth/AuthView.swift b/Example/DApp/Auth/AuthView.swift index 5acdaf08c..74e17e09b 100644 --- a/Example/DApp/Auth/AuthView.swift +++ b/Example/DApp/Auth/AuthView.swift @@ -1,39 +1,59 @@ import SwiftUI +import Web3Modal struct AuthView: View { @ObservedObject var viewModel: AuthViewModel + @State var showSheet: Bool = false + var body: some View { - VStack(spacing: 16.0) { - - Spacer() - - Image(uiImage: viewModel.qrImage ?? UIImage()) - .interpolation(.none) - .resizable() - .frame(width: 300, height: 300) - - signingLabel() - .frame(maxWidth: .infinity) - - Spacer() - - Button("Connect Wallet", action: { }) - .buttonStyle(CircleButtonStyle()) - - Button("Copy URI", action: { viewModel.copyDidPressed() }) - .buttonStyle(CircleButtonStyle()) - - Button("Deeplink", action: { viewModel.deeplinkPressed() }) - .buttonStyle(CircleButtonStyle()) + ZStack(alignment: .bottom) { + + VStack(spacing: 16.0) { + + Spacer() + + Image(uiImage: viewModel.qrImage ?? UIImage()) + .interpolation(.none) + .resizable() + .frame(width: 300, height: 300) + + signingLabel() + .frame(maxWidth: .infinity) + + Spacer() + + connectWalletButton() + + Button("Copy URI", action: { viewModel.copyDidPressed() }) + .buttonStyle(CircleButtonStyle()) + + Button("Deeplink", action: { viewModel.deeplinkPressed() }) + .buttonStyle(CircleButtonStyle()) + + + } + .padding(16.0) + + + if showSheet { + Web3ModalSheet() + .transition(.move(edge: .bottom)) + .animation(.easeInOut) + } } - .padding(16.0) .onAppear { Task(priority: .userInitiated) { try await viewModel.setupInitialState() }} } + private func connectWalletButton() -> some View { + + Button("Connect Wallet", action: { showSheet.toggle() }) + .buttonStyle(CircleButtonStyle()) + } + @ViewBuilder private func signingLabel() -> some View { switch viewModel.state { diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 0907a126a..2b0947204 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -272,6 +272,7 @@ CF1A594D29E5876600AAC16B /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594429E5876600AAC16B /* App.swift */; }; CF6704DF29E59DDC003326A4 /* XCUIElementQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704DE29E59DDC003326A4 /* XCUIElementQuery.swift */; }; CF6704E129E5A014003326A4 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704E029E5A014003326A4 /* XCTestCase.swift */; }; + CFFD44C729FFE9BA00D28724 /* Web3Modal in Frameworks */ = {isa = PBXBuildFile; productRef = CFFD44C629FFE9BA00D28724 /* Web3Modal */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -594,6 +595,7 @@ 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, A54195A52934E83F0035AD19 /* Web3 in Frameworks */, 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */, + CFFD44C729FFE9BA00D28724 /* Web3Modal in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -1706,6 +1708,7 @@ A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, A54195A42934E83F0035AD19 /* Web3 */, 84E6B8642981720400428BAF /* WalletConnectPush */, + CFFD44C629FFE9BA00D28724 /* Web3Modal */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -3111,6 +3114,10 @@ isa = XCSwiftPackageProductDependency; productName = Web3Wallet; }; + CFFD44C629FFE9BA00D28724 /* Web3Modal */ = { + isa = XCSwiftPackageProductDependency; + productName = Web3Modal; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 764E1D3426F8D3FC00A1FB15 /* Project object */; diff --git a/Package.swift b/Package.swift index d65fe6fe8..2b5c17361 100644 --- a/Package.swift +++ b/Package.swift @@ -43,8 +43,14 @@ let package = Package( .library( name: "Web3Inbox", targets: ["Web3Inbox"]), + .library( + name: "Web3Modal", + targets: ["Web3Modal"]), + + ], + dependencies: [ + .package(url: "https://github.com/dagronf/QRCode", from: "13.9.0") ], - dependencies: [], targets: [ .target( name: "WalletConnectSign", @@ -112,6 +118,9 @@ let package = Package( .target( name: "WalletConnectVerify", dependencies: ["WalletConnectUtils"]), + .target( + name: "Web3Modal", + dependencies: ["QRCode"]), .testTarget( name: "WalletConnectSignTests", dependencies: ["WalletConnectSign", "WalletConnectUtils", "TestingUtils"]), diff --git a/Sources/Web3Modal/QRCodeView.swift b/Sources/Web3Modal/QRCodeView.swift new file mode 100644 index 000000000..b58041575 --- /dev/null +++ b/Sources/Web3Modal/QRCodeView.swift @@ -0,0 +1,69 @@ +import SwiftUI +import QRCode + +struct QRCodeView: View { + + @State var doc: QRCode.Document! + + @Environment(\.colorScheme) var colorScheme: ColorScheme + + var body: some View { + VStack { + render()? + .resizable() + .scaledToFit() +// .padding(.top, 60) +// .padding([.horizontal, .bottom], 20) + + } + } + + func render() -> Image? { + let doc = QRCode.Document( + utf8String: "jfhaskfhakslfhkljashdfkjahdkljfhakljhfklhkjaglkjdhfjhaskjdfhakjfbajlsdhfkjsfkjahsdfkjlhalkjdhfajskdfkjdhaksjdhakjdhakjsdhkasjhskjdhskjashdkjashda13aefdsadsadsajdkajshdaksjhdskajhsdakjhsakdasjdhkasjdhaksjdhakjdhakjsdhkasjhskjdhskjashdkjashd", + errorCorrection: .high + ) + + doc.design.shape.eye = QRCode.EyeShape.Squircle() + doc.design.shape.onPixels = QRCode.PixelShape.Vertical( + insetFraction: 0.2, + cornerRadiusFraction: 1 + ) + + doc.design.style.eye = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) + doc.design.style.pupil = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) + doc.design.style.onPixels = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) + doc.design.style.background = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.white.cgColor : UIColor.black.cgColor) + + guard let logo = UIImage(named: "wc_logo", in: .module, with: .none)?.cgImage else { + return doc.imageUI( + CGSize(width: 600, height: 600), label: Text("fooo") + ) + } + + doc.logoTemplate = QRCode.LogoTemplate( + image: logo, + path: CGPath( + rect: CGRect(x: 0.35, y: 0.3875, width: 0.30, height: 0.225), + transform: nil + ) + ) + + return doc.imageUI( + CGSize(width: 600, height: 600), label: Text("fooo") + ) + } +} + + +struct QRCodeView_Previews: PreviewProvider { + static var previews: some View { + QRCodeView() + .previewLayout(.sizeThatFits) + .preferredColorScheme(.dark) + + QRCodeView() + .previewLayout(.sizeThatFits) + .preferredColorScheme(.light) + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Browser.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Browser.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b7944ba52351db718bb39b203521c0bd4aea3912 GIT binary patch literal 14173 zcmeI3U619)afa{Dui%XUJAgR--6UWbus#tahUMJ=F<|6kbTpRMnVpemMuwbUpXaUS zVV@!CXq9X2g-zAzE>_p)Th*M8zWVHokNsFrCqKC1@$0{yhT%8A8E*dLAK%{%H-GxW z*MGe`zd+;P&A;D0eEag<5LozTA$|Gu_`~;C8|=T^Z?>mxd<(y)+lS}xZ=dcS&i{Dz z`)_}{uzLH$*Dv1eah3&574)C+WB-uy+3tMeQkd_`nB^ppZdy9 zy}zP%={l$N5Pi!^x|VV!-k&;~mAF0}{MH$tyUIH8{`B4HFQJ)v|o0tq=R3Rt8t6UthB>-;1|W z&Zl+xN?RZHKV7}AEA|6J!{;&PIn8zOe)LnF=kNilC3fPJYaa6GYW7na-k(TGc}k(q zsMNtSgyCL6wpl3SyD<=XF$`r4bt-XYdl+4)E*4lO@13jcKBDdSHjd8Eo*>L)t+4cP z^y56a2@`X~X$oaFdA94c8?W*;rKt=tBHH9o0uoK5OTqhTh<;3=xY_xsJjOYqn<1j^SQAh1I9MwlK8!x3 zG?yV{6yPVPvp~hNKGY#3&N|n5g2&NX@tDV&rWl+Sml|gd{{A$L*`*211gw@^vXdMV z54MYIMFBpBJOpAOV0HeRYA;avd8UJ9d@o*@|*9jhRoP*0K8Xov> znL?#V1oT+lY{p-x={5C$gE)KKEf)n7@mW&LQMT~7CN{`vn8zu3+)ma!Bp&QHzVn!| z1#TeM$UaiakeFIni-R3+ixZCPVZ5hG$NqqW>0;=0(sl*Z8)SE4wglWY?0)@ipJXBXyLC69Nq5xL5WKY3yZB&|A! zmK(MU?Km^pEKgHb1ji(Am7?&l%#n!HfCDDEwZ-)j^?<{|ITT;>EY4G^L`UNs0U0=r zI1~2dGC0rUZtcRury%38I^%g2%4L{Iqy8~)*6q4vzRL&MV{tCE{H)b zu#Uq*@5^;!El(tdMdky(dY{5p&OOtvE>aJD%Iio-wi@&lIks)Mx0 z(T)>)86)9}n^~H5uBAxi0w3UWm>lPsoC$Hj*HpPHV(&?&p+@rEKp3S$Ws@xq)1E45 zEi-O_##0GAVEG+OJJ>h_USq}mDRDK)Cvl>Q;01^hMdUG(5pGago%K;*SCmwg%M>;~ z;LE}`6vyC;Xw_5xv%|!03{qU1Ozgl3#>IaB=WNF3~rAZEYq?9vI!Q6sTcF&4s#Ms2zF>FoFgJQX$SU zCL~~1HmQB7DFfZm(jN6dDA*w*d|@7wJvqn8py5B} z2iPK$0idLiUej(6Umaw^mLDs{4TlD_qDG#`ncjJY zv}l=kgASpQ6KKXnD@3wKsiNi}ASY_Yi<7OaF@ps}n@AeyfptKonGy)wPneo5;zmOq>v3g9gTV z-4gI_iX}9XHq|H26m0|fg`g+20UuD6m)Kv>e@P%SXcB{yLEF_XJI_G_^PL6}U;|uk zh+v{JJ1-y&)zN#P38>`YMA8LzRKh|{O&TYRjc%ar+7XjKQbZQrk<0-f28g1=1MK_+ zokMD!R4B?cTF73C322O7s1xXk{fw_ zB=oi$Bnx+VQhI1E+DbY-^N%u<+KeN`Z}G!IingL5p7>m{-Gy>Nxe<-z-`-tl=FppH zcn?KWk^_Qh_1e1uVDzJ+Qnl2Mv66O-z!!~n6nUYEGf7M|>hfhbF2zhrRMMy;U1}wK zNb#bf3A|42pn584}3DOcjQ{5_kjh%?Aku z+=Z9uIN%-CqFvK{K9W#^A81aq03u8M1v3T;l7Iw00}#+_)GrscR0k58(szeJ>9oJ3 zbNUpHSk?Z3F=du)-wF@gg&{3u^hz)>w0WKJ*Cn?+f5bV zc_62Gywv=<%Qx}Kvg9Fi3;J3#B?h#`f)ePA;SLF9-?XbpaVQ)bm@lEoAxb@HzX~)m z8Ec{f^f+E6A(0EgZp`yQx)g?^&dqpk#ayH}{jqJ;AL5f65)C`rXI>L%MoiO$nOqcF z-XtfC;uY&aD|12rmuXF4%aC<~1OOT!}SNRsd~s~pF`2DlOuYvjrpiaKu; zoKb6Nb#ZBv(*?knwUty^YLz%m8q10ko-HVkOd66&0uuUiJ`!0wGax~MiWZnhn4Ae2 z#7SlLdSWBJP=ke88Fgs%X7e$P+ZruWLFrWkSsb)WY5~)6K;lU7{?%YWytej|6%RTw z8~-o>Wq?R~2D)Wlp^hzqL`)O`9pKl&kjNy)5adv_7NhVd;e#{`rmT5X>L&ZrMv%DF zkT3#c3?>RA6b=Igy*9KeO%^Nw4fmjq3*Rqo8U3S_ED9~PS`^+;i5sH8;t}7`KB@ue z07?X{7$s}C3q^rolt>OSd~3c#6E7L8sb&?g!%9#<&G-(Kh{e^NU=QGXdmqB!YS2J1 z=Dk!cTtyTVsT##WXPOh~#p6uc2Mx3Yte38YGPH`^%P<8Bvqx|3<7SXS@3Zfomw?j<{}&|TC~PnySeae zZviH%-)<6RW`p25R;o+Gi?tGm1Url4R2TfnB`J<98aW=U-*(d-;Sf-+C4+8T ziInOKMbjLm(E!wxrM8u5>A|Gp|1FMl%D|t)v4*J%mW0Kyp zp>awu8AC}>?&qDdj`N<+BlWs-o~o8-xJ_hT|LO;+J+7TOQN z6?MA2;!WJtpj3vN(70?1ByhUiK);P=L)-PpcAlMeE!IgC+h`@$b!NroUibjo^3qLFsRg5n8x1d@F=8Qp?LKFFrO3O% zcdxJQ1ZO0_1AU`Zlm4=|77?!$?9v zDVu9KeJw%jf@rK_8UzYt6}bsLK+ymY%?PK_wP+2orRz}bV~QfQ%8-f?8p#Yrf@4qs zO$JjLiU97p`4Yjt&VWchWR}cE+BcvFI9Ydm85L3rW;Sj=8L*AyBRm-V!(o8`YTGG_ zy08H3DnIQ%R8Y^3T69;4pDb74_iCmQ5gb5&g0|K1V>M>OO#NvXNgNtp9^785XUAri z(CN_tqohi@TQ2E^I8h?d7AqXw$73VpqjywWgr~Aj%?yc%X&vW+1_&g3S^LBZTd%)- zPk^^DhTDEIVaZAN5gHsbYoNBZa!wzhTOMenb{&u1yYC;2ryO0$mz4lbluy?11rM}z zjmp(HLfd^M!S0DYnlWp)vW{l=3@@nCAYN#Mr2q}IT^>5GDe!|oNg}-A3@lTit6jr2 zfBK^`nrb!OEgTv1kmq!9hue~9lpvTs028S1`gBHB8(WYnbQhvdS8fdko1ifSBD9Rg z{87V1Ht>A}3LvxqJCqn>L1iRJxw!_DLD6lT&FN|886uORnA3orsC#N<&^Mg~r9}NV zpH)~2Ar5qbiF?o^snt9>M_1wpetLwDQN2*2v1lvWNpzQp*yI4I!!B-_bg021n=+J0Bt~0{KPJ+XQ5ld? z04z(=e5yqu6|hbdDdveZ!D@hr585hJp=`$_S)s2$rf*OaYk7p(k*;#pvXYV@=_UYy zi8m0XrHJiWu9ZkO6lAcS%hO6)A4m|iVzW>g7UQ!E1pXPH#^G;NO_1Exr*V;X8-G+b zT|SKivA0$0mu4%)l70J-#K+HmmBg8}On=ZTZMfp=?sE?^`KXpT?~; zQtpP+O1XO91?8OX9==&VE#*m{&z|&oZ2OLuiWY-b7@cbKVc*vhbH99FYpH6ngvIgl zyc&Of_~rXrRMWDv9!OUDWY9h$UUjrS9Q;PZaM5!w6P?%*{eiE`RhzG;T2@b|R$O`d z@(sCtB>pFoqP)9*%l-L}FJHcWxqZ2_-#-ubf9G4(7L#gS**_=O)6M7JR=)lH?el-z zQ~O|D|M}|k^4Ht?f8BgJC?J>b*gO63>4p#3e|P`=ceg*y`tp(bF`p*ndggz|?tqDr z_@7HQp*&sbc=<9PsM>V9q@@4*Yj^4J+vn2_M)~Z~zMoz_p_~7g&(wvFmH=5F{(oWg zpZ~&l>VG@k;N|F@>0I;hm8GHY)sN9>o%#7no=&yF%_?)3_`UunW0AB|`_s{{ zohyHnu}amc{7uG6?I-C^{nx&y)2?;W<#;WBUybLiM(xzDwf@#l>u(jHTg=#r8U-xG ze)@}L>6KS&_br`vEq#}#7pXs<4$74B;sVK-$gVk*$iB*EjRV^*i~Wp?i;9_T~2e@!QvXe|)<8 z%ZaNDe`*%g{|?+m(mX<6{^y@Zr|PCJ)NZ9fBg*Q{_6Sh{oSvJU%mV0*G7X0`}AVkp`)w*^wCG3|KW@O1)oImf&c&j literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Contents.json new file mode 100644 index 000000000..c45b00a1d --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Browser.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Browser.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/Contents.json new file mode 100644 index 000000000..3406b5cf5 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "DAO.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/DAO.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/DAO.imageset/DAO.pdf new file mode 100644 index 0000000000000000000000000000000000000000..53cd6149608ac362645193c2326be75aa53d42d4 GIT binary patch literal 15022 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 literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Contents.json new file mode 100644 index 000000000..4b52ef2bc --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Lock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Lock.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/Lock.imageset/Lock.pdf new file mode 100644 index 0000000000000000000000000000000000000000..59e30d6e5efa88aafa69d9a2fc488fd7472958e9 GIT binary patch literal 9067 zcmeI2O^+P65r*&mEBa!<4zRt=?AO?&a)}yf`XSE~mj0`!yK2NdP zMUHop$cNlKhh5G~vRL0NlJn6wkH5S!QyK;{&g0>SpN4V#^waVBZ@<3XkJsOQ{licD z-3g4(^$SLd)Lo4lgTE{aYQwZCaq}P*?gNPpQC|~Jf)ni zn@6B|vB8^hH{>b$kYX6^GNl;ZvM|nPAEOm0EXG+2WK9U!3FKl*)-iRNmM{mz0-Tq{ zE`D4l6a6wXHBE@-<}gN9b$*3nTpTWn$4qvfSy;vepXeXM6jNT@B9L3GSptyB(Yq*+ z2`noNyUC^{1;#-nB8B*Va>)zir(}%1^K#3{mvY<8iz+CXwmFTN63PuE_$ha<; zL91`HsBf6G*6h$>nn#E%osC{9o#H#S4O+638M~31#aJ=Nkb>{>9qyQsBFsx3v7Qkz z&{o`9-mSgp0Pm6<=|-?Z$KnN`k|V=BdgVxLFFBHtJyv;a7_ogF3mZraWP>!x*uY3N7AC07HjLSea@$6dB!pet&t?}B ze!;r6EjxbYSGlxd<=1!8fE9jYFZPx)XTYkS2B{KzsPf;k?M!x*+6`0PY6=#!#biPv za0wIkN1R_{jLcMLC%kXv9YCi zG({`3i{cD`L`uNdu>xXP(jnm}Ej$2Tq!ZnWJ<1fYN~MGzn#7fg?pOAUg$oPfU5afj z#h5K#Gslcp6?bLk|J_$4-Ba3AvLf@?|yRG|f2IaB2{ zNLAQFbvR7xR5c2vnSyFO0;;$|SIHvLLst+Lug;_X0aTaMz6L1`SkZBvEBxM?S2GgZ z*e0Z$JpOA&(uJ>ZWHzW$^C}3}nOd)RZc50=R!dl9lS3i}Pd-xpNezHR9xqs^DMCnw zO4&}{+lAPVlcySmI??V-VtawfW%gD)mwL*(T&mH^IL4eU2PNv$ilFk!pF%9SOrEnC z?yn#wBr!pbnp#18o;4liXb-u?%<#>oPm!CY03w8pp)wXQMe-OSL>=L&c%umAgGD+L zL1jvvg{htzD=&l^b+P=X%4XYy24$|UDto^n3Om8r#wmo7rABY#sD0`Z6g<+$Can-5 zw$fm#f>+c)6g)YT&pMRdQ!Hq^=~sALG2$W{e>K7tUM_Z=7t}!AP^=ZkE8?M;lJ#qA z0n~n2FV?q!N~;tmb%iU^K$V30sCrWmD%%}c3>(<4NE97hOgrO#9DX{OT-C zDv1PHu=C0fffXpounKig@c3SlJs62Es!~pUM7THrXSYB(NcbDZVQ3DP(R0XSvgQOp z_=*(P4I<(=lIC;B&xv$>HdS+soaot{tRsma4auG}rAbsnQdgS9ead*nenu#A(j+Xa z4{?3PK9Lha*kiS~1aNIboN7qQaw)t^OD#%MiG!kc1Q6v)m$?~2O<&|6>IU;!Q}95t&VB~wZ(jGZ_FmS zbvZ*3OUf0wIw3%s_(Xoe;;KAR6e=q;xkQ*(^%a#8_c9W55zXWnQ9XqX_cKYhpK~Ko zbg}sE4!JbRJ$`FUs@8 zcO*OU>h_B8(pU)>!4Sz7B;iF`eK z*f^iozFj{ZB@pWiO2aRD<~yvH5`7u4ATKqH)OmI0ZEKGi!tQsMc2}^eQU{ z5@EUHLmau8M_1`>t{jpn%PnzwXsv2K#m1mp*6JmP?f-q4N_*Hs1BrH zVO;&E4S;iHv!Pw}KcDPVG$Y!x{dAgySu~b zIDRj`AK+7e5b@2;%Y6mD+P`>x^Lu}ZAe7pV8l+s=eo67@aCh3@v7+p+8~Baj!s4F(fAKjd8ZVxZd z=f1z+|2W76@35vB`8#qAz&#sy{m26H8TlPV`EYoIefcyf@;-15$@T98h&zkljFQ*$ zbBH(Nhmd3-`RIx~KK*vZrKfeezj=MTzaK=se}0T|KRzDb>^~Vle*NN;RmIoO4hQrr h>MQoTe*XGjd(j2TJ)UmvPenVN{NwP^M^C=~^512~`xF2G literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Contents.json new file mode 100644 index 000000000..509ec01f3 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Login.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Login.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/Login.imageset/Login.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f1d3aaf66c02d9c2a8ef0ebfea5c843fb3fc5f28 GIT binary patch literal 19249 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 literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/Contents.json new file mode 100644 index 000000000..762deb793 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "NFT.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/NFT.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/NFT.imageset/NFT.pdf new file mode 100644 index 0000000000000000000000000000000000000000..31794831e9d24804e82ed00131e3eac73bcf8323 GIT binary patch literal 13768 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 Y!hL$aetb6R(Da?>(?=hD{<|;!0brq!_W%F@ literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Contents.json new file mode 100644 index 000000000..30b0cf9c7 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Network.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Network.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/Network.imageset/Network.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ad24230ca1c80c3984b98626cf1d5891a5d500b7 GIT binary patch literal 12677 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 literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Contents.json new file mode 100644 index 000000000..f934274a6 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Noun.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Noun.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/help/Noun.imageset/Noun.pdf new file mode 100644 index 0000000000000000000000000000000000000000..20e4696237bba556236c36224d728479a3087d1b GIT binary patch literal 9725 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

!^A~A2qY{I>dAO%I@1D9fc=HF@^ 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 literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/Contents.json new file mode 100644 index 000000000..92d4d7f9d --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "External Link.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/External Link.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/icons/external_link.imageset/External Link.pdf new file mode 100644 index 0000000000000000000000000000000000000000..58f827c2076fa23962bd97f141b1f9facf83ed1d GIT binary patch literal 1427 zcmZWpOK;RL5Wf3Y%%xI$sN+ZclB!B{OA!J@*>bBmgsj^x+9r^s2>A8PB#x5>A6DKk z^Z4eQ8K2x<-<(VC7?U8N{rZgoTwK8AC5z3D{xY@3i}$g49G*ZUxFxH8=wkKEitG4u zU&rm6E7;tv|J6PF!6X;s0Wn|B^KJD_d7*B66)v0Gn?g0UpM&K$zWv(662LB z*Z}0SfTeNLD^Oe*Qwl+LE{t}Hmv(kdP|X5YIbH$}Y>e$iDOcVUCtFG-iT8qGm?=q3 z&dE@5Gcg<^W>6~cj3H<3wO0<1{^T4u)-x72BSg@oLS`&eR(uR)VTGiOtHK<&%G*-I zVJX&eZ=^=;JP$@_6~JL?6{JK2$$0G$@xqic0G7n5QCd)=kf~G{OYq=biFQ+8(N1t9 zm9ga-A#vTxuVyp{PAi4;#EmzY17--D%7qoydHhD6NKkTZG`193I8J^+AVqJj3=&x& zNgte-D1lJgdX2Q5^Qsgi%A~3z1+m-(sac+dY;`~^jZDV!Tm{n58SMDnTkKvMznVR< z2iCApj20+u$Gl3kHPebY@A877M9bn8ubQSEdbt0Bi-@QA{QG-^;3HT6q`|7Iw z1@}o>9P3GSns1~%t=e*%BW+{fzI1i$fmVH+!fo1Shz(|>J)gnbszVG+f>y5qLHU&^ z-tBit6{vzO%y~BL3;N3c`1gpcBH^O1B2j^TPPi1lsfOy%J|4UN>f!?v;Mi0v{em?( z8Gvv)5>=rCO@=h`Jchi(iTwu(IJ-xX6b`jHhGdEK6Et+y{t!Ec&t~!V8s#|c+vj)& or~BR6BucSu+X1NHQQ+ck|0g2*@%gN}VJt@~Vc5yZ>yI~o0muR~C;$Ke literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/Contents.json new file mode 100644 index 000000000..9797adf2f --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "WCLOGO.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/WCLOGO.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/wc_logo.imageset/WCLOGO.pdf new file mode 100644 index 0000000000000000000000000000000000000000..551f66c99ee576c135e65c9129684a8c289a0770 GIT binary patch literal 46930 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%0DB^Dd(|PN_r2Oip951(H=;3pkV{lh zPkW{Jlz<3Mi92a2xM%E@m(qD!+x6_?a some View { + Button(action: { + withAnimation(.default) { + destination = .welcome + } + }, label: { + Image(systemName: "x.circle") + .foregroundColor(.black) + }) + } +} + +struct Web3ModalSheet_Previews: PreviewProvider { + static var previews: some View { + Web3ModalSheet() + .previewLayout(.sizeThatFits) + .preferredColorScheme(.light) + + Web3ModalSheet() + .previewLayout(.sizeThatFits) + .preferredColorScheme(.dark) + } +} diff --git a/Sources/Web3Modal/WhatIsWalletView.swift b/Sources/Web3Modal/WhatIsWalletView.swift new file mode 100644 index 000000000..515da3691 --- /dev/null +++ b/Sources/Web3Modal/WhatIsWalletView.swift @@ -0,0 +1,98 @@ +import SwiftUI + +struct WhatIsWalletView: View { + + var body: some View { + + VStack(spacing: 10) { + HelpSection( + title: "A home for your digital assets", + description: "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", + assets: ["DeFi", "NFT", "ETH"] + ) + HelpSection( + title: "One login for all of web3", + description: "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", + assets: ["Login", "Profile", "Lock"] + ) + HelpSection( + title: "Your gateway to a new web", + description: "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", + assets: ["Browser", "Noun", "DAO"] + ) + + HStack { + Button(action: {}) { + HStack { + Image("wallet", bundle: .module) + Text("Get a Wallet") + } + } + Button(action: {}) { + HStack { + Text("Learn More") + Image("external_link", bundle: .module) + } + } + } + .buttonStyle(W3MButtonStyle()) + } + .padding(34) + } +} + +extension Color { + static let foreground1 = Color(red: 20/255, green: 20/255, blue: 20/255) + static let foreground2 = Color(red: 121/255, green: 134/255, blue: 134/255) + + static let blueDark = Color(red: 71/255, green: 161/255, blue: 255/255) +} + +struct HelpSection: View { + + let title: String + let description: String + let assets: [String] + + var body: some View { + VStack { + HStack { + ForEach(assets, id: \.self) { asset in + Image(asset, bundle: .module) + } + } + + Text(title) + .font(.system(size: 16)) + .foregroundColor(.foreground1) + .fontWeight(.medium) + .multilineTextAlignment(.center) + Text(description) + .font(.system(size: 14)) + .foregroundColor(.foreground2) + .multilineTextAlignment(.center) + .lineLimit(3, reservesSpace: true) + } + .padding(.top, 10) + .padding(.bottom, 5) + } +} + +struct WhatIsWalletView_Previews: PreviewProvider { + + static var previews: some View { + + WhatIsWalletView() + } +} + +struct W3MButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.blueDark) + .foregroundColor(.white) + .clipShape(Capsule()) + } +} From 1ed570db892a45b087776837b70481ffec5e143b Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Thu, 4 May 2023 12:44:53 +0200 Subject: [PATCH 62/89] Figured out how to present both from UIKit and SwiftUI --- Example/DApp/Auth/AuthView.swift | 62 ++++++----------- Example/ExampleApp.xcodeproj/project.pbxproj | 16 ++--- .../Chat/ChatList/ChatListRouter.swift | 14 ++-- Package.swift | 2 +- Sources/Web3Modal/QRCodeView.swift | 23 ++++--- .../Web3ModalSheetController.swift | 19 ++++++ .../Web3Modal/Web3ModalContainerView.swift | 45 ++++++++++++ Sources/Web3Modal/Web3ModalSheet.swift | 68 +++++++++---------- Sources/Web3Modal/WhatIsWalletView.swift | 1 - 9 files changed, 151 insertions(+), 99 deletions(-) create mode 100644 Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift create mode 100644 Sources/Web3Modal/Web3ModalContainerView.swift diff --git a/Example/DApp/Auth/AuthView.swift b/Example/DApp/Auth/AuthView.swift index 74e17e09b..d3e699ed3 100644 --- a/Example/DApp/Auth/AuthView.swift +++ b/Example/DApp/Auth/AuthView.swift @@ -1,58 +1,40 @@ import SwiftUI -import Web3Modal struct AuthView: View { @ObservedObject var viewModel: AuthViewModel - - @State var showSheet: Bool = false var body: some View { - ZStack(alignment: .bottom) { + VStack(spacing: 16.0) { - VStack(spacing: 16.0) { - - Spacer() - - Image(uiImage: viewModel.qrImage ?? UIImage()) - .interpolation(.none) - .resizable() - .frame(width: 300, height: 300) - - signingLabel() - .frame(maxWidth: .infinity) - - Spacer() - - connectWalletButton() - - Button("Copy URI", action: { viewModel.copyDidPressed() }) - .buttonStyle(CircleButtonStyle()) - - Button("Deeplink", action: { viewModel.deeplinkPressed() }) - .buttonStyle(CircleButtonStyle()) - - - } - .padding(16.0) + Spacer() + + Image(uiImage: viewModel.qrImage ?? UIImage()) + .interpolation(.none) + .resizable() + .frame(width: 300, height: 300) + + signingLabel() + .frame(maxWidth: .infinity) + + Spacer() + + Button("Connect Wallet", action: { }) + .buttonStyle(CircleButtonStyle()) + + Button("Copy URI", action: { viewModel.copyDidPressed() }) + .buttonStyle(CircleButtonStyle()) + + Button("Deeplink", action: { viewModel.deeplinkPressed() }) + .buttonStyle(CircleButtonStyle()) - if showSheet { - Web3ModalSheet() - .transition(.move(edge: .bottom)) - .animation(.easeInOut) - } } + .padding(16.0) .onAppear { Task(priority: .userInitiated) { try await viewModel.setupInitialState() }} } - - private func connectWalletButton() -> some View { - - Button("Connect Wallet", action: { showSheet.toggle() }) - .buttonStyle(CircleButtonStyle()) - } @ViewBuilder private func signingLabel() -> some View { diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 2b0947204..6773f542f 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -272,7 +272,7 @@ CF1A594D29E5876600AAC16B /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594429E5876600AAC16B /* App.swift */; }; CF6704DF29E59DDC003326A4 /* XCUIElementQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704DE29E59DDC003326A4 /* XCUIElementQuery.swift */; }; CF6704E129E5A014003326A4 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704E029E5A014003326A4 /* XCTestCase.swift */; }; - CFFD44C729FFE9BA00D28724 /* Web3Modal in Frameworks */ = {isa = PBXBuildFile; productRef = CFFD44C629FFE9BA00D28724 /* Web3Modal */; }; + CF9C7E4A2A01802F0037C006 /* Web3Modal in Frameworks */ = {isa = PBXBuildFile; productRef = CF9C7E492A01802F0037C006 /* Web3Modal */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -595,7 +595,6 @@ 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, A54195A52934E83F0035AD19 /* Web3 in Frameworks */, 84E6B8652981720400428BAF /* WalletConnectPush in Frameworks */, - CFFD44C729FFE9BA00D28724 /* Web3Modal in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -613,6 +612,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CF9C7E4A2A01802F0037C006 /* Web3Modal in Frameworks */, A58EC618299D665A00F3452A /* Web3Inbox in Frameworks */, A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */, A59FAEC928B7B93A002BB66F /* Web3 in Frameworks */, @@ -1708,7 +1708,6 @@ A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, A54195A42934E83F0035AD19 /* Web3 */, 84E6B8642981720400428BAF /* WalletConnectPush */, - CFFD44C629FFE9BA00D28724 /* Web3Modal */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -1754,6 +1753,7 @@ A59FAEC828B7B93A002BB66F /* Web3 */, A58EC610299D57B800F3452A /* AsyncButton */, A58EC617299D665A00F3452A /* Web3Inbox */, + CF9C7E492A01802F0037C006 /* Web3Modal */, ); productName = Showcase; productReference = A58E7CE828729F550082D443 /* Showcase.app */; @@ -2372,7 +2372,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2430,7 +2430,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = NO; @@ -2497,7 +2497,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2531,7 +2531,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3114,7 +3114,7 @@ isa = XCSwiftPackageProductDependency; productName = Web3Wallet; }; - CFFD44C629FFE9BA00D28724 /* Web3Modal */ = { + CF9C7E492A01802F0037C006 /* Web3Modal */ = { isa = XCSwiftPackageProductDependency; productName = Web3Modal; }; diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift index 85f613e26..30ac0cb60 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift @@ -1,10 +1,12 @@ import UIKit +import SwiftUI +import Web3Modal import WalletConnectChat final class ChatListRouter { weak var viewController: UIViewController! - + private let app: Application init(app: Application) { @@ -12,9 +14,12 @@ final class ChatListRouter { } func presentInvite(account: Account) { - InviteModule.create(app: app, account: account) - .wrapToNavigationController() - .present(from: viewController) +// InviteModule.create(app: app, account: account) +// .wrapToNavigationController() +// .present(from: viewController) + + let modal = Web3ModalSheetController() + modal.present(from: viewController) } func presentReceivedInviteList(account: Account) { @@ -33,3 +38,4 @@ final class ChatListRouter { WelcomeModule.create(app: app).present() } } + diff --git a/Package.swift b/Package.swift index 2b5c17361..ee2cde883 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "WalletConnect", platforms: [ - .iOS(.v13), + .iOS(.v14), .macOS(.v10_15), .tvOS(.v13) ], diff --git a/Sources/Web3Modal/QRCodeView.swift b/Sources/Web3Modal/QRCodeView.swift index b58041575..77bb87ae4 100644 --- a/Sources/Web3Modal/QRCodeView.swift +++ b/Sources/Web3Modal/QRCodeView.swift @@ -7,20 +7,21 @@ struct QRCodeView: View { @Environment(\.colorScheme) var colorScheme: ColorScheme + + var body: some View { - VStack { - render()? - .resizable() - .scaledToFit() -// .padding(.top, 60) -// .padding([.horizontal, .bottom], 20) - + + GeometryReader { g in + VStack(alignment: .center) { + render(frame: g.frame(in: .local)) + } } + .padding(.bottom, 40) } - func render() -> Image? { + func render(frame: CGRect) -> Image? { let doc = QRCode.Document( - utf8String: "jfhaskfhakslfhkljashdfkjahdkljfhakljhfklhkjaglkjdhfjhaskjdfhakjfbajlsdhfkjsfkjahsdfkjlhalkjdhfajskdfkjdhaksjdhakjdhakjsdhkasjhskjdhskjashdkjashda13aefdsadsadsajdkajshdaksjhdskajhsdakjhsakdasjdhkasjdhaksjdhakjdhakjsdhkasjhskjdhskjashdkjashd", + utf8String: Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50).flatMap({ $0 }).shuffled().joined(), errorCorrection: .high ) @@ -37,7 +38,7 @@ struct QRCodeView: View { guard let logo = UIImage(named: "wc_logo", in: .module, with: .none)?.cgImage else { return doc.imageUI( - CGSize(width: 600, height: 600), label: Text("fooo") + frame.size, label: Text("fooo") ) } @@ -50,7 +51,7 @@ struct QRCodeView: View { ) return doc.imageUI( - CGSize(width: 600, height: 600), label: Text("fooo") + frame.size, label: Text("fooo") ) } } diff --git a/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift b/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift new file mode 100644 index 000000000..61b9cb237 --- /dev/null +++ b/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift @@ -0,0 +1,19 @@ +import SwiftUI + +public class Web3ModalSheetController: UIHostingController { + + @MainActor required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public init() { + super.init(rootView: Web3ModalContainerView()) + self.modalTransitionStyle = .crossDissolve + self.modalPresentationStyle = .overFullScreen + } + + public override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = .clear + } +} diff --git a/Sources/Web3Modal/Web3ModalContainerView.swift b/Sources/Web3Modal/Web3ModalContainerView.swift new file mode 100644 index 000000000..ae2a2fb0c --- /dev/null +++ b/Sources/Web3Modal/Web3ModalContainerView.swift @@ -0,0 +1,45 @@ +import SwiftUI + +public struct Web3ModalContainerView: View { + + @Environment(\.presentationMode) var presentationMode + + @State var showModal: Bool = false + + public var body: some View { + + VStack(spacing: 0) { + + Color.black.opacity(0.3) + .onTapGesture { + showModal = false + } + + if showModal { + Web3ModalSheet(destination: .welcome, isShown: $showModal) + .transition(.move(edge: .bottom)) + .animation(.spring(), value: showModal) + } + } + .ignoresSafeArea() + .onChange(of: showModal, perform: { newValue in + if newValue == false { + dismiss() + } + }) + .onAppear { + withAnimation { + showModal = true + } + } + } + + + func dismiss() { + + // Small delay so the sliding transition can happen before cross disolve starts + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + presentationMode.wrappedValue.dismiss() + } + } +} diff --git a/Sources/Web3Modal/Web3ModalSheet.swift b/Sources/Web3Modal/Web3ModalSheet.swift index 1f55b65d6..796d3cc50 100644 --- a/Sources/Web3Modal/Web3ModalSheet.swift +++ b/Sources/Web3Modal/Web3ModalSheet.swift @@ -2,86 +2,86 @@ import SwiftUI public struct Web3ModalSheet: View { - @State var destination: Destination = .welcome + @State public var destination: Destination + @Binding public var isShown: Bool - enum Destination: String { + public enum Destination: String { case welcome case help case qr - - var preferredHeight: CGFloat { - switch self { - case .welcome: - return 300 - case .help: - return 600 - case .qr: - return 400 - } - } } - public init() {} - public var body: some View { ZStack(alignment: .top) { Color.white VStack { - Color.cyan + Color.blue .frame(height: 40) + .overlay( + HStack() { + backButton() + .disabled(destination == .welcome) + + Spacer() + + closeButton() + }.animation(nil), + alignment: .topTrailing + ) switch destination { case .welcome: Button("Help") { - withAnimation(.default) { +// withAnimation(.default) { destination = .help - } +// } } Button("QR") { - withAnimation(.default) { +// withAnimation(.default) { destination = .qr - } +// } } case .help: WhatIsWalletView() - .overlay( - backButton(), - alignment: .topTrailing - ) case .qr: QRCodeView() - .overlay( - backButton(), - alignment: .topTrailing - ) } } } - .frame(height: destination.preferredHeight) + .animation(nil) } func backButton() -> some View { Button(action: { - withAnimation(.default) { - destination = .welcome - } + destination = .welcome + }, label: { + Image(systemName: "chevron.backward") + .foregroundColor(.black) + }) + .padding() + } + + func closeButton() -> some View { + Button(action: { + isShown = false }, label: { Image(systemName: "x.circle") .foregroundColor(.black) }) + .padding() } } struct Web3ModalSheet_Previews: PreviewProvider { static var previews: some View { - Web3ModalSheet() + Web3ModalSheet(destination: .welcome, isShown: .constant(true)) .previewLayout(.sizeThatFits) .preferredColorScheme(.light) - Web3ModalSheet() + Web3ModalSheet(destination: .qr, isShown: .constant(true)) .previewLayout(.sizeThatFits) .preferredColorScheme(.dark) } diff --git a/Sources/Web3Modal/WhatIsWalletView.swift b/Sources/Web3Modal/WhatIsWalletView.swift index 515da3691..847d9b9e7 100644 --- a/Sources/Web3Modal/WhatIsWalletView.swift +++ b/Sources/Web3Modal/WhatIsWalletView.swift @@ -71,7 +71,6 @@ struct HelpSection: View { .font(.system(size: 14)) .foregroundColor(.foreground2) .multilineTextAlignment(.center) - .lineLimit(3, reservesSpace: true) } .padding(.top, 10) .padding(.bottom, 5) From 3c15831dfa9304d0c8892386a7828cabbd802e24 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Thu, 4 May 2023 14:54:23 +0200 Subject: [PATCH 63/89] Cleanup --- .../Chat/ChatList/ChatListRouter.swift | 9 +++------ Package.swift | 2 +- Sources/Web3Modal/Extensions/View+Backport.swift | 15 +++++++++++++++ Sources/Web3Modal/QRCodeView.swift | 6 ++---- Sources/Web3Modal/Web3ModalContainerView.swift | 10 +++++----- Sources/Web3Modal/Web3ModalSheet.swift | 8 ++++---- 6 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 Sources/Web3Modal/Extensions/View+Backport.swift diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift index 30ac0cb60..a1651307d 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift @@ -14,12 +14,9 @@ final class ChatListRouter { } func presentInvite(account: Account) { -// InviteModule.create(app: app, account: account) -// .wrapToNavigationController() -// .present(from: viewController) - - let modal = Web3ModalSheetController() - modal.present(from: viewController) + InviteModule.create(app: app, account: account) + .wrapToNavigationController() + .present(from: viewController) } func presentReceivedInviteList(account: Account) { diff --git a/Package.swift b/Package.swift index ee2cde883..2b5c17361 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "WalletConnect", platforms: [ - .iOS(.v14), + .iOS(.v13), .macOS(.v10_15), .tvOS(.v13) ], diff --git a/Sources/Web3Modal/Extensions/View+Backport.swift b/Sources/Web3Modal/Extensions/View+Backport.swift new file mode 100644 index 000000000..1946cd60c --- /dev/null +++ b/Sources/Web3Modal/Extensions/View+Backport.swift @@ -0,0 +1,15 @@ +import SwiftUI +import Combine + +extension View { + /// A backwards compatible wrapper for iOS 14 `onChange` + @ViewBuilder func onChangeBackported(of value: T, perform: @escaping (T) -> Void) -> some View { + if #available(iOS 14.0, *) { + self.onChange(of: value, perform: perform) + } else { + self.onReceive(Just(value)) { (value) in + perform(value) + } + } + } +} diff --git a/Sources/Web3Modal/QRCodeView.swift b/Sources/Web3Modal/QRCodeView.swift index 77bb87ae4..1051e5c02 100644 --- a/Sources/Web3Modal/QRCodeView.swift +++ b/Sources/Web3Modal/QRCodeView.swift @@ -6,9 +6,7 @@ struct QRCodeView: View { @State var doc: QRCode.Document! @Environment(\.colorScheme) var colorScheme: ColorScheme - - - + var body: some View { GeometryReader { g in @@ -19,7 +17,7 @@ struct QRCodeView: View { .padding(.bottom, 40) } - func render(frame: CGRect) -> Image? { + private func render(frame: CGRect) -> Image? { let doc = QRCode.Document( utf8String: Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50).flatMap({ $0 }).shuffled().joined(), errorCorrection: .high diff --git a/Sources/Web3Modal/Web3ModalContainerView.swift b/Sources/Web3Modal/Web3ModalContainerView.swift index ae2a2fb0c..0583557bb 100644 --- a/Sources/Web3Modal/Web3ModalContainerView.swift +++ b/Sources/Web3Modal/Web3ModalContainerView.swift @@ -21,8 +21,8 @@ public struct Web3ModalContainerView: View { .animation(.spring(), value: showModal) } } - .ignoresSafeArea() - .onChange(of: showModal, perform: { newValue in + .edgesIgnoringSafeArea(.all) + .onChangeBackported(of: showModal, perform: { newValue in if newValue == false { dismiss() } @@ -34,12 +34,12 @@ public struct Web3ModalContainerView: View { } } - - func dismiss() { - + private func dismiss() { // Small delay so the sliding transition can happen before cross disolve starts DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { presentationMode.wrappedValue.dismiss() } } } + + diff --git a/Sources/Web3Modal/Web3ModalSheet.swift b/Sources/Web3Modal/Web3ModalSheet.swift index 796d3cc50..70614ce45 100644 --- a/Sources/Web3Modal/Web3ModalSheet.swift +++ b/Sources/Web3Modal/Web3ModalSheet.swift @@ -34,15 +34,15 @@ public struct Web3ModalSheet: View { case .welcome: Button("Help") { -// withAnimation(.default) { + withAnimation(.default) { destination = .help -// } + } } Button("QR") { -// withAnimation(.default) { + withAnimation(.default) { destination = .qr -// } + } } case .help: WhatIsWalletView() From 12b0601da8999e365f7fa25f23d33c2102a54d07 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Fri, 5 May 2023 10:42:47 +0200 Subject: [PATCH 64/89] Improve animations --- .../Chat/ChatList/ChatListRouter.swift | 2 - Sources/Web3Modal/QRCodeView.swift | 56 +++++------------- .../Contents.json | 0 .../Wallet.pdf | Bin .../Web3Modal/Web3ModalContainerView.swift | 8 ++- Sources/Web3Modal/Web3ModalSheet.swift | 15 +++-- 6 files changed, 32 insertions(+), 49 deletions(-) rename Sources/Web3Modal/Resources/Assets.xcassets/icons/{Wallet.imageset => wallet.imageset}/Contents.json (100%) rename Sources/Web3Modal/Resources/Assets.xcassets/icons/{Wallet.imageset => wallet.imageset}/Wallet.pdf (100%) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift index a1651307d..bd2d8407f 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/ChatList/ChatListRouter.swift @@ -1,6 +1,4 @@ import UIKit -import SwiftUI -import Web3Modal import WalletConnectChat final class ChatListRouter { diff --git a/Sources/Web3Modal/QRCodeView.swift b/Sources/Web3Modal/QRCodeView.swift index 1051e5c02..82b78386c 100644 --- a/Sources/Web3Modal/QRCodeView.swift +++ b/Sources/Web3Modal/QRCodeView.swift @@ -9,48 +9,24 @@ struct QRCodeView: View { var body: some View { - GeometryReader { g in - VStack(alignment: .center) { - render(frame: g.frame(in: .local)) - } - } - .padding(.bottom, 40) - } - - private func render(frame: CGRect) -> Image? { - let doc = QRCode.Document( - utf8String: Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50).flatMap({ $0 }).shuffled().joined(), - errorCorrection: .high - ) - - doc.design.shape.eye = QRCode.EyeShape.Squircle() - doc.design.shape.onPixels = QRCode.PixelShape.Vertical( - insetFraction: 0.2, - cornerRadiusFraction: 1 - ) - - doc.design.style.eye = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) - doc.design.style.pupil = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) - doc.design.style.onPixels = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor) - doc.design.style.background = QRCode.FillStyle.Solid(colorScheme == .light ? UIColor.white.cgColor : UIColor.black.cgColor) - - guard let logo = UIImage(named: "wc_logo", in: .module, with: .none)?.cgImage else { - return doc.imageUI( - frame.size, label: Text("fooo") - ) - } - - doc.logoTemplate = QRCode.LogoTemplate( - image: logo, - path: CGPath( - rect: CGRect(x: 0.35, y: 0.3875, width: 0.30, height: 0.225), - transform: nil + QRCodeViewUI( + content: Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50).flatMap({ $0 }).shuffled().joined(), + foregroundColor: colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor, + backgroundColor: colorScheme == .light ? UIColor.white.cgColor : UIColor.black.cgColor, + pixelStyle: QRCode.PixelShape.Vertical( + insetFraction: 0.2, + cornerRadiusFraction: 1 + ), + eyeStyle: QRCode.EyeShape.Squircle(), + logoTemplate: QRCode.LogoTemplate( + image: (UIImage(named: "wc_logo", in: .module, with: .none)?.cgImage)!, + path: CGPath( + rect: CGRect(x: 0.35, y: 0.3875, width: 0.30, height: 0.225), + transform: nil + ) ) ) - - return doc.imageUI( - frame.size, label: Text("fooo") - ) + .padding(.bottom, 40) } } diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json similarity index 100% rename from Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Contents.json rename to Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Wallet.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Wallet.pdf similarity index 100% rename from Sources/Web3Modal/Resources/Assets.xcassets/icons/Wallet.imageset/Wallet.pdf rename to Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Wallet.pdf diff --git a/Sources/Web3Modal/Web3ModalContainerView.swift b/Sources/Web3Modal/Web3ModalContainerView.swift index 0583557bb..8d77ee4d3 100644 --- a/Sources/Web3Modal/Web3ModalContainerView.swift +++ b/Sources/Web3Modal/Web3ModalContainerView.swift @@ -6,13 +6,19 @@ public struct Web3ModalContainerView: View { @State var showModal: Bool = false + public init() { + + } + public var body: some View { VStack(spacing: 0) { Color.black.opacity(0.3) .onTapGesture { - showModal = false + withAnimation { + showModal = false + } } if showModal { diff --git a/Sources/Web3Modal/Web3ModalSheet.swift b/Sources/Web3Modal/Web3ModalSheet.swift index 70614ce45..d4039f3d8 100644 --- a/Sources/Web3Modal/Web3ModalSheet.swift +++ b/Sources/Web3Modal/Web3ModalSheet.swift @@ -26,7 +26,7 @@ public struct Web3ModalSheet: View { Spacer() closeButton() - }.animation(nil), + }, alignment: .topTrailing ) @@ -34,13 +34,13 @@ public struct Web3ModalSheet: View { case .welcome: Button("Help") { - withAnimation(.default) { + withAnimation { destination = .help } } Button("QR") { - withAnimation(.default) { + withAnimation { destination = .qr } } @@ -51,12 +51,13 @@ public struct Web3ModalSheet: View { } } } - .animation(nil) } func backButton() -> some View { Button(action: { - destination = .welcome + withAnimation { + destination = .welcome + } }, label: { Image(systemName: "chevron.backward") .foregroundColor(.black) @@ -66,7 +67,9 @@ public struct Web3ModalSheet: View { func closeButton() -> some View { Button(action: { - isShown = false + withAnimation { + isShown = false + } }, label: { Image(systemName: "x.circle") .foregroundColor(.black) From 8f6c9066cfc73f5630ebf130bf4905c04da8cc66 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 11 May 2023 11:13:39 +0200 Subject: [PATCH 65/89] fix w3i events --- .../Wallet/Main/MainPresenter.swift | 2 +- .../wc_pushRequest/PushRequestResponder.swift | 6 ++++++ .../Client/Wallet/WalletPushClient.swift | 13 ++++++++++++- .../Client/Wallet/WalletPushClientFactory.swift | 2 +- .../PushClientRequestSubscriber.swift | 2 +- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index 208143f34..0936daa39 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -36,7 +36,7 @@ extension MainPresenter { .receive(on: DispatchQueue.main) .sink { [weak self] request in - self?.router.present(pushRequest: request) +// self?.router.present(pushRequest: request) }.store(in: &disposeBag) interactor.sessionProposalPublisher diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift index cc398a533..cfebe88a3 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift @@ -1,5 +1,6 @@ import WalletConnectNetworking import WalletConnectIdentity +import Combine import Foundation class PushRequestResponder { @@ -17,6 +18,10 @@ class PushRequestResponder { // Keychain shared with UNNotificationServiceExtension in order to decrypt PNs private let groupKeychainStorage: KeychainStorageProtocol + private var subscriptionPublisherSubject = PassthroughSubject, Never>() + var subscriptionPublisher: AnyPublisher, Never> { + return subscriptionPublisherSubject.eraseToAnyPublisher() + } init(keyserverURL: URL, networkingInteractor: NetworkInteracting, @@ -74,6 +79,7 @@ class PushRequestResponder { try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: PushRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) kms.deletePrivateKey(for: keys.publicKey.hexRepresentation) + subscriptionPublisherSubject.send(.success(pushSubscription)) } func respondError(requestId: RPCID) async throws { diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 1422bcc6c..2740cb252 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -11,9 +11,11 @@ public class WalletPushClient { /// publishes new subscriptions public var subscriptionPublisher: AnyPublisher, Never> { - return pushSubscribeResponseSubscriber.subscriptionPublisher + return subscriptionPublisherSubject.eraseToAnyPublisher() } + public var subscriptionPublisherSubject = PassthroughSubject, Never>() + public var subscriptionsPublisher: AnyPublisher<[PushSubscription], Never> { return pushSubscriptionsObserver.subscriptionsPublisher } @@ -144,9 +146,18 @@ private extension WalletPushClient { pushMessageSubscriber.onPushMessage = { [unowned self] pushMessageRecord in pushMessagePublisherSubject.send(pushMessageRecord) } + deletePushSubscriptionSubscriber.onDelete = {[unowned self] topic in deleteSubscriptionPublisherSubject.send(topic) } + + pushSubscribeResponseSubscriber.subscriptionPublisher.sink { [unowned self] result in + subscriptionPublisherSubject.send(result) + }.store(in: &publishers) + + proposeResponder.subscriptionPublisher.sink { [unowned self] result in + subscriptionPublisherSubject.send(result) + }.store(in: &publishers) } } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 3756647ee..adec22410 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -6,7 +6,7 @@ import WalletConnectIdentity public struct WalletPushClientFactory { public static func create(networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient { - let logger = ConsoleLogger(loggingLevel: .debug) + let logger = ConsoleLogger(suffix: "🔔",loggingLevel: .debug) let keyValueStorage = UserDefaults.standard let keyserverURL = URL(string: "https://keys.walletconnect.com")! let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift index 050ac5212..7cb07c45a 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift @@ -45,7 +45,7 @@ final class PushClientRequestSubscriber { client.updateSubscriptionPublisher.sink { [unowned self] record in switch record { case .success(let subscription): - handle(event: .pushSubscription, params: subscription) + handle(event: .pushUpdate, params: subscription) case .failure: //TODO - handle error break From f6fcd05f631849695d2462f96a6cc2405802725e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 11 May 2023 11:24:38 +0200 Subject: [PATCH 66/89] fix ci build --- Sources/Web3Inbox/WebView/WebViewFactory.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift index 5063c35e5..70a784c64 100644 --- a/Sources/Web3Inbox/WebView/WebViewFactory.swift +++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift @@ -29,9 +29,7 @@ final class WebViewFactory { name: WebViewRequestSubscriber.push ) let webview = WKWebView(frame: .zero, configuration: configuration) - if #available(iOS 16.4, *) { - webview.isInspectable = true - } + let request = URLRequest(url: url) webview.load(request) webview.uiDelegate = chatWebviewSubscriber From 3e875fd81be4130c8d5c1296422246355081413e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 May 2023 08:04:44 +0200 Subject: [PATCH 67/89] remove unneeded struct --- Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index 29184d935..ba97def69 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -80,10 +80,6 @@ private extension PushClientProxy { let account: Account } - struct GetActiveSubscriptionsRequest: Codable { - - } - struct GetMessageHistoryRequest: Codable { let topic: String } From 24adad8118f8a8cadf733433d83ff9dc6f4fcaee Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 May 2023 08:44:01 +0200 Subject: [PATCH 68/89] Apply review suggestions --- Sources/WalletConnectJWT/JWTEncoder.swift | 11 +---------- .../Crypto/CryptoKitWrapper/AgreementCryptoKit.swift | 9 +-------- .../NotifyUpdateResponseSubscriber.swift | 4 ++-- Sources/WalletConnectUtils/Extensions/Data.swift | 11 +++++++++++ 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Sources/WalletConnectJWT/JWTEncoder.swift b/Sources/WalletConnectJWT/JWTEncoder.swift index f9a2d5425..fd2ec386f 100644 --- a/Sources/WalletConnectJWT/JWTEncoder.swift +++ b/Sources/WalletConnectJWT/JWTEncoder.swift @@ -13,17 +13,8 @@ struct JWTEncoder { } public static func base64urlDecodedData(string: String) throws -> Data { - var base64 = string - .replacingOccurrences(of: "-", with: "+") - .replacingOccurrences(of: "_", with: "/") - - if base64.count % 4 != 0 { - base64.append(String(repeating: "=", count: 4 - base64.count % 4)) - } - - guard let result = Data(base64Encoded: base64) + guard let result = Data(base64url: string) else { throw JWTError.notBase64String } - return result } } diff --git a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift index 8d60c4f7a..f5e0f906e 100644 --- a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift +++ b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift @@ -38,14 +38,7 @@ public struct AgreementPublicKey: GenericPasswordConvertible, Equatable { } public init(base64url: String) throws { - var base64 = base64url - .replacingOccurrences(of: "-", with: "+") - .replacingOccurrences(of: "_", with: "/") - - if base64.count % 4 != 0 { - base64.append(String(repeating: "=", count: 4 - base64.count % 4)) - } - guard let raw = Data(base64Encoded: base64) else { throw Errors.invalidBase64urlString } + guard let raw = Data(base64url: base64url) else { throw Errors.invalidBase64urlString } try self.init(rawRepresentation: raw) } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 9fa6f5b99..5847d1185 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -39,7 +39,7 @@ class NotifyUpdateResponseSubscriber { let subscriptionTopic = payload.topic let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) - let scope = try await builScope(selected: claims.scp, dappUrl: claims.aud) + let scope = try await buildScope(selected: claims.scp, dappUrl: claims.aud) guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else { logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist") @@ -59,7 +59,7 @@ class NotifyUpdateResponseSubscriber { }.store(in: &publishers) } - private func builScope(selected: String, dappUrl: String) async throws -> [NotificationScope: ScopeValue] { + private func buildScope(selected: String, dappUrl: String) async throws -> [NotificationScope: ScopeValue] { let selectedScope = selected .components(separatedBy: " ") .compactMap { NotificationScope(rawValue: $0) } diff --git a/Sources/WalletConnectUtils/Extensions/Data.swift b/Sources/WalletConnectUtils/Extensions/Data.swift index 88624109f..cb2ffb6c8 100644 --- a/Sources/WalletConnectUtils/Extensions/Data.swift +++ b/Sources/WalletConnectUtils/Extensions/Data.swift @@ -53,4 +53,15 @@ extension Data { public func toHexString() -> String { return map({ String(format: "%02x", $0) }).joined() } + + public init?(base64url: String) { + var base64 = base64url + .replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + + if base64.count % 4 != 0 { + base64.append(String(repeating: "=", count: 4 - base64.count % 4)) + } + self.init(base64Encoded: base64) + } } From e466ea994c722f3210ee96b9fd04ff73097ccf82 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 May 2023 11:46:30 +0200 Subject: [PATCH 69/89] self review --- .../Web3Inbox/Web3InboxViewController.swift | 2 +- .../Web3Inbox/PushClientProxy/PushClientProxy.swift | 1 - Sources/Web3Inbox/Web3Inbox.swift | 2 +- Sources/Web3Inbox/Web3InboxClientFactory.swift | 4 ++-- Sources/Web3Inbox/WebView/WebViewProxy.swift | 10 +++++++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index 9a5a23f1e..b31c33fb0 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -18,7 +18,7 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - Web3Inbox.configure(account: importAccount.account, config: [:], onSign: onSing) + Web3Inbox.configure(account: importAccount.account, onSign: onSing) edgesForExtendedLayout = [] navigationItem.title = "Web3Inbox SDK" diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index ba97def69..c64b22f0d 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -35,7 +35,6 @@ final class PushClientProxy { try await respond(request: request) case .getActiveSubscriptions: let subscriptions = client.getActiveSubscriptions() - print(subscriptions) try await respond(with: subscriptions, request: request) case .getMessageHistory: let params = try parse(GetMessageHistoryRequest.self, params: request.params) diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index bbfd2b89c..591b34546 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -19,7 +19,7 @@ public final class Web3Inbox { /// Web3Inbox instance config method /// - Parameters: /// - account: Web3Inbox initial account - static public func configure(account: Account, config: [ConfigParam: Bool], onSign: @escaping SigningCallback) { + static public func configure(account: Account, config: [ConfigParam: Bool] = [:], onSign: @escaping SigningCallback) { Web3Inbox.account = account Web3Inbox.config = config Web3Inbox.onSign = onSign diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 25609b68d..71b2b4cfc 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -15,8 +15,8 @@ final class Web3InboxClientFactory { let chatWebviewSubscriber = WebViewRequestSubscriber(logger: logger) let pushWebviewSubscriber = WebViewRequestSubscriber(logger: logger) let webView = WebViewFactory(url: url, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create() - let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter()) - let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter()) + let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter(), logger: logger) + let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter(), logger: logger) let clientProxy = ChatClientProxy(client: chatClient, onSign: onSign) let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger) diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift index 1ed94be85..5948b6660 100644 --- a/Sources/Web3Inbox/WebView/WebViewProxy.swift +++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift @@ -5,16 +5,20 @@ actor WebViewProxy { private let webView: WKWebView private let scriptFormatter: WebViewScriptFormatter + private let logger: ConsoleLogging + init(webView: WKWebView, - scriptFormatter: WebViewScriptFormatter) { + scriptFormatter: WebViewScriptFormatter, + logger: ConsoleLogging) { self.webView = webView self.scriptFormatter = scriptFormatter + self.logger = logger } @MainActor func respond(_ response: RPCResponse) async throws { let body = try response.json() - print("resonding to w3i with \(body)") + logger.debug("resonding to w3i with \(body)") let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } @@ -22,7 +26,7 @@ actor WebViewProxy { @MainActor func request(_ request: RPCRequest) async throws { let body = try request.json() - print("requesting w3i with \(body)") + logger.debug("requesting w3i with \(body)") let script = scriptFormatter.formatScript(body: body) webView.evaluateJavaScript(script, completionHandler: nil) } From 1af0e34d2f91d2556abc1f098813e64a9f0af037 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Fri, 12 May 2023 11:50:53 +0200 Subject: [PATCH 70/89] use forked version of QRCode lib --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 2b5c17361..12754c806 100644 --- a/Package.swift +++ b/Package.swift @@ -49,7 +49,7 @@ let package = Package( ], dependencies: [ - .package(url: "https://github.com/dagronf/QRCode", from: "13.9.0") + .package(url: "https://github.com/WalletConnect/QRCode", branch: "main") ], targets: [ .target( From 624fbf099361c695dc587bfcc8507cdb3b8aaaa7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 May 2023 13:47:10 +0200 Subject: [PATCH 71/89] fix showcase app crash, disable notifications tab in a wallet --- .../Web3Inbox/Web3InboxViewController.swift | 2 +- .../Configurator/ThirdPartyConfigurator.swift | 15 +++++++++++++-- .../Wallet/Main/MainPresenter.swift | 1 - .../Wallet/Main/Model/TabPage.swift | 12 ++++++------ .../PushRequest/PushRequestInteractor.swift | 9 +-------- .../Web3Inbox/Web3InboxViewController.swift | 11 +---------- Sources/Web3Inbox/Web3Inbox.swift | 3 ++- 7 files changed, 24 insertions(+), 29 deletions(-) diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift index b31c33fb0..5e1111b53 100644 --- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift +++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift @@ -18,7 +18,7 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - Web3Inbox.configure(account: importAccount.account, onSign: onSing) + Web3Inbox.configure(account: importAccount.account, config: [.pushEnabled: false], onSign: onSing, environment: .sandbox) edgesForExtendedLayout = [] navigationItem.title = "Web3Inbox SDK" diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index dec54cc78..cf6e1ff8b 100644 --- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -1,6 +1,6 @@ import WalletConnectNetworking import Web3Wallet -import WalletConnectPush +import Web3Inbox struct ThirdPartyConfigurator: Configurator { @@ -15,9 +15,20 @@ struct ThirdPartyConfigurator: Configurator { ) Web3Wallet.configure(metadata: metadata, crypto: DefaultCryptoProvider(), environment: BuildConfiguration.shared.apnsEnvironment) - Push.configure(environment: BuildConfiguration.shared.apnsEnvironment) + + let account = Account(blockchain: Blockchain("eip155:1")!, address: EthKeyStore.shared.address)! + + Web3Inbox.configure(account: account, config: [.chatEnabled: false, .settingsEnabled: false], onSign: Web3InboxSigner.onSing, environment: BuildConfiguration.shared.apnsEnvironment) } } +class Web3InboxSigner { + static func onSing(_ message: String) -> SigningResult { + let privateKey = EthKeyStore.shared.privateKeyRaw + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() + let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) + return .signed(signature) + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift index 0936daa39..5c09b83b0 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift @@ -14,7 +14,6 @@ final class MainPresenter { var viewControllers: [UIViewController] { return [ router.walletViewController(), - router.notificationsViewController(), router.web3InboxViewController() ] } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift index 698349ebd..d195c90ae 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift @@ -2,15 +2,15 @@ import UIKit enum TabPage: CaseIterable { case wallet - case notifications +// case notifications case web3Inbox var title: String { switch self { case .wallet: return "Apps" - case .notifications: - return "Notifications" +// case .notifications: +// return "Notifications" case .web3Inbox: return "w3i" } @@ -20,8 +20,8 @@ enum TabPage: CaseIterable { switch self { case .wallet: return UIImage(systemName: "house.fill")! - case .notifications: - return UIImage(systemName: "bell.fill")! +// case .notifications: +// return UIImage(systemName: "bell.fill")! case .web3Inbox: return UIImage(systemName: "bell.fill")! } @@ -32,6 +32,6 @@ enum TabPage: CaseIterable { } static var enabledTabs: [TabPage] { - return [.wallet, .notifications, .web3Inbox] + return [.wallet, .web3Inbox] } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift index 987c7920c..2afda2ff4 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -3,17 +3,10 @@ import WalletConnectPush final class PushRequestInteractor { func approve(pushRequest: PushRequest) async throws { - try await Push.wallet.approve(id: pushRequest.id, onSign: onSing(_:)) + try await Push.wallet.approve(id: pushRequest.id, onSign: Web3InboxSigner.onSing) } func reject(pushRequest: PushRequest) async throws { try await Push.wallet.reject(id: pushRequest.id) } - - func onSing(_ message: String) async -> SigningResult { - let privateKey = EthKeyStore.shared.privateKeyRaw - let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() - let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) - return .signed(signature) - } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift index 868c2f7c1..e4043cca5 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift @@ -15,8 +15,6 @@ final class Web3InboxViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - let account = Account(blockchain: Blockchain("eip155:1")!, address: EthKeyStore.shared.address)! - Web3Inbox.configure(account: account, config: [.chatEnabled: false, .settingsEnabled: false], onSign: onSing) edgesForExtendedLayout = [] navigationItem.title = "Web3Inbox SDK" @@ -25,12 +23,5 @@ final class Web3InboxViewController: UIViewController { } } -private extension Web3InboxViewController { - func onSing(_ message: String) -> SigningResult { - let privateKey = EthKeyStore.shared.privateKeyRaw - let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() - let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) - return .signed(signature) - } -} + diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift index 591b34546..32a1a87fb 100644 --- a/Sources/Web3Inbox/Web3Inbox.swift +++ b/Sources/Web3Inbox/Web3Inbox.swift @@ -19,10 +19,11 @@ public final class Web3Inbox { /// Web3Inbox instance config method /// - Parameters: /// - account: Web3Inbox initial account - static public func configure(account: Account, config: [ConfigParam: Bool] = [:], onSign: @escaping SigningCallback) { + static public func configure(account: Account, config: [ConfigParam: Bool] = [:], onSign: @escaping SigningCallback, environment: APNSEnvironment) { Web3Inbox.account = account Web3Inbox.config = config Web3Inbox.onSign = onSign Chat.configure() + Push.configure(environment: environment) } } From 8241819a5a0a57a532aa9d561e235189bb87c4c1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 May 2023 13:48:55 +0200 Subject: [PATCH 72/89] apply review suggestion --- Sources/Web3Inbox/Web3InboxClientFactory.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift index 71b2b4cfc..50d255e29 100644 --- a/Sources/Web3Inbox/Web3InboxClientFactory.swift +++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift @@ -41,11 +41,7 @@ final class Web3InboxClientFactory { private static func buildUrl(account: Account, config: [ConfigParam: Bool]) -> URL { var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden.vercel.app/")! - let defaultQueryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios")] - let accountQueryItem = URLQueryItem(name: "account", value: account.address) - var queryItems = [URLQueryItem]() - queryItems.append(accountQueryItem) - queryItems.append(contentsOf: defaultQueryItems) + var queryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios"), URLQueryItem(name: "account", value: account.address)] for param in config.filter({ $0.value == false}) { queryItems.append(URLQueryItem(name: "\(param.key)", value: "false")) From 742a501a87101801389e35e427b0a3695812423d Mon Sep 17 00:00:00 2001 From: llbartekll Date: Mon, 15 May 2023 07:11:50 +0000 Subject: [PATCH 73/89] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 19e7a1501..90a323c7b 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.5.15"} +{"version": "1.6.0"} From 84f489d1b1a5c5efcbe8ad42998377e8fb5473a4 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Mon, 15 May 2023 15:09:35 +0200 Subject: [PATCH 74/89] Add color and icon assets --- .../Assets.xcassets/colors/Contents.json | 6 +++ .../colors/accent.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/background1.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/background2.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/background3.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/foreground1.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/foreground2.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/foreground3.colorset/Contents.json | 38 ++++++++++++++++++ .../foregroundInverse.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/negative.colorset/Contents.json | 38 ++++++++++++++++++ .../thickOverlay.colorset/Contents.json | 38 ++++++++++++++++++ .../colors/thinOverlay.colorset/Contents.json | 38 ++++++++++++++++++ .../icons/close.imageset/Close.pdf | Bin 0 -> 1642 bytes .../icons/close.imageset/Contents.json | 15 +++++++ .../external_link.imageset/Contents.json | 3 ++ .../icons/help.imageset/Contents.json | 15 +++++++ .../icons/help.imageset/Help.pdf | Bin 0 -> 1879 bytes .../Assets.xcassets/icons/large/Contents.json | 6 +++ .../large/copy_large.imageset/Contents.json | 15 +++++++ .../icons/large/copy_large.imageset/Copy.pdf | Bin 0 -> 4296 bytes .../large/qr_large.imageset/Contents.json | 15 +++++++ .../icons/large/qr_large.imageset/QRCode.pdf | Bin 0 -> 7870 bytes .../icons/wallet.imageset/Contents.json | 3 ++ .../walletconnect_logo.imageset/Contents.json | 12 ++++++ .../LogoLockup.pdf | Bin 0 -> 9907 bytes 25 files changed, 508 insertions(+) create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/accent.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/background1.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/background2.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/background3.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground1.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground2.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground3.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/foregroundInverse.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/negative.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/thickOverlay.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/colors/thinOverlay.colorset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/close.imageset/Close.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/close.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/help.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/help.imageset/Help.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/large/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Copy.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/large/qr_large.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/icons/large/qr_large.imageset/QRCode.pdf create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/Contents.json create mode 100644 Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/LogoLockup.pdf diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/accent.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/accent.colorset/Contents.json new file mode 100644 index 000000000..7157df412 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/accent.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "255", + "green" : "150", + "red" : "51" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xA1", + "red" : "0x47" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/background1.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background1.colorset/Contents.json new file mode 100644 index 000000000..855c60979 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x14", + "green" : "0x14", + "red" : "0x14" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/background2.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background2.colorset/Contents.json new file mode 100644 index 000000000..c32b826d0 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF3", + "green" : "0xF3", + "red" : "0xF1" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x2A", + "green" : "0x2A", + "red" : "0x27" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/background3.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background3.colorset/Contents.json new file mode 100644 index 000000000..bd44d87a7 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/background3.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE7", + "green" : "0xE7", + "red" : "0xE4" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x40", + "green" : "0x40", + "red" : "0x3B" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground1.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground1.colorset/Contents.json new file mode 100644 index 000000000..3142df2de --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground1.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x14", + "green" : "0x14", + "red" : "0x14" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE7", + "green" : "0xE7", + "red" : "0xE4" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground2.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground2.colorset/Contents.json new file mode 100644 index 000000000..530bc2ce5 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground2.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x86", + "green" : "0x86", + "red" : "0x79" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x9E", + "green" : "0x9E", + "red" : "0x94" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground3.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground3.colorset/Contents.json new file mode 100644 index 000000000..93ad845ea --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foreground3.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xA9", + "green" : "0xA9", + "red" : "0x9E" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x77", + "green" : "0x77", + "red" : "0x6E" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/foregroundInverse.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foregroundInverse.colorset/Contents.json new file mode 100644 index 000000000..2536dc2d1 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/foregroundInverse.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/negative.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/negative.colorset/Contents.json new file mode 100644 index 000000000..01cebdc26 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/negative.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x42", + "green" : "0x51", + "red" : "0xF0" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0x67", + "green" : "0x5A", + "red" : "0xF2" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/thickOverlay.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/thickOverlay.colorset/Contents.json new file mode 100644 index 000000000..dd489ec2c --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/thickOverlay.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/colors/thinOverlay.colorset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/colors/thinOverlay.colorset/Contents.json new file mode 100644 index 000000000..bac5e9b1a --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/colors/thinOverlay.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.100", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.100", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/close.imageset/Close.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/icons/close.imageset/Close.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f29dd2ed6cb8c92580a2f8ddf0b98ff84ce4a62a GIT binary patch literal 1642 zcmZWqOK;Oa5WerPm`kPh(Dl9_QdNnj6d^#Alv~BYIBpx1*buv@@avhe*K0TU5Us!L zjK9Zvd~$nzb1u1OOoD*+`ws?iaRHZ?tZp`R%hVDtKGw}+{{$MrHSOy9LtXA!ab5r3 zR`vSb6|C--cU8xJG0DYnftVKOdA0b@2cfmWO-}0&BvkvkGH=U^u4-81(ncW#)@bLX zbg)OvSQ!P<=E7^GG(;Yi^WM#f6kN&ZT$~FlhM<+oI@HD;wiOX(NODP}G(o9Uazkw` zRLqT(Jy52-+C5 z5t*B!6)BZwNp5sRIP949<3?vgj1jvela>&9If^DThf>QedFYWTrK1dTv0Qr}k*ni2 zSb=__YA1D2Gh%qa7K7$ghr+m`fF-dU(z4i0@EE+&p1{HMfbORlmJoUF2hl0$iqRPw z1ICTRO2LdJpD5u}JKAZ%{E)ju|?eUMa*prF0#tvbEes$j3gtcQ$GZi440sJv@%3GfhiUZhD+n6lPE1kmk=@_1)S^X zyd1w(JNCpLSi`ubw(08zpGf0t0&mL$Vqg=r z7;*&V;a7avZjdUFf;CLfS+&ntEC1u)A+qR%Q)bbL1a|p>v+z~fm%H}yb?M8YeqaJT z8|Bk4IBT^9gwsW{jxK02L?iPRBt=9kUPBV6!x5x{KZZ1(k!e?dD7U-%!0>BOyuU^} tPP=wrpTX&Nb2ge%tlPE+5UI8jRZIzg^OMTA$GcIFjcX#Tm!B=6Q%(Cf)vfFpZAhWQA!GiWm(ejHc1> zfI)5UUtvSK5Yn&*cE?)w<-b2CiF%m-{hn4*pk~oZ1v~5{4u{4Ma)IJ#A9|88qhoLZ zr)R<<_hxj?GR>f{5tTy0rg$MwZcajkR|Jh6wb5adW>8H-wTiq%TSAm|2Iul{KuWlo z+?;pDW~uWIEDLVhhI#QFwxm=#(2_I+-s(w;8WUc z>+|jzZs%`-#vLmul1ZACDbhOi-P68FJ>+%0F5xb^c1SH|%)5F5uj@TxU<0M8Cw^Dj7S zwFQLBkpjjiXbI9-XFdesZVIm;i|V|zvg(zijkDvhueXnB&vN(PU!okxeYZ;|aJ=1| l97L(sT{l1`JPKUhZ2u;#e*6op_rpjB_lRdlM{hr0{R6iQhP40y literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Contents.json new file mode 100644 index 000000000..7cd8eaf7e --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Copy.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Copy.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/icons/large/copy_large.imageset/Copy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ada796931658ce4b73930477a99607bb0468738b GIT binary patch literal 4296 zcmZu#%Z^(`5Z&Le=uM;qDeZngq$m=Z5QG2`ChQ^>#&eSlnz6xlP~hu1)sO17Vy}A6 z?MKzAs?*i;&$W!pqJ-KTkyk7sg->tXbzY;g^=D&8Q2a`sm2mME2R);eL7G+ITh zh%%;ZY$Pa0A3_jGMyr$qq^0v-S%aln#+-u+!kF%))>7vjZJIIHzm9pkMF*LbGB|Fa zFx4n!GmIHKvaR#c!`NZt#i+S5*33RnyI(u;nq}<6ZkbkJ@ph2HOfh*_%Z$z>Gw&@F z!`2qE1h#7&4d`y6MI39ig9bzipb_nw6}do^C={pyCW<%a;fDne1@ze^C!BPl%*4Fp zwZjosdXWF(%e*<%IL773NT(6(opLgIO4GA%Vd#I+n? zidH=n7)m{37}+hpXBK0&=N}`*M&%N!Zk*V&c^KAHo~@_pr7|Wb>!wP93rcg84I|YQ zc+E&P7|!e##;RY|KsdTP)q471COh;#e0@_gOlv71SuolptUc3sFAa zxFSMENBW4OrGX_`s-Ji|Z$Qe%oANR3;Mo>m4IDPpO0TLT7rqe*;u-_YR9#x

T{# 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 P@TqP&yn6M!zkK;$0Tvk3 literal 0 HcmV?d00001 diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json index 20ace14c5..5e974e19c 100644 --- a/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json +++ b/Sources/Web3Modal/Resources/Assets.xcassets/icons/wallet.imageset/Contents.json @@ -8,5 +8,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" } } diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/Contents.json b/Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/Contents.json new file mode 100644 index 000000000..cfe764035 --- /dev/null +++ b/Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LogoLockup.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/LogoLockup.pdf b/Sources/Web3Modal/Resources/Assets.xcassets/walletconnect_logo.imageset/LogoLockup.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1f0698299f1ea9b50fb582503e1465c749335456 GIT binary patch literal 9907 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 T&#&z`G*aU3lTUvA$8Y`%kqV$s literal 0 HcmV?d00001 From 7c087e1bc7582f9d6f96b2f9c0ebf3857bd97188 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Tue, 16 May 2023 16:41:11 +0200 Subject: [PATCH 75/89] Add Verify as a dependency --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 72dc4fdcd..5c63323d5 100644 --- a/Package.swift +++ b/Package.swift @@ -62,7 +62,7 @@ let package = Package( path: "Sources/Chat"), .target( name: "Auth", - dependencies: ["WalletConnectPairing", "WalletConnectSigner"], + dependencies: ["WalletConnectPairing", "WalletConnectSigner", "WalletConnectVerify"], path: "Sources/Auth"), .target( name: "Web3Wallet", From d1b42755349d0039a25dd47c50fcec5ed6e0c63f Mon Sep 17 00:00:00 2001 From: alexander-lsvk Date: Tue, 16 May 2023 14:55:35 +0000 Subject: [PATCH 76/89] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 90a323c7b..932ed438f 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.6.0"} +{"version": "1.6.1"} From b3f546876bdbcfe2a8aec1d413205c75f453243b Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Tue, 16 May 2023 17:53:20 +0200 Subject: [PATCH 77/89] Add verify cocoapods support --- Sources/Auth/Auth.swift | 2 -- Sources/Auth/AuthClientFactory.swift | 2 -- Sources/Auth/AuthImports.swift | 1 + .../Auth/Services/Wallet/WalletRequestSubscriber.swift | 2 -- .../WalletConnectSign/Engine/Common/ApproveEngine.swift | 2 -- .../WalletConnectSign/Engine/Common/SessionEngine.swift | 2 -- Sources/WalletConnectSign/Sign/Sign.swift | 2 -- Sources/WalletConnectSign/Sign/SignClientFactory.swift | 2 -- Sources/WalletConnectSign/Sign/SignImports.swift | 1 + Sources/Web3Wallet/Web3Wallet.swift | 2 -- Sources/Web3Wallet/Web3WalletImports.swift | 1 + WalletConnectSwiftV2.podspec | 9 +++++++++ 12 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 8240dff20..e613091da 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - public typealias VerifyContext = WalletConnectVerify.VerifyContext /// Auth instatnce wrapper /// diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 3b4a430be..66f4d7b34 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -1,7 +1,5 @@ import Foundation -import WalletConnectVerify - public struct AuthClientFactory { public static func create( diff --git a/Sources/Auth/AuthImports.swift b/Sources/Auth/AuthImports.swift index f27efa95c..91463cad3 100644 --- a/Sources/Auth/AuthImports.swift +++ b/Sources/Auth/AuthImports.swift @@ -1,4 +1,5 @@ #if !CocoaPods @_exported import WalletConnectPairing @_exported import WalletConnectSigner +@_exported import WalletConnectVerify #endif diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 84640faf9..18cda08d3 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index f56ffd908..acb93af8b 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - final class ApproveEngine { enum Errors: Error { case wrongRequestParams diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 27aae0941..76dc4dc10 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - final class SessionEngine { enum Errors: Error { case sessionNotFound(topic: String) diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 40ba5b16a..b4935c681 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - public typealias VerifyContext = WalletConnectVerify.VerifyContext /// Sign instatnce wrapper diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index ecacb5e6e..8527c5776 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -1,7 +1,5 @@ import Foundation -import WalletConnectVerify - public struct SignClientFactory { /// Initializes and returns newly created WalletConnect Client Instance diff --git a/Sources/WalletConnectSign/Sign/SignImports.swift b/Sources/WalletConnectSign/Sign/SignImports.swift index 27245bda6..b60ea566a 100644 --- a/Sources/WalletConnectSign/Sign/SignImports.swift +++ b/Sources/WalletConnectSign/Sign/SignImports.swift @@ -1,3 +1,4 @@ #if !CocoaPods @_exported import WalletConnectPairing +@_exported import WalletConnectVerify #endif diff --git a/Sources/Web3Wallet/Web3Wallet.swift b/Sources/Web3Wallet/Web3Wallet.swift index ffdf09f16..fdc122e76 100644 --- a/Sources/Web3Wallet/Web3Wallet.swift +++ b/Sources/Web3Wallet/Web3Wallet.swift @@ -1,8 +1,6 @@ import Foundation import Combine -import WalletConnectVerify - public typealias VerifyContext = WalletConnectVerify.VerifyContext /// Web3Wallet instance wrapper /// diff --git a/Sources/Web3Wallet/Web3WalletImports.swift b/Sources/Web3Wallet/Web3WalletImports.swift index 03faad8ad..c27041056 100644 --- a/Sources/Web3Wallet/Web3WalletImports.swift +++ b/Sources/Web3Wallet/Web3WalletImports.swift @@ -2,4 +2,5 @@ @_exported import Auth @_exported import WalletConnectSign @_exported import WalletConnectEcho +@_exported import WalletConnectVerify #endif diff --git a/WalletConnectSwiftV2.podspec b/WalletConnectSwiftV2.podspec index f87cf006f..534b4cabd 100644 --- a/WalletConnectSwiftV2.podspec +++ b/WalletConnectSwiftV2.podspec @@ -77,12 +77,14 @@ Pod::Spec.new do |spec| spec.subspec 'WalletConnect' do |ss| ss.source_files = 'Sources/WalletConnectSign/**/*.{h,m,swift}' ss.dependency 'WalletConnectSwiftV2/WalletConnectPairing' + ss.dependency 'WalletConnectSwiftV2/WalletConnectVerify' end spec.subspec 'WalletConnectAuth' do |ss| ss.source_files = 'Sources/Auth/**/*.{h,m,swift}' ss.dependency 'WalletConnectSwiftV2/WalletConnectPairing' ss.dependency 'WalletConnectSwiftV2/WalletConnectSigner' + ss.dependency 'WalletConnectSwiftV2/WalletConnectVerify' end spec.subspec 'Web3Wallet' do |ss| @@ -90,6 +92,13 @@ Pod::Spec.new do |spec| ss.dependency 'WalletConnectSwiftV2/WalletConnect' ss.dependency 'WalletConnectSwiftV2/WalletConnectAuth' ss.dependency 'WalletConnectSwiftV2/WalletConnectEcho' + ss.dependency 'WalletConnectSwiftV2/WalletConnectVerify' + end + + spec.subspec 'WalletConnectVerify' do |ss| + ss.source_files = 'Sources/WalletConnectVerify/**/*.{h,m,swift}' + ss.dependency 'WalletConnectSwiftV2/WalletConnectUtils' + ss.dependency 'WalletConnectSwiftV2/WalletConnectNetworking' end spec.subspec 'WalletConnectChat' do |ss| From 43d3a4e3e7861454293815b71e486f61e5ee6f26 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Tue, 16 May 2023 18:58:48 +0200 Subject: [PATCH 78/89] UI improvements + Sign integration --- .../Chat/Import/ImportPresenter.swift | 5 + .../Chat/Import/ImportRouter.swift | 15 ++ .../Chat/Import/ImportView.swift | 11 +- Package.swift | 2 +- Sources/WalletConnectRelay/BundleFinder.swift | 11 +- Sources/Web3Modal/Extensions/Color.swift | 58 +++++ .../Extensions/View+RoundedCorners.swift | 18 ++ .../ModalContainerView.swift} | 31 ++- Sources/Web3Modal/Modal/ModalInteractor.swift | 47 ++++ .../Web3Modal/Modal/ModalSheet+Previews.swift | 51 +++++ Sources/Web3Modal/Modal/ModalSheet.swift | 210 ++++++++++++++++++ Sources/Web3Modal/Modal/ModalViewModel.swift | 86 +++++++ .../{ => Modal/Screens}/QRCodeView.swift | 30 ++- .../Screens}/WhatIsWalletView.swift | 21 +- Sources/Web3Modal/Styles/Button.swift | 44 ++++ Sources/Web3Modal/UI/ActivityIndicator.swift | 14 ++ Sources/Web3Modal/UI/AsyncImage.swift | 56 +++++ .../Web3ModalSheetController.swift | 18 +- Sources/Web3Modal/Web3ModalSheet.swift | 91 -------- 19 files changed, 678 insertions(+), 141 deletions(-) create mode 100644 Sources/Web3Modal/Extensions/Color.swift create mode 100644 Sources/Web3Modal/Extensions/View+RoundedCorners.swift rename Sources/Web3Modal/{Web3ModalContainerView.swift => Modal/ModalContainerView.swift} (56%) create mode 100644 Sources/Web3Modal/Modal/ModalInteractor.swift create mode 100644 Sources/Web3Modal/Modal/ModalSheet+Previews.swift create mode 100644 Sources/Web3Modal/Modal/ModalSheet.swift create mode 100644 Sources/Web3Modal/Modal/ModalViewModel.swift rename Sources/Web3Modal/{ => Modal/Screens}/QRCodeView.swift (57%) rename Sources/Web3Modal/{ => Modal/Screens}/WhatIsWalletView.swift (79%) create mode 100644 Sources/Web3Modal/Styles/Button.swift create mode 100644 Sources/Web3Modal/UI/ActivityIndicator.swift create mode 100644 Sources/Web3Modal/UI/AsyncImage.swift delete mode 100644 Sources/Web3Modal/Web3ModalSheet.swift diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift index 114130e41..9987116c2 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportPresenter.swift @@ -15,6 +15,11 @@ final class ImportPresenter: ObservableObject { self.router = router } + @MainActor + func didPressWeb3Modal() async throws { + router.presentWeb3Modal() + } + @MainActor func didPressImport() async throws { guard let importAccount = ImportAccount(input: input) diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift index 503509d36..512dc95da 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportRouter.swift @@ -1,4 +1,6 @@ import UIKit +import Web3Modal +import WalletConnectPairing final class ImportRouter { @@ -9,6 +11,19 @@ final class ImportRouter { init(app: Application) { self.app = app } + + func presentWeb3Modal() { + Web3ModalSheetController( + projectId: InputConfig.projectId, + metadata: AppMetadata( + name: "Showcase App", + description: "Showcase description", + url: "example.wallet", + icons: ["https://avatars.githubusercontent.com/u/37784886"] + ), + webSocketFactory: DefaultSocketFactory() + ).present(from: viewController) + } func presentChat(importAccount: ImportAccount) { MainModule.create(app: app, importAccount: importAccount).present() diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift index 969c66834..a7ab737cf 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift @@ -15,8 +15,15 @@ struct ImportView: View { Spacer() - BrandButton(title: "Ok, done" ) { - try await presenter.didPressImport() + VStack { + + BrandButton(title: "Web3Modal") { + try await presenter.didPressWeb3Modal() + } + + BrandButton(title: "Ok, done" ) { + try await presenter.didPressImport() + } } .padding(16.0) } diff --git a/Package.swift b/Package.swift index 5c63323d5..de8696965 100644 --- a/Package.swift +++ b/Package.swift @@ -120,7 +120,7 @@ let package = Package( dependencies: ["WalletConnectUtils", "WalletConnectNetworking"]), .target( name: "Web3Modal", - dependencies: ["QRCode"]), + dependencies: ["QRCode", "WalletConnectSign"]), .testTarget( name: "WalletConnectSignTests", dependencies: ["WalletConnectSign", "WalletConnectUtils", "TestingUtils", "WalletConnectVerify"]), diff --git a/Sources/WalletConnectRelay/BundleFinder.swift b/Sources/WalletConnectRelay/BundleFinder.swift index 90abe6696..b237dbb00 100644 --- a/Sources/WalletConnectRelay/BundleFinder.swift +++ b/Sources/WalletConnectRelay/BundleFinder.swift @@ -15,7 +15,16 @@ extension Foundation.Bundle { Bundle(for: BundleFinder.self).resourceURL, // For command-line tools. - Bundle.main.bundleURL + Bundle.main.bundleURL, + + // One of these should be used when building SwiftUI Previews + Bundle(for: BundleFinder.self).resourceURL? + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent(), + Bundle(for: BundleFinder.self).resourceURL? + .deletingLastPathComponent() + .deletingLastPathComponent() ] for candidate in candidates { diff --git a/Sources/Web3Modal/Extensions/Color.swift b/Sources/Web3Modal/Extensions/Color.swift new file mode 100644 index 000000000..84e687266 --- /dev/null +++ b/Sources/Web3Modal/Extensions/Color.swift @@ -0,0 +1,58 @@ +import SwiftUI + +extension Color { + static let foreground1 = Color("foreground1", bundle: .module) + static let foreground2 = Color("foreground2", bundle: .module) + static let foreground3 = Color("foreground3", bundle: .module) + static let foregroundInverse = Color("foregroundInverse", bundle: .module) + static let background1 = Color("background1", bundle: .module) + static let background2 = Color("background2", bundle: .module) + static let background3 = Color("background3", bundle: .module) + static let negative = Color("negative", bundle: .module) + static let thickOverlay = Color("thickOverlay", bundle: .module) + static let thinOverlay = Color("thinOverlay", bundle: .module) + static let accent = Color("accent", bundle: .module) +} + +@available(iOS 15.0, *) +struct Color_Previews: PreviewProvider { + static var allColors: [(String, Color)] { + [ + ("foreground1", Color("foreground1", bundle: .module)), + ("foreground2", Color("foreground2", bundle: .module)), + ("foreground3", Color("foreground3", bundle: .module)), + ("foregroundInverse", Color("foregroundInverse", bundle: .module)), + ("background1", Color("background1", bundle: .module)), + ("background2", Color("background2", bundle: .module)), + ("background3", Color("background3", bundle: .module)), + ("negative", Color("negative", bundle: .module)), + ("thickOverlay", Color("thickOverlay", bundle: .module)), + ("thinOverlay", Color("thinOverlay", bundle: .module)), + ("accent", Color("accent", bundle: .module)), + ] + } + + static var previews: some View { + VStack { + let columns = [ + GridItem(.adaptive(minimum: 150)), + ] + + LazyVGrid(columns: columns, alignment: .leading) { + ForEach(allColors, id: \.1) { name, color in + + VStack(alignment: .leading) { + RoundedRectangle(cornerRadius: 12) + .fill(color) + .frame(width: 62, height: 62) + + Text(name).bold() + } + .font(.footnote) + } + } + } + .padding() + .previewLayout(.sizeThatFits) + } +} diff --git a/Sources/Web3Modal/Extensions/View+RoundedCorners.swift b/Sources/Web3Modal/Extensions/View+RoundedCorners.swift new file mode 100644 index 000000000..74265563b --- /dev/null +++ b/Sources/Web3Modal/Extensions/View+RoundedCorners.swift @@ -0,0 +1,18 @@ +import SwiftUI + +extension View { + func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { + clipShape( RoundedCorner(radius: radius, corners: corners) ) + } +} + +struct RoundedCorner: Shape { + + var radius: CGFloat = .infinity + var corners: UIRectCorner = .allCorners + + func path(in rect: CGRect) -> Path { + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) + return Path(path.cgPath) + } +} diff --git a/Sources/Web3Modal/Web3ModalContainerView.swift b/Sources/Web3Modal/Modal/ModalContainerView.swift similarity index 56% rename from Sources/Web3Modal/Web3ModalContainerView.swift rename to Sources/Web3Modal/Modal/ModalContainerView.swift index 8d77ee4d3..312120dbf 100644 --- a/Sources/Web3Modal/Web3ModalContainerView.swift +++ b/Sources/Web3Modal/Modal/ModalContainerView.swift @@ -1,20 +1,28 @@ import SwiftUI +import WalletConnectPairing -public struct Web3ModalContainerView: View { - +public struct ModalContainerView: View { + @Environment(\.presentationMode) var presentationMode @State var showModal: Bool = false - public init() { - + let projectId: String + let metadata: AppMetadata + let webSocketFactory: WebSocketFactory + + public init(projectId: String, metadata: AppMetadata, webSocketFactory: WebSocketFactory) { + self.projectId = projectId + self.metadata = metadata + self.webSocketFactory = webSocketFactory } public var body: some View { VStack(spacing: 0) { - Color.black.opacity(0.3) + Color.thickOverlay + .colorScheme(.light) .onTapGesture { withAnimation { showModal = false @@ -22,7 +30,12 @@ public struct Web3ModalContainerView: View { } if showModal { - Web3ModalSheet(destination: .welcome, isShown: $showModal) + ModalSheet( + viewModel: .init( + isShown: $showModal, + projectId: projectId, + interactor: .init(projectId: projectId, metadata: metadata, webSocketFactory: webSocketFactory) + )) .transition(.move(edge: .bottom)) .animation(.spring(), value: showModal) } @@ -30,7 +43,9 @@ public struct Web3ModalContainerView: View { .edgesIgnoringSafeArea(.all) .onChangeBackported(of: showModal, perform: { newValue in if newValue == false { - dismiss() + withAnimation { + dismiss() + } } }) .onAppear { @@ -47,5 +62,3 @@ public struct Web3ModalContainerView: View { } } } - - diff --git a/Sources/Web3Modal/Modal/ModalInteractor.swift b/Sources/Web3Modal/Modal/ModalInteractor.swift new file mode 100644 index 000000000..a89a1b0db --- /dev/null +++ b/Sources/Web3Modal/Modal/ModalInteractor.swift @@ -0,0 +1,47 @@ +import WalletConnectPairing +import WalletConnectSign +import Combine +import WalletConnectNetworking + +extension ModalSheet { + final class Interactor { + let projectId: String + let metadata: AppMetadata + let socketFactory: WebSocketFactory + + lazy var sessionsPublisher: AnyPublisher<[Session], Never> = Sign.instance.sessionsPublisher + + init(projectId: String, metadata: AppMetadata, webSocketFactory: WebSocketFactory) { + self.projectId = projectId + self.metadata = metadata + self.socketFactory = webSocketFactory + + Pair.configure(metadata: metadata) + Networking.configure(projectId: projectId, socketFactory: socketFactory) + } + +// func getListings() async throws -> [Listing] { +// let listingResponse = try await ExplorerApi.live().getMobileListings(projectId) +// return listingResponse.listings.values.compactMap { $0 } +// } + + func connect() async throws -> WalletConnectURI { + + let uri = try await Pair.instance.create() + + let methods: Set = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"] + let blockchains: Set = [Blockchain("eip155:1")!, Blockchain("eip155:137")!] + let namespaces: [String: ProposalNamespace] = [ + "eip155": ProposalNamespace( + chains: blockchains, + methods: methods, + events: [] + ) + ] + + try await Sign.instance.connect(requiredNamespaces: namespaces, topic: uri.topic) + + return uri + } + } +} diff --git a/Sources/Web3Modal/Modal/ModalSheet+Previews.swift b/Sources/Web3Modal/Modal/ModalSheet+Previews.swift new file mode 100644 index 000000000..f4be2b253 --- /dev/null +++ b/Sources/Web3Modal/Modal/ModalSheet+Previews.swift @@ -0,0 +1,51 @@ +#if DEBUG + +import SwiftUI +import WalletConnectPairing + +class WebSocketMock: WebSocketConnecting { + var request: URLRequest = .init(url: URL(string: "wss://relay.walletconnect.com")!) + + var onText: ((String) -> Void)? + var onConnect: (() -> Void)? + var onDisconnect: ((Error?) -> Void)? + var sendCallCount: Int = 0 + var isConnected: Bool = false + + func connect() {} + func disconnect() {} + func write(string: String, completion: (() -> Void)?) {} +} + +class WebSocketFactoryMock: WebSocketFactory { + func create(with url: URL) -> WebSocketConnecting { + WebSocketMock() + } +} + +struct ModalSheet_Previews: PreviewProvider { + static let projectId = "9bfe94c9cbf74aaa0597094ef561f703" + static let metadata = AppMetadata( + name: "Showcase App", + description: "Showcase description", + url: "example.wallet", + icons: ["https://avatars.githubusercontent.com/u/37784886"] + ) + + static var previews: some View { + ModalSheet( + viewModel: .init( + isShown: .constant(true), + projectId: projectId, + interactor: .init( + projectId: projectId, + metadata: metadata, + webSocketFactory: WebSocketFactoryMock() + ) + ) + ) + .previewLayout(.sizeThatFits) + } +} + +#endif diff --git a/Sources/Web3Modal/Modal/ModalSheet.swift b/Sources/Web3Modal/Modal/ModalSheet.swift new file mode 100644 index 000000000..380e54184 --- /dev/null +++ b/Sources/Web3Modal/Modal/ModalSheet.swift @@ -0,0 +1,210 @@ +import SwiftUI + +public struct ModalSheet: View { + @ObservedObject var viewModel: ModalViewModel + + public var body: some View { + VStack(spacing: 0) { + modalHeader() + + VStack(spacing: 0) { + contentHeader() + content() + } + .frame(maxWidth: .infinity) + .background(Color.background1) + .cornerRadius(30, corners: [.topLeft, .topRight]) + } + .padding(.bottom, 40) + .onAppear { + Task { + await viewModel.createURI() +// await viewModel.fetchWallets() + } + } + .background( + VStack(spacing: 0) { + Color.accent + .frame(height: 90) + .cornerRadius(8, corners: [[.topLeft, .topRight]]) + Color.background1 + } + ) + } + + private func modalHeader() -> some View { + HStack(spacing: 0) { + Image("walletconnect_logo", bundle: .module) + .resizable() + .scaledToFit() + .frame(width: 180) + .padding(.leading, 10) + + Spacer() + + HStack(spacing: 16) { + helpButton() + closeButton() + } + .padding(.trailing, 10) + } + .foregroundColor(Color.foreground1) + .frame(height: 48) + } + + private func contentHeader() -> some View { + HStack(spacing: 0) { + if viewModel.destination != .wallets { + backButton() + } + + Spacer() + + switch viewModel.destination { + case .wallets: + qrButton() + case .qr: + copyButton() + default: + EmptyView() + } + } + .animation(.default) + .foregroundColor(.accent) + .frame(height: 60) + .overlay( + Text(viewModel.destination.contentTitle) + .font(.system(size: 20).weight(.semibold)) + .foregroundColor(.foreground1) + .padding(.horizontal, 50) + ) + } + + @ViewBuilder + private func content() -> some View { + switch viewModel.destination { + case .wallets: + + Text("TBD in subsequent PR") + +// ZStack { +// VStack { +// HStack { +// ForEach(0..<4) { wallet in +// gridItem(for: wallet) +// } +// } +// HStack { +// ForEach(4..<7) { wallet in +// gridItem(for: wallet) +// } +// } +// } +// +// Spacer().frame(height: 200) +// } + + case .help: + WhatIsWalletView() + case .qr: + VStack { + if let uri = viewModel.uri { + QRCodeView(uri: uri) + } else { + ActivityIndicator(isAnimating: .constant(true), style: .large) + } + } + } + } + +// @ViewBuilder +// private func gridItem(for index: Int) -> some View { +// let wallet: Listing = viewModel.wallets.indices.contains(index) ? viewModel.wallets[index] : nil +// +// if #available(iOS 14.0, *) { +// VStack { +// AsyncImage(url: wallet != nil ? viewModel.imageUrl(for: wallet!) : nil) { image in +// image +// .resizable() +// .scaledToFit() +// } placeholder: { +// Color.foreground3 +// } +// .cornerRadius(8) +// .overlay( +// RoundedRectangle(cornerRadius: 8) +// .stroke(.gray.opacity(0.4), lineWidth: 1) +// ) +// +// Text(wallet?.name ?? "WalletName") +// .font(.system(size: 12)) +// .foregroundColor(.foreground1) +// .padding(.horizontal, 12) +// .fixedSize(horizontal: true, vertical: true) +// +// Text("RECENT") +// .opacity(0) +// .font(.system(size: 10)) +// .foregroundColor(.foreground3) +// .padding(.horizontal, 12) +// } +// .redacted(reason: wallet == nil ? .placeholder : []) +// .frame(maxWidth: 80, maxHeight: 96) +// } +// } + + private func helpButton() -> some View { + Button(action: { + withAnimation { + viewModel.navigateTo(.help) + } + }, label: { + Image("help", bundle: .module) + .padding(8) + }) + .buttonStyle(CircuralIconButtonStyle()) + } + + private func closeButton() -> some View { + Button(action: { + withAnimation { + viewModel.isShown.wrappedValue = false + } + }, label: { + Image("close", bundle: .module) + .padding(8) + }) + .buttonStyle(CircuralIconButtonStyle()) + } + + private func backButton() -> some View { + Button(action: { + withAnimation { + viewModel.onBackButton() + } + }, label: { + Image(systemName: "chevron.backward") + .padding(20) + }) + } + + private func qrButton() -> some View { + Button(action: { + withAnimation { + viewModel.navigateTo(.qr) + } + }, label: { + Image("qr_large", bundle: .module) + .padding() + }) + } + + private func copyButton() -> some View { + Button(action: { + UIPasteboard.general.string = viewModel.uri + }, label: { + Image("copy_large", bundle: .module) + .padding() + }) + } +} diff --git a/Sources/Web3Modal/Modal/ModalViewModel.swift b/Sources/Web3Modal/Modal/ModalViewModel.swift new file mode 100644 index 000000000..f64025d70 --- /dev/null +++ b/Sources/Web3Modal/Modal/ModalViewModel.swift @@ -0,0 +1,86 @@ + +import Combine +import Foundation +import SwiftUI + +extension ModalSheet { + enum Destination: String, CaseIterable { + case wallets + case help + case qr + + var contentTitle: String { + switch self { + case .wallets: + return "Connect your wallet" + case .qr: + return "Scan the code" + case .help: + return "What is a wallet?" + } + } + } + + final class ModalViewModel: ObservableObject { + private var disposeBag = Set() + private let interactor: Interactor + private let projectId: String + + @Published var isShown: Binding + + @Published var uri: String? + @Published var destination: Destination = .wallets + @Published var errorMessage: String? +// @Published var wallets: [Listing] = [] + + init(isShown: Binding, projectId: String, interactor: Interactor) { + self.isShown = isShown + self.interactor = interactor + self.projectId = projectId + + interactor.sessionsPublisher + .receive(on: DispatchQueue.main) + .sink { sessions in + print(sessions) + isShown.wrappedValue = false + } + .store(in: &disposeBag) + } + +// @MainActor +// func fetchWallets() async { +// do { +// +// try await Task.sleep(nanoseconds: 1_000_000_000) +// wallets = try await interactor.getListings() +// } catch { +// print(error) +// errorMessage = error.localizedDescription +// } +// } + + @MainActor + func createURI() async { + do { + uri = try await interactor.connect().absoluteString + } catch { + print(error) + errorMessage = error.localizedDescription + } + } + + func navigateTo(_ destination: Destination) { + self.destination = destination + } + + func onBackButton() { + destination = .wallets + } + +// func imageUrl(for listing: Listing) -> URL? { +// let urlString = "https://explorer-api.walletconnect.com/v3/logo/md/\(listing.imageId)?projectId=\(projectId)" +// +// return URL(string: urlString) +// } + } +} diff --git a/Sources/Web3Modal/QRCodeView.swift b/Sources/Web3Modal/Modal/Screens/QRCodeView.swift similarity index 57% rename from Sources/Web3Modal/QRCodeView.swift rename to Sources/Web3Modal/Modal/Screens/QRCodeView.swift index 82b78386c..54a2c4c13 100644 --- a/Sources/Web3Modal/QRCodeView.swift +++ b/Sources/Web3Modal/Modal/Screens/QRCodeView.swift @@ -6,13 +6,19 @@ struct QRCodeView: View { @State var doc: QRCode.Document! @Environment(\.colorScheme) var colorScheme: ColorScheme - + + @State var uri: String + var body: some View { + let backgroundColor = UIColor(named: "background1", in: .module, compatibleWith: nil)!.cgColor + let foregroundColor = UIColor(named: "foreground1", in: .module, compatibleWith: .current)!.cgColor + QRCodeViewUI( - content: Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50).flatMap({ $0 }).shuffled().joined(), - foregroundColor: colorScheme == .light ? UIColor.black.cgColor : UIColor.white.cgColor, - backgroundColor: colorScheme == .light ? UIColor.white.cgColor : UIColor.black.cgColor, + content: uri, + errorCorrection: .quantize, + foregroundColor: foregroundColor, + backgroundColor: backgroundColor, pixelStyle: QRCode.PixelShape.Vertical( insetFraction: 0.2, cornerRadiusFraction: 1 @@ -26,19 +32,19 @@ struct QRCodeView: View { ) ) ) - .padding(.bottom, 40) + .frame(height: UIScreen.main.bounds.width) } } - struct QRCodeView_Previews: PreviewProvider { + + static let stubUri: String = Array(repeating: ["a", "b", "c", "1", "2", "3"], count: 50) + .flatMap({ $0 }) + .shuffled() + .joined() + static var previews: some View { - QRCodeView() - .previewLayout(.sizeThatFits) - .preferredColorScheme(.dark) - - QRCodeView() + QRCodeView(uri: stubUri) .previewLayout(.sizeThatFits) - .preferredColorScheme(.light) } } diff --git a/Sources/Web3Modal/WhatIsWalletView.swift b/Sources/Web3Modal/Modal/Screens/WhatIsWalletView.swift similarity index 79% rename from Sources/Web3Modal/WhatIsWalletView.swift rename to Sources/Web3Modal/Modal/Screens/WhatIsWalletView.swift index 847d9b9e7..1119fc8eb 100644 --- a/Sources/Web3Modal/WhatIsWalletView.swift +++ b/Sources/Web3Modal/Modal/Screens/WhatIsWalletView.swift @@ -37,17 +37,10 @@ struct WhatIsWalletView: View { } .buttonStyle(W3MButtonStyle()) } - .padding(34) + .padding(.horizontal, 24) } } -extension Color { - static let foreground1 = Color(red: 20/255, green: 20/255, blue: 20/255) - static let foreground2 = Color(red: 121/255, green: 134/255, blue: 134/255) - - static let blueDark = Color(red: 71/255, green: 161/255, blue: 255/255) -} - struct HelpSection: View { let title: String @@ -71,6 +64,7 @@ struct HelpSection: View { .font(.system(size: 14)) .foregroundColor(.foreground2) .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) } .padding(.top, 10) .padding(.bottom, 5) @@ -84,14 +78,3 @@ struct WhatIsWalletView_Previews: PreviewProvider { WhatIsWalletView() } } - -struct W3MButtonStyle: ButtonStyle { - func makeBody(configuration: Configuration) -> some View { - configuration.label - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.blueDark) - .foregroundColor(.white) - .clipShape(Capsule()) - } -} diff --git a/Sources/Web3Modal/Styles/Button.swift b/Sources/Web3Modal/Styles/Button.swift new file mode 100644 index 000000000..d3784853b --- /dev/null +++ b/Sources/Web3Modal/Styles/Button.swift @@ -0,0 +1,44 @@ + +import SwiftUI + +struct CircuralIconButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .frame(width: 28, height: 28) + .background(Color.background1) + .foregroundColor(.foreground1) + .clipShape(Capsule()) + .scaleEffect(configuration.isPressed ? 0.8 : 1) + .animation(.default, value: configuration.isPressed) + } +} + +struct W3MButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.accent) + .foregroundColor(.foregroundInverse) + .clipShape(Capsule()) + .scaleEffect(configuration.isPressed ? 0.9 : 1) + .animation(.default, value: configuration.isPressed) + } +} + +struct ButtonStyle_Previews: PreviewProvider { + + static var previews: some View { + + VStack { + + Button("Foo", action: {}) + .buttonStyle(W3MButtonStyle()) + + Button("F", action: {}) + .buttonStyle(CircuralIconButtonStyle()) + } + .background(Color.background3) + .previewLayout(.sizeThatFits) + } +} diff --git a/Sources/Web3Modal/UI/ActivityIndicator.swift b/Sources/Web3Modal/UI/ActivityIndicator.swift new file mode 100644 index 000000000..0d220ec62 --- /dev/null +++ b/Sources/Web3Modal/UI/ActivityIndicator.swift @@ -0,0 +1,14 @@ +import SwiftUI + +struct ActivityIndicator: UIViewRepresentable { + @Binding var isAnimating: Bool + let style: UIActivityIndicatorView.Style + + func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { + return UIActivityIndicatorView(style: style) + } + + func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) { + isAnimating ? uiView.startAnimating() : uiView.stopAnimating() + } +} diff --git a/Sources/Web3Modal/UI/AsyncImage.swift b/Sources/Web3Modal/UI/AsyncImage.swift new file mode 100644 index 000000000..4eca1aba1 --- /dev/null +++ b/Sources/Web3Modal/UI/AsyncImage.swift @@ -0,0 +1,56 @@ +import Combine +import SwiftUI + +struct AsyncImage: View where Content: View { + final class Loader: ObservableObject { + @Published var data: Data? = nil + + private var cancellables = Set() + + init(_ url: URL?) { + guard let url = url else { return } + URLSession.shared.dataTaskPublisher(for: url) + .map(\.data) + .map { $0 as Data? } + .replaceError(with: nil) + .receive(on: RunLoop.main) + .assign(to: \.data, on: self) + .store(in: &cancellables) + } + } + + @ObservedObject private var imageLoader: Loader + private let conditionalContent: ((Image?) -> Content)? + + init(url: URL?) where Content == Image { + self.imageLoader = Loader(url) + self.conditionalContent = nil + } + + init(url: URL?, @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent, I: View, P: View { + self.imageLoader = Loader(url) + self.conditionalContent = { image in + if let image = image { + return ViewBuilder.buildEither(first: content(image)) + } else { + return ViewBuilder.buildEither(second: placeholder()) + } + } + } + + private var image: Image? { + imageLoader.data + .flatMap { + UIImage(data: $0) + } + .flatMap(Image.init) + } + + var body: some View { + if let conditionalContent = conditionalContent { + conditionalContent(image) + } else if let image = image { + image + } + } +} diff --git a/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift b/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift index 61b9cb237..9890a66d2 100644 --- a/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift +++ b/Sources/Web3Modal/UIKitSupport/Web3ModalSheetController.swift @@ -1,18 +1,24 @@ import SwiftUI +import WalletConnectNetworking +import WalletConnectPairing -public class Web3ModalSheetController: UIHostingController { - - @MainActor required dynamic init?(coder aDecoder: NSCoder) { +public class Web3ModalSheetController: UIHostingController { + + @MainActor dynamic required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - public init() { - super.init(rootView: Web3ModalContainerView()) + public init(projectId: String, metadata: AppMetadata, webSocketFactory: WebSocketFactory) { + let view = AnyView( + ModalContainerView(projectId: projectId, metadata: metadata, webSocketFactory: webSocketFactory) + ) + + super.init(rootView: view) self.modalTransitionStyle = .crossDissolve self.modalPresentationStyle = .overFullScreen } - public override func viewDidLoad() { + override public func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .clear } diff --git a/Sources/Web3Modal/Web3ModalSheet.swift b/Sources/Web3Modal/Web3ModalSheet.swift deleted file mode 100644 index d4039f3d8..000000000 --- a/Sources/Web3Modal/Web3ModalSheet.swift +++ /dev/null @@ -1,91 +0,0 @@ -import SwiftUI - -public struct Web3ModalSheet: View { - - @State public var destination: Destination - @Binding public var isShown: Bool - - public enum Destination: String { - case welcome - case help - case qr - } - - public var body: some View { - ZStack(alignment: .top) { - Color.white - - VStack { - Color.blue - .frame(height: 40) - .overlay( - HStack() { - backButton() - .disabled(destination == .welcome) - - Spacer() - - closeButton() - }, - alignment: .topTrailing - ) - - switch destination { - case .welcome: - - Button("Help") { - withAnimation { - destination = .help - } - } - - Button("QR") { - withAnimation { - destination = .qr - } - } - case .help: - WhatIsWalletView() - case .qr: - QRCodeView() - } - } - } - } - - func backButton() -> some View { - Button(action: { - withAnimation { - destination = .welcome - } - }, label: { - Image(systemName: "chevron.backward") - .foregroundColor(.black) - }) - .padding() - } - - func closeButton() -> some View { - Button(action: { - withAnimation { - isShown = false - } - }, label: { - Image(systemName: "x.circle") - .foregroundColor(.black) - }) - .padding() - } -} - -struct Web3ModalSheet_Previews: PreviewProvider { - static var previews: some View { - Web3ModalSheet(destination: .welcome, isShown: .constant(true)) - .previewLayout(.sizeThatFits) - .preferredColorScheme(.light) - - Web3ModalSheet(destination: .qr, isShown: .constant(true)) - .previewLayout(.sizeThatFits) - .preferredColorScheme(.dark) - } -} From 9c1515bd6062dc24ca147ac743766ec7fd469d9d Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Tue, 16 May 2023 22:38:25 +0200 Subject: [PATCH 79/89] Add verify cocoapods imports --- Sources/WalletConnectVerify/AppAttestationRegistrer.swift | 1 - Sources/WalletConnectVerify/AttestKeyGenerator.swift | 1 - Sources/WalletConnectVerify/OriginVerifier.swift | 1 - Sources/WalletConnectVerify/Register/VerifyService.swift | 1 - Sources/WalletConnectVerify/VerifyClient.swift | 3 --- Sources/WalletConnectVerify/VerifyClientFactory.swift | 1 - Sources/WalletConnectVerify/VerifyImports.swift | 4 ++++ 7 files changed, 4 insertions(+), 8 deletions(-) create mode 100644 Sources/WalletConnectVerify/VerifyImports.swift diff --git a/Sources/WalletConnectVerify/AppAttestationRegistrer.swift b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift index 84b5d5dd1..944e88902 100644 --- a/Sources/WalletConnectVerify/AppAttestationRegistrer.swift +++ b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift @@ -1,6 +1,5 @@ import Foundation import DeviceCheck -import WalletConnectUtils import CryptoKit class AppAttestationRegistrer { diff --git a/Sources/WalletConnectVerify/AttestKeyGenerator.swift b/Sources/WalletConnectVerify/AttestKeyGenerator.swift index a53b15c6d..aaebb6b31 100644 --- a/Sources/WalletConnectVerify/AttestKeyGenerator.swift +++ b/Sources/WalletConnectVerify/AttestKeyGenerator.swift @@ -1,6 +1,5 @@ import DeviceCheck import Foundation -import WalletConnectUtils protocol AttestKeyGenerating { func generateKeys() async throws -> String diff --git a/Sources/WalletConnectVerify/OriginVerifier.swift b/Sources/WalletConnectVerify/OriginVerifier.swift index 424a45ac0..039cccecf 100644 --- a/Sources/WalletConnectVerify/OriginVerifier.swift +++ b/Sources/WalletConnectVerify/OriginVerifier.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectNetworking public final class OriginVerifier { enum Errors: Error { diff --git a/Sources/WalletConnectVerify/Register/VerifyService.swift b/Sources/WalletConnectVerify/Register/VerifyService.swift index cec111182..5cf720118 100644 --- a/Sources/WalletConnectVerify/Register/VerifyService.swift +++ b/Sources/WalletConnectVerify/Register/VerifyService.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectNetworking enum VerifyAPI: HTTPService { case resolve(assertionId: String) diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift index 74d01217b..9edbc6f3a 100644 --- a/Sources/WalletConnectVerify/VerifyClient.swift +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -1,9 +1,6 @@ import DeviceCheck import Foundation -import WalletConnectUtils -import WalletConnectNetworking - public actor VerifyClient { enum Errors: Error { case attestationNotSupported diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index b07648532..96e0c7526 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectUtils public class VerifyClientFactory { public static func create(verifyHost: String?) throws -> VerifyClient? { diff --git a/Sources/WalletConnectVerify/VerifyImports.swift b/Sources/WalletConnectVerify/VerifyImports.swift new file mode 100644 index 000000000..065c96db2 --- /dev/null +++ b/Sources/WalletConnectVerify/VerifyImports.swift @@ -0,0 +1,4 @@ +#if !CocoaPods +@_exported import WalletConnectUtils +@_exported import WalletConnectNetworking +#endif From 95369bb83e01e29339176ed62a3c56e53b541608 Mon Sep 17 00:00:00 2001 From: Alexander Lisovyk Date: Wed, 17 May 2023 10:10:52 +0200 Subject: [PATCH 80/89] Add SPM special case --- Sources/Auth/Auth.swift | 3 +++ Sources/WalletConnectSign/Sign/Sign.swift | 2 ++ Sources/Web3Wallet/Web3Wallet.swift | 3 +++ 3 files changed, 8 insertions(+) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index e613091da..fb91ed780 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,7 +1,10 @@ import Foundation import Combine +#if SWIFT_PACKAGE public typealias VerifyContext = WalletConnectVerify.VerifyContext +#endif + /// Auth instatnce wrapper /// /// ```Swift diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index b4935c681..b140c6fc9 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -1,7 +1,9 @@ import Foundation import Combine +#if SWIFT_PACKAGE public typealias VerifyContext = WalletConnectVerify.VerifyContext +#endif /// Sign instatnce wrapper /// diff --git a/Sources/Web3Wallet/Web3Wallet.swift b/Sources/Web3Wallet/Web3Wallet.swift index fdc122e76..4745d2989 100644 --- a/Sources/Web3Wallet/Web3Wallet.swift +++ b/Sources/Web3Wallet/Web3Wallet.swift @@ -1,7 +1,10 @@ import Foundation import Combine +#if SWIFT_PACKAGE public typealias VerifyContext = WalletConnectVerify.VerifyContext +#endif + /// Web3Wallet instance wrapper /// /// ```Swift From cdb29b3487143fef8b317a3488801ad1f5fa54ed Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Wed, 17 May 2023 14:42:34 +0200 Subject: [PATCH 81/89] Add convenience code for assets --- .../Chat/Import/ImportView.swift | 6 +- Sources/Web3Modal/Extensions/Color.swift | 58 ----------- Sources/Web3Modal/Modal/ModalSheet.swift | 36 +++---- Sources/Web3Modal/Modal/ModalViewModel.swift | 4 + .../Web3Modal/Modal/Screens/QRCodeView.swift | 10 +- Sources/Web3Modal/Resources/Asset.swift | 59 ++++++++++++ Sources/Web3Modal/Resources/Color.swift | 95 +++++++++++++++++++ 7 files changed, 182 insertions(+), 86 deletions(-) delete mode 100644 Sources/Web3Modal/Extensions/Color.swift create mode 100644 Sources/Web3Modal/Resources/Asset.swift create mode 100644 Sources/Web3Modal/Resources/Color.swift diff --git a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift index a7ab737cf..9e0584723 100644 --- a/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift +++ b/Example/Showcase/Classes/PresentationLayer/Chat/Import/ImportView.swift @@ -17,9 +17,9 @@ struct ImportView: View { VStack { - BrandButton(title: "Web3Modal") { - try await presenter.didPressWeb3Modal() - } +// BrandButton(title: "Web3Modal") { +// try await presenter.didPressWeb3Modal() +// } BrandButton(title: "Ok, done" ) { try await presenter.didPressImport() diff --git a/Sources/Web3Modal/Extensions/Color.swift b/Sources/Web3Modal/Extensions/Color.swift deleted file mode 100644 index 84e687266..000000000 --- a/Sources/Web3Modal/Extensions/Color.swift +++ /dev/null @@ -1,58 +0,0 @@ -import SwiftUI - -extension Color { - static let foreground1 = Color("foreground1", bundle: .module) - static let foreground2 = Color("foreground2", bundle: .module) - static let foreground3 = Color("foreground3", bundle: .module) - static let foregroundInverse = Color("foregroundInverse", bundle: .module) - static let background1 = Color("background1", bundle: .module) - static let background2 = Color("background2", bundle: .module) - static let background3 = Color("background3", bundle: .module) - static let negative = Color("negative", bundle: .module) - static let thickOverlay = Color("thickOverlay", bundle: .module) - static let thinOverlay = Color("thinOverlay", bundle: .module) - static let accent = Color("accent", bundle: .module) -} - -@available(iOS 15.0, *) -struct Color_Previews: PreviewProvider { - static var allColors: [(String, Color)] { - [ - ("foreground1", Color("foreground1", bundle: .module)), - ("foreground2", Color("foreground2", bundle: .module)), - ("foreground3", Color("foreground3", bundle: .module)), - ("foregroundInverse", Color("foregroundInverse", bundle: .module)), - ("background1", Color("background1", bundle: .module)), - ("background2", Color("background2", bundle: .module)), - ("background3", Color("background3", bundle: .module)), - ("negative", Color("negative", bundle: .module)), - ("thickOverlay", Color("thickOverlay", bundle: .module)), - ("thinOverlay", Color("thinOverlay", bundle: .module)), - ("accent", Color("accent", bundle: .module)), - ] - } - - static var previews: some View { - VStack { - let columns = [ - GridItem(.adaptive(minimum: 150)), - ] - - LazyVGrid(columns: columns, alignment: .leading) { - ForEach(allColors, id: \.1) { name, color in - - VStack(alignment: .leading) { - RoundedRectangle(cornerRadius: 12) - .fill(color) - .frame(width: 62, height: 62) - - Text(name).bold() - } - .font(.footnote) - } - } - } - .padding() - .previewLayout(.sizeThatFits) - } -} diff --git a/Sources/Web3Modal/Modal/ModalSheet.swift b/Sources/Web3Modal/Modal/ModalSheet.swift index 380e54184..3df7d7cf3 100644 --- a/Sources/Web3Modal/Modal/ModalSheet.swift +++ b/Sources/Web3Modal/Modal/ModalSheet.swift @@ -34,7 +34,7 @@ public struct ModalSheet: View { private func modalHeader() -> some View { HStack(spacing: 0) { - Image("walletconnect_logo", bundle: .module) + Image(.walletconnect_logo) .resizable() .scaledToFit() .frame(width: 180) @@ -159,52 +159,52 @@ public struct ModalSheet: View { viewModel.navigateTo(.help) } }, label: { - Image("help", bundle: .module) + Image(.help) .padding(8) }) .buttonStyle(CircuralIconButtonStyle()) } private func closeButton() -> some View { - Button(action: { + Button { withAnimation { viewModel.isShown.wrappedValue = false } - }, label: { - Image("close", bundle: .module) + } label: { + Image(.close) .padding(8) - }) + } .buttonStyle(CircuralIconButtonStyle()) } private func backButton() -> some View { - Button(action: { + Button { withAnimation { viewModel.onBackButton() } - }, label: { + } label: { Image(systemName: "chevron.backward") .padding(20) - }) + } } private func qrButton() -> some View { - Button(action: { + Button { withAnimation { viewModel.navigateTo(.qr) } - }, label: { - Image("qr_large", bundle: .module) + } label: { + Image(.qr_large) .padding() - }) + } } private func copyButton() -> some View { - Button(action: { - UIPasteboard.general.string = viewModel.uri - }, label: { - Image("copy_large", bundle: .module) + Button { + viewModel.onCopyButton() + } label: { + Image(.copy_large) .padding() - }) + } } } diff --git a/Sources/Web3Modal/Modal/ModalViewModel.swift b/Sources/Web3Modal/Modal/ModalViewModel.swift index f64025d70..05633ca9c 100644 --- a/Sources/Web3Modal/Modal/ModalViewModel.swift +++ b/Sources/Web3Modal/Modal/ModalViewModel.swift @@ -77,6 +77,10 @@ extension ModalSheet { destination = .wallets } + func onCopyButton() { + UIPasteboard.general.string = uri + } + // func imageUrl(for listing: Listing) -> URL? { // let urlString = "https://explorer-api.walletconnect.com/v3/logo/md/\(listing.imageId)?projectId=\(projectId)" // diff --git a/Sources/Web3Modal/Modal/Screens/QRCodeView.swift b/Sources/Web3Modal/Modal/Screens/QRCodeView.swift index 54a2c4c13..8ca2a6ca6 100644 --- a/Sources/Web3Modal/Modal/Screens/QRCodeView.swift +++ b/Sources/Web3Modal/Modal/Screens/QRCodeView.swift @@ -10,22 +10,18 @@ struct QRCodeView: View { @State var uri: String var body: some View { - - let backgroundColor = UIColor(named: "background1", in: .module, compatibleWith: nil)!.cgColor - let foregroundColor = UIColor(named: "foreground1", in: .module, compatibleWith: .current)!.cgColor - QRCodeViewUI( content: uri, errorCorrection: .quantize, - foregroundColor: foregroundColor, - backgroundColor: backgroundColor, + foregroundColor: AssetColor.background1.uiColor.cgColor, + backgroundColor: AssetColor.foreground1.uiColor.cgColor, pixelStyle: QRCode.PixelShape.Vertical( insetFraction: 0.2, cornerRadiusFraction: 1 ), eyeStyle: QRCode.EyeShape.Squircle(), logoTemplate: QRCode.LogoTemplate( - image: (UIImage(named: "wc_logo", in: .module, with: .none)?.cgImage)!, + image: Asset.wc_logo.uiImage.cgImage!, path: CGPath( rect: CGRect(x: 0.35, y: 0.3875, width: 0.30, height: 0.225), transform: nil diff --git a/Sources/Web3Modal/Resources/Asset.swift b/Sources/Web3Modal/Resources/Asset.swift new file mode 100644 index 000000000..c5d987646 --- /dev/null +++ b/Sources/Web3Modal/Resources/Asset.swift @@ -0,0 +1,59 @@ + +import SwiftUI +import UIKit + +enum Asset: String { + + // Icons + case close + case external_link + case help + case wallet + + // large + case copy_large + case qr_large + + // Images + case walletconnect_logo + case wc_logo + + // Help + case Browser + case DAO + case DeFi + case ETH + case Layers + case Lock + case Login + case Network + case NFT + case Noun + case Profile + case System +} + +extension Asset { + + var image: Image { + Image(self) + } + + var uiImage: UIImage { + UIImage(self) + } +} + +extension Image { + + init(_ asset: Asset) { + self.init(asset.rawValue, bundle: .module) + } +} + +extension UIImage { + + convenience init(_ asset: Asset) { + self.init(named: asset.rawValue, in: .module, compatibleWith: .current)! + } +} diff --git a/Sources/Web3Modal/Resources/Color.swift b/Sources/Web3Modal/Resources/Color.swift new file mode 100644 index 000000000..5664e6081 --- /dev/null +++ b/Sources/Web3Modal/Resources/Color.swift @@ -0,0 +1,95 @@ +import SwiftUI + +enum AssetColor: String { + case foreground1 + case foreground2 + case foreground3 + case foregroundInverse + case background1 + case background2 + case background3 + case negative + case thickOverlay + case thinOverlay + case accent +} + +extension Color { + + init(_ asset: AssetColor) { + self.init(asset.rawValue, bundle: .module) + } + + static let foreground1 = Color(.foreground1) + static let foreground2 = Color(.foreground2) + static let foreground3 = Color(.foreground3) + static let foregroundInverse = Color(.foregroundInverse) + static let background1 = Color(.background1) + static let background2 = Color(.background2) + static let background3 = Color(.background3) + static let negative = Color(.negative) + static let thickOverlay = Color(.thickOverlay) + static let thinOverlay = Color(.thinOverlay) + static let accent = Color(.accent) +} + +extension UIColor { + + convenience init(_ asset: AssetColor) { + self.init(named: asset.rawValue, in: .module, compatibleWith: nil)! + } +} + +extension AssetColor { + + var swituiColor: Color { + Color(self) + } + + var uiColor: UIColor { + UIColor(self) + } +} + +@available(iOS 15.0, *) +struct Color_Previews: PreviewProvider { + static var allColors: [AssetColor] { + [ + .foreground1, + .foreground2, + .foreground3, + .foregroundInverse, + .background1, + .background2, + .background3, + .negative, + .thickOverlay, + .thinOverlay, + .accent, + ] + } + + static var previews: some View { + VStack { + let columns = [ + GridItem(.adaptive(minimum: 150)), + ] + + LazyVGrid(columns: columns, alignment: .leading) { + ForEach(allColors, id: \.self) { colorAsset in + + VStack(alignment: .leading) { + RoundedRectangle(cornerRadius: 12) + .fill(colorAsset.swituiColor) + .frame(width: 62, height: 62) + + Text(colorAsset.rawValue).bold() + } + .font(.footnote) + } + } + } + .padding() + .previewLayout(.sizeThatFits) + } +} From c57af7dbc4ccc4624918e269e0b1d3fcb940785f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 May 2023 08:26:37 +0200 Subject: [PATCH 82/89] get scope before subscribing --- Example/ExampleApp.xcodeproj/project.pbxproj | 5 +---- .../wc_pushSubscribe/PushSubscribeRequester.swift | 13 ++++++++++--- .../PushSubscribeResponseSubscriber.swift | 14 +++++++++----- .../Client/Wallet/WalletPushClientFactory.swift | 4 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 98b7c6e9e..ea905af36 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -2575,11 +2575,9 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PNDecryptionService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; @@ -2594,7 +2592,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.walletconnect.walletapp.PNDecryptionService"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index 49217c554..c8df2a1bd 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -16,6 +16,7 @@ class PushSubscribeRequester { private let logger: ConsoleLogging private let webDidResolver: WebDidResolver private let dappsMetadataStore: CodableStore + private let subscriptionScopeProvider: SubscriptionScopeProvider init(keyserverURL: URL, networkingInteractor: NetworkInteracting, @@ -23,6 +24,7 @@ class PushSubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, webDidResolver: WebDidResolver = WebDidResolver(), + subscriptionScopeProvider: SubscriptionScopeProvider, dappsMetadataStore: CodableStore ) { self.keyserverURL = keyserverURL @@ -31,6 +33,7 @@ class PushSubscribeRequester { self.logger = logger self.kms = kms self.webDidResolver = webDidResolver + self.subscriptionScopeProvider = subscriptionScopeProvider self.dappsMetadataStore = dappsMetadataStore } @@ -57,7 +60,11 @@ class PushSubscribeRequester { logger.debug("setting symm key for response topic \(responseTopic)") - let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl) + let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata.url) + + let scope = availableScope.map{$0.name.rawValue}.joined(separator: " ") + + let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl, scope: scope) let protocolMethod = PushSubscribeProtocolMethod() @@ -86,9 +93,9 @@ class PushSubscribeRequester { return keys } - private func createJWTRequest(subscriptionAccount: Account, dappUrl: String) throws -> RPCRequest { + private func createJWTRequest(subscriptionAccount: Account, dappUrl: String, scope: String) throws -> RPCRequest { let protocolMethod = PushSubscribeProtocolMethod().method - let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "") + let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scope) let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index 63e8e0fa3..c164a240d 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -58,8 +58,11 @@ class PushSubscribeResponseSubscriber { var account: Account! var metadata: AppMetadata! var pushSubscriptionTopic: String! - var availableScope: Set! - let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) + var subscribedTypes: Set! + let (subscriptionPayload, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) + let subscribedScope = subscriptionPayload.scope + .components(separatedBy: " ") + .compactMap{NotificationScope(rawValue: $0)} do { // generate symm key P let agreementKeysP = try kms.performKeyAgreement(selfPublicKey: pubKeyY, peerPublicKey: peerPubKeyZ) @@ -68,7 +71,8 @@ class PushSubscribeResponseSubscriber { try groupKeychainStorage.add(agreementKeysP, forKey: pushSubscriptionTopic) account = try Account(DIDPKHString: claims.sub) metadata = try dappsMetadataStore.get(key: payload.topic) - availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) + let availableTypes = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url) + subscribedTypes = availableTypes.filter{subscribedScope.contains($0.name)} logger.debug("PushSubscribeResponseSubscriber: subscribing push subscription topic: \(pushSubscriptionTopic!)") try await networkingInteractor.subscribe(topic: pushSubscriptionTopic) } catch { @@ -78,12 +82,12 @@ class PushSubscribeResponseSubscriber { } guard let metadata = metadata else { - logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(pushSubscriptionTopic)") + logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(pushSubscriptionTopic!)") return } dappsMetadataStore.delete(forKey: payload.topic) let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let scope: [NotificationScope: ScopeValue] = availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) } + let scope: [NotificationScope: ScopeValue] = subscribedTypes.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) } let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index adec22410..fd8d9739c 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -55,10 +55,10 @@ public struct WalletPushClientFactory { let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.dappsMetadataStore) + let subscriptionScopeProvider = SubscriptionScopeProvider() - let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, dappsMetadataStore: dappsMetadataStore) + let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, subscriptionScopeProvider: subscriptionScopeProvider, dappsMetadataStore: dappsMetadataStore) - let subscriptionScopeProvider = SubscriptionScopeProvider() let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore, subscriptionScopeProvider: subscriptionScopeProvider) let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore) From 1bfb38a984f5d62f44a7646353bd19304049ac1b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 May 2023 08:58:04 +0200 Subject: [PATCH 83/89] fix siwe message --- Sources/WalletConnectUtils/SIWE/SIWEMessage.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift b/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift index 0a7fc18df..8e4a14313 100644 --- a/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift +++ b/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift @@ -34,6 +34,7 @@ public struct SIWEMessage: Equatable { \(domain) wants you to sign in with your Ethereum account: \(address) \(statementLine) + URI: \(uri) Version: \(version) Chain ID: \(chainId) @@ -52,7 +53,7 @@ private extension SIWEMessage { var statementLine: String { guard let statement = statement else { return "" } - return "\n\(statement)\n" + return "\n\(statement)" } var nbfLine: String { From b89416e878346543a5f0ce55c123fe9963152054 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 May 2023 09:45:34 +0200 Subject: [PATCH 84/89] remove notification scope --- Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan | 4 ++++ Example/ExampleApp.xcodeproj/project.pbxproj | 2 +- Example/IntegrationTests/Push/PushTests.swift | 2 +- Example/SmokeTests.xctestplan | 1 + .../wc_notifyUpdate/NotifyUpdateRequester.swift | 6 +++--- .../wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift | 3 +-- .../wc_pushSubscribe/PushSubscribeRequester.swift | 2 +- .../PushSubscribeResponseSubscriber.swift | 3 +-- .../Client/Wallet/WalletPushClient.swift | 2 +- Sources/WalletConnectPush/Types/NotificationScope.swift | 9 --------- Sources/WalletConnectPush/Types/NotificationType.swift | 2 +- Sources/WalletConnectPush/Types/PushSubscription.swift | 2 +- Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift | 2 +- 13 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 Sources/WalletConnectPush/Types/NotificationScope.swift diff --git a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan index 5b274df17..044fd6d54 100644 --- a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan +++ b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan @@ -28,6 +28,10 @@ }, "testTargets" : [ { + "skippedTests" : [ + "AuthTests", + "ChatTests" + ], "target" : { "containerPath" : "container:ExampleApp.xcodeproj", "identifier" : "A5E03DEC286464DB00888481", diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index ea905af36..58481035b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -2577,7 +2577,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PNDecryptionService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 3be34d4f2..a02487b37 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -255,7 +255,7 @@ final class PushTests: XCTestCase { func testWalletCreatesAndUpdatesSubscription() async { let expectation = expectation(description: "expects to create and update push subscription") let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: []) - let updateScope: Set = [NotificationScope.alerts] + let updateScope: Set = ["alerts"] try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign) walletPushClient.subscriptionsPublisher .first() diff --git a/Example/SmokeTests.xctestplan b/Example/SmokeTests.xctestplan index 34685f639..894a5d133 100644 --- a/Example/SmokeTests.xctestplan +++ b/Example/SmokeTests.xctestplan @@ -45,6 +45,7 @@ "EIP55Tests", "ENSResolverTests", "PairingTests", + "PushTests", "PushTests\/testDappDeletePushSubscription()", "PushTests\/testRequestPush()", "PushTests\/testWalletApprovesPushRequest()", diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift index 402d8084c..814a08d2e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift @@ -25,7 +25,7 @@ class NotifyUpdateRequester { self.subscriptionsStore = subscriptionsStore } - func update(topic: String, scope: Set) async throws { + func update(topic: String, scope: Set) async throws { logger.debug("NotifyUpdateRequester: updating subscription for topic: \(topic)") guard let subscription = try subscriptionsStore.get(key: topic) else { throw Errors.noSubscriptionForGivenTopic } @@ -37,9 +37,9 @@ class NotifyUpdateRequester { try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) } - private func createJWTRequest(subscriptionAccount: Account, dappUrl: String, scope: Set) throws -> RPCRequest { + private func createJWTRequest(subscriptionAccount: Account, dappUrl: String, scope: Set) throws -> RPCRequest { let protocolMethod = NotifyUpdateProtocolMethod().method - let scopeClaim = scope.map {$0.rawValue}.joined(separator: " ") + let scopeClaim = scope.joined(separator: " ") let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scopeClaim) let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index 5847d1185..ef7e24ea0 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -59,10 +59,9 @@ class NotifyUpdateResponseSubscriber { }.store(in: &publishers) } - private func buildScope(selected: String, dappUrl: String) async throws -> [NotificationScope: ScopeValue] { + private func buildScope(selected: String, dappUrl: String) async throws -> [String: ScopeValue] { let selectedScope = selected .components(separatedBy: " ") - .compactMap { NotificationScope(rawValue: $0) } let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: dappUrl) return availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: selectedScope.contains($1.name)) } diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift index c8df2a1bd..185fa0109 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift @@ -62,7 +62,7 @@ class PushSubscribeRequester { let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata.url) - let scope = availableScope.map{$0.name.rawValue}.joined(separator: " ") + let scope = availableScope.map{$0.name}.joined(separator: " ") let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl, scope: scope) diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift index c164a240d..0fb0e036e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift @@ -62,7 +62,6 @@ class PushSubscribeResponseSubscriber { let (subscriptionPayload, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request) let subscribedScope = subscriptionPayload.scope .components(separatedBy: " ") - .compactMap{NotificationScope(rawValue: $0)} do { // generate symm key P let agreementKeysP = try kms.performKeyAgreement(selfPublicKey: pubKeyY, peerPublicKey: peerPubKeyZ) @@ -87,7 +86,7 @@ class PushSubscribeResponseSubscriber { } dappsMetadataStore.delete(forKey: payload.topic) let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp)) - let scope: [NotificationScope: ScopeValue] = subscribedTypes.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) } + let scope: [String: ScopeValue] = subscribedTypes.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) } let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry) subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 2740cb252..4c11907a5 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -108,7 +108,7 @@ public class WalletPushClient { try await proposeResponder.respondError(requestId: id) } - public func update(topic: String, scope: Set) async throws { + public func update(topic: String, scope: Set) async throws { try await notifyUpdateRequester.update(topic: topic, scope: scope) } diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift deleted file mode 100644 index b4c74e4be..000000000 --- a/Sources/WalletConnectPush/Types/NotificationScope.swift +++ /dev/null @@ -1,9 +0,0 @@ - -import Foundation - -public enum NotificationScope: String, Hashable, Codable, CodingKeyRepresentable, CaseIterable { - case promotional - case transactional - case `private` - case alerts -} diff --git a/Sources/WalletConnectPush/Types/NotificationType.swift b/Sources/WalletConnectPush/Types/NotificationType.swift index 0e6b2df35..b741c4a2f 100644 --- a/Sources/WalletConnectPush/Types/NotificationType.swift +++ b/Sources/WalletConnectPush/Types/NotificationType.swift @@ -2,6 +2,6 @@ import Foundation public struct NotificationType: Codable, Hashable { - let name: NotificationScope + let name: String let description: String } diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift index 57522f2d8..bfd1fd1eb 100644 --- a/Sources/WalletConnectPush/Types/PushSubscription.swift +++ b/Sources/WalletConnectPush/Types/PushSubscription.swift @@ -7,7 +7,7 @@ public struct PushSubscription: Codable, Equatable { public let account: Account public let relay: RelayProtocolOptions public let metadata: AppMetadata - public let scope: [NotificationScope: ScopeValue] + public let scope: [String: ScopeValue] public let expiry: Date } diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift index c64b22f0d..2a84f4bd4 100644 --- a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift +++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift @@ -67,7 +67,7 @@ private extension PushClientProxy { struct UpdateRequest: Codable { let topic: String - let scope: Set + let scope: Set } struct RejectRequest: Codable { From 3119fcf3999faa0269a4fc98bd5278cb5587a082 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 May 2023 10:01:57 +0200 Subject: [PATCH 85/89] exclude testEIP191RespondSuccess from integration tests --- Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan index 044fd6d54..597f8cc1f 100644 --- a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan +++ b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan @@ -29,7 +29,7 @@ "testTargets" : [ { "skippedTests" : [ - "AuthTests", + "AuthTests\/testEIP1271RespondSuccess()", "ChatTests" ], "target" : { From bf2e7d51a7468915c5483e039d4b13fc5bb41f04 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 May 2023 10:17:28 +0200 Subject: [PATCH 86/89] fix unit tests --- Tests/AuthTests/SIWEMessageFormatterTests.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/AuthTests/SIWEMessageFormatterTests.swift b/Tests/AuthTests/SIWEMessageFormatterTests.swift index 18ff4ce77..521c46c64 100644 --- a/Tests/AuthTests/SIWEMessageFormatterTests.swift +++ b/Tests/AuthTests/SIWEMessageFormatterTests.swift @@ -37,6 +37,7 @@ class SIWEMessageFormatterTests: XCTestCase { service.invalid wants you to sign in with your Ethereum account: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 + URI: https://service.invalid/login Version: 1 Chain ID: 1 @@ -81,6 +82,7 @@ class SIWEMessageFormatterTests: XCTestCase { service.invalid wants you to sign in with your Ethereum account: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 + URI: https://service.invalid/login Version: 1 Chain ID: 1 From 3be63badcd3fc2b20ba7b6bb143e9c26db018202 Mon Sep 17 00:00:00 2001 From: llbartekll Date: Thu, 18 May 2023 08:57:31 +0000 Subject: [PATCH 87/89] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 932ed438f..4b56ff699 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.6.1"} +{"version": "1.6.2"} From 670042cca3318b60ab65642adf52686bac6dea44 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Fri, 19 May 2023 14:23:30 +0200 Subject: [PATCH 88/89] pin QRCode dependency version --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index de8696965..db5990140 100644 --- a/Package.swift +++ b/Package.swift @@ -49,7 +49,7 @@ let package = Package( ], dependencies: [ - .package(url: "https://github.com/WalletConnect/QRCode", branch: "main") + .package(url: "https://github.com/WalletConnect/QRCode", branch: "wc-main") ], targets: [ .target( From db0a3ef9750b9bf70d2ccd4b35591a5c59d14f85 Mon Sep 17 00:00:00 2001 From: llbartekll Date: Sun, 21 May 2023 08:12:56 +0000 Subject: [PATCH 89/89] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 4b56ff699..4cbaef37b 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.6.2"} +{"version": "1.6.3"}