Skip to content

Commit

Permalink
Merge pull request #1185 from WalletConnect/feature/notify-remove-config
Browse files Browse the repository at this point in the history
[Notify] Config from explorer
  • Loading branch information
flypaper0 authored Oct 18, 2023
2 parents fbf5e09 + 33daa17 commit 081f2a3
Show file tree
Hide file tree
Showing 17 changed files with 163 additions and 80 deletions.
7 changes: 4 additions & 3 deletions Example/IntegrationTests/Push/NotifyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ final class NotifyTests: XCTestCase {
keychainStorage: keychain,
environment: .sandbox)
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let client = NotifyClientFactory.create(keyserverURL: keyserverURL,
let client = NotifyClientFactory.create(projectId: InputConfig.projectId,
keyserverURL: keyserverURL,
logger: notifyLogger,
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
Expand Down Expand Up @@ -151,7 +152,7 @@ final class NotifyTests: XCTestCase {

func testWalletCreatesAndUpdatesSubscription() async {
let expectation = expectation(description: "expects to create and update notify subscription")
let updateScope: Set<String> = ["alerts"]
let updateScope: Set<String> = ["8529aae8-cb26-4d49-922e-eb099044bebe"]
expectation.assertForOverFulfill = false

var didUpdate = false
Expand Down Expand Up @@ -183,7 +184,7 @@ final class NotifyTests: XCTestCase {
func testNotifyServerSubscribeAndNotifies() async throws {
let subscribeExpectation = expectation(description: "creates notify subscription")
let messageExpectation = expectation(description: "receives a notify message")
let notifyMessage = NotifyMessage.stub()
let notifyMessage = NotifyMessage.stub(type: "8529aae8-cb26-4d49-922e-eb099044bebe")

var didNotify = false
walletNotifyClientA.subscriptionsPublisher
Expand Down
6 changes: 3 additions & 3 deletions Example/IntegrationTests/Stubs/PushMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import Foundation
import WalletConnectNotify

extension NotifyMessage {
static func stub() -> NotifyMessage {
static func stub(type: String) -> NotifyMessage {
return NotifyMessage(
title: "swift_test",
body: "cad9a52d-9b0f-4aed-9cca-3e9568a079f9",
body: "body",
icon: "https://images.unsplash.com/photo-1581224463294-908316338239?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80",
url: "https://web3inbox.com",
type: "private")
type: type)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ struct NotifyPreferencesView: View {
Toggle(isOn: .init(get: {
viewModel.update[title]?.enabled ?? value.enabled
}, set: { newValue in
viewModel.update[title] = ScopeValue(description: value.description, enabled: newValue)
viewModel.update[title] = ScopeValue(id: value.id, name: value.name, description: value.description, enabled: newValue)
})) {
VStack(alignment: .leading, spacing: 4) {
Text(title)
Text(value.name)
.foregroundColor(.primary)
.font(.system(size: 14, weight: .semibold))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import Foundation

public struct NotifyClientFactory {

public static func create(groupIdentifier: String, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, pushClient: PushClient, crypto: CryptoProvider, notifyHost: String) -> NotifyClient {
public static func create(projectId: String, groupIdentifier: String, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, pushClient: PushClient, crypto: CryptoProvider, notifyHost: String) -> NotifyClient {
let logger = ConsoleLogger(prefix: "🔔",loggingLevel: .debug)
let keyValueStorage = UserDefaults.standard
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let groupKeychainService = GroupKeychainStorage(serviceIdentifier: groupIdentifier)

return NotifyClientFactory.create(
projectId: projectId,
keyserverURL: keyserverURL,
logger: logger,
keyValueStorage: keyValueStorage,
Expand All @@ -24,6 +25,7 @@ public struct NotifyClientFactory {
}

static func create(
projectId: String,
keyserverURL: URL,
logger: ConsoleLogging,
keyValueStorage: KeyValueStorage,
Expand All @@ -46,12 +48,11 @@ public struct NotifyClientFactory {
let deleteNotifySubscriptionRequester = DeleteNotifySubscriptionRequester(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, webDidResolver: webDidResolver, kms: kms, logger: logger, notifyStorage: notifyStorage)
let resubscribeService = NotifyResubscribeService(networkInteractor: networkInteractor, notifyStorage: notifyStorage, logger: logger)

let dappsMetadataStore = CodableStore<AppMetadata>(defaults: keyValueStorage, identifier: NotifyStorageIdntifiers.dappsMetadataStore)
let notifyConfigProvider = NotifyConfigProvider()
let notifyConfigProvider = NotifyConfigProvider(projectId: projectId)

let notifySubscribeRequester = NotifySubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyConfigProvider: notifyConfigProvider, dappsMetadataStore: dappsMetadataStore)
let notifySubscribeRequester = NotifySubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyConfigProvider: notifyConfigProvider)

let notifySubscribeResponseSubscriber = NotifySubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, notifyStorage: notifyStorage, dappsMetadataStore: dappsMetadataStore, notifyConfigProvider: notifyConfigProvider)
let notifySubscribeResponseSubscriber = NotifySubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, notifyStorage: notifyStorage, notifyConfigProvider: notifyConfigProvider)

let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, webDidResolver: webDidResolver, identityClient: identityClient, networkingInteractor: networkInteractor, notifyConfigProvider: notifyConfigProvider, logger: logger, notifyStorage: notifyStorage)

Expand Down
35 changes: 35 additions & 0 deletions Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation

struct NotifyConfig: Codable {
struct NotificationType: Codable {
let id: String
let name: String
let description: String
}
struct ImageUrl: Codable {
let sm: String?
let md: String?
let lg: String?
}
let id: String
let name: String
let homepage: String
let description: String
let dapp_url: String
let image_url: ImageUrl?
let notificationTypes: [NotificationType]

var appDomain: String {
return URL(string: dapp_url)?.host ?? dapp_url
}

var metadata: AppMetadata {
return AppMetadata(
name: name,
description:
description,
url: appDomain,
icons: [image_url?.sm, image_url?.md, image_url?.lg].compactMap { $0 }
)
}
}
33 changes: 33 additions & 0 deletions Sources/WalletConnectNotify/Client/Wallet/NotifyConfigAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation

enum NotifyConfigAPI: HTTPService {

var path: String {
return "/w3i/v1/notify-config"
}

var method: HTTPMethod {
return .get
}

var body: Data? {
return nil
}

var queryParameters: [String : String]? {
switch self {
case .notifyDApps(let projectId, let appDomain):
return ["projectId": projectId, "appDomain": appDomain]
}
}

var additionalHeaderFields: [String : String]? {
return nil
}

var scheme: String {
return "https"
}

case notifyDApps(projectId: String, appDomain: String)
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,48 @@

import Foundation

actor NotifyConfigProvider {
enum Errors: Error {
case invalidUrl

private let projectId: String

private var cache: [String: NotifyConfig] = [:]

init(projectId: String) {
self.projectId = projectId
}

private var cache = [String: Set<NotificationType>]()
func resolveNotifyConfig(appDomain: String) async -> NotifyConfig {
if let config = cache[appDomain] {
return config
}

func getSubscriptionScope(appDomain: String) async throws -> Set<NotificationType> {
if let availableScope = cache[appDomain] {
return availableScope
do {
let httpClient = HTTPNetworkClient(host: "explorer-api.walletconnect.com")
let request = NotifyConfigAPI.notifyDApps(projectId: projectId, appDomain: appDomain)
let response = try await httpClient.request(NotifyConfigResponse.self, at: request)
let config = response.data
cache[appDomain] = config
return config
} catch {
return emptyConfig(appDomain: appDomain)
}
guard let notifyConfigUrl = URL(string: "https://\(appDomain)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl }
let (data, _) = try await URLSession.shared.data(from: notifyConfigUrl)
let config = try JSONDecoder().decode(NotificationConfig.self, from: data)
let availableScope = Set(config.types)
cache[appDomain] = availableScope
return availableScope
}
}

private extension NotifyConfigProvider {

struct NotifyConfigResponse: Codable {
let data: NotifyConfig
}

func getMetadata(appDomain: String) async throws -> AppMetadata {
guard let notifyConfigUrl = URL(string: "https://\(appDomain)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl }
let (data, _) = try await URLSession.shared.data(from: notifyConfigUrl)
let config = try JSONDecoder().decode(NotificationConfig.self, from: data)
return AppMetadata(name: config.name, description: config.description, url: appDomain, icons: config.icons)
func emptyConfig(appDomain: String) -> NotifyConfig {
return NotifyConfig(
id: UUID().uuidString,
name: appDomain,
homepage: "https://\(appDomain)",
description: "",
dapp_url: "https://\(appDomain)",
image_url: nil,
notificationTypes: []
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,37 @@ class NotifySubscriptionsBuilder {
var result = [NotifySubscription]()

for subscription in notifyServerSubscriptions {
let scope = try await buildScope(selectedScope: subscription.scope, appDomain: subscription.appDomain)
guard let metadata = try? await notifyConfigProvider.getMetadata(appDomain: subscription.appDomain),
let topic = try? SymmetricKey(hex: subscription.symKey).derivedTopic() else { continue }

let notifySubscription = NotifySubscription(topic: topic,
account: subscription.account,
relay: RelayProtocolOptions(protocol: "irn", data: nil),
metadata: metadata,
scope: scope,
expiry: subscription.expiry,
symKey: subscription.symKey)
result.append(notifySubscription)
let config = await notifyConfigProvider.resolveNotifyConfig(appDomain: subscription.appDomain)

do {
let topic = try SymmetricKey(hex: subscription.symKey).derivedTopic()
let scope = try await buildScope(selectedScope: subscription.scope, availableScope: config.notificationTypes)

result.append(NotifySubscription(
topic: topic,
account: subscription.account,
relay: RelayProtocolOptions(protocol: "irn", data: nil),
metadata: config.metadata,
scope: scope,
expiry: subscription.expiry,
symKey: subscription.symKey
))
} catch {
continue
}
}

return result
}

private func buildScope(selectedScope: [String], appDomain: String) async throws -> [String: ScopeValue] {
let availableScope = try await notifyConfigProvider.getSubscriptionScope(appDomain: appDomain)
private func buildScope(selectedScope: [String], availableScope: [NotifyConfig.NotificationType]) async throws -> [String: ScopeValue] {
return availableScope.reduce(into: [:]) {
$0[$1.name] = ScopeValue(description: $1.description, enabled: selectedScope.contains($1.name))
$0[$1.id] = ScopeValue(
id: $1.id,
name: $1.name,
description: $1.description,
enabled: selectedScope.contains($1.id)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class NotifySubscribeRequester {
private let kms: KeyManagementService
private let logger: ConsoleLogging
private let webDidResolver: NotifyWebDidResolver
private let dappsMetadataStore: CodableStore<AppMetadata>
private let notifyConfigProvider: NotifyConfigProvider

init(keyserverURL: URL,
Expand All @@ -22,33 +21,29 @@ class NotifySubscribeRequester {
logger: ConsoleLogging,
kms: KeyManagementService,
webDidResolver: NotifyWebDidResolver,
notifyConfigProvider: NotifyConfigProvider,
dappsMetadataStore: CodableStore<AppMetadata>
notifyConfigProvider: NotifyConfigProvider
) {
self.keyserverURL = keyserverURL
self.identityClient = identityClient
self.networkingInteractor = networkingInteractor
self.logger = logger
self.kms = kms
self.webDidResolver = webDidResolver
self.dappsMetadataStore = dappsMetadataStore
self.notifyConfigProvider = notifyConfigProvider
}

@discardableResult func subscribe(appDomain: String, account: Account) async throws -> NotifySubscriptionPayload.Wrapper {

logger.debug("Subscribing for Notify, dappUrl: \(appDomain)")

let metadata = try await notifyConfigProvider.getMetadata(appDomain: appDomain)
let config = try await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain)

let peerPublicKey = try await webDidResolver.resolveAgreementKey(domain: metadata.url)
let peerPublicKey = try await webDidResolver.resolveAgreementKey(domain: appDomain)
let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString()

let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey)

let responseTopic = keysY.derivedTopic()

dappsMetadataStore.set(metadata, forKey: responseTopic)

try kms.setSymmetricKey(keysY.sharedKey, for: subscribeTopic)
try kms.setAgreementSecret(keysY, topic: responseTopic)
Expand Down Expand Up @@ -80,10 +75,15 @@ class NotifySubscribeRequester {
}

private func createJWTWrapper(dappPubKey: DIDKey, subscriptionAccount: Account, appDomain: String) async throws -> NotifySubscriptionPayload.Wrapper {
let types = try await notifyConfigProvider.getSubscriptionScope(appDomain: appDomain)
let scope = types.map{$0.name}.joined(separator: " ")
let config = await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain)
let app = DIDWeb(host: appDomain)
let jwtPayload = NotifySubscriptionPayload(dappPubKey: dappPubKey, keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, app: app, scope: scope)
let jwtPayload = NotifySubscriptionPayload(
dappPubKey: dappPubKey,
keyserver: keyserverURL,
subscriptionAccount: subscriptionAccount,
app: app,
scope: config.notificationTypes.map { $0.id }.joined(separator: " ")
)
return try identityClient.signAndCreateWrapper(
payload: jwtPayload,
account: subscriptionAccount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,20 @@ class NotifySubscribeResponseSubscriber {
private let logger: ConsoleLogging
private let notifyStorage: NotifyStorage
private let groupKeychainStorage: KeychainStorageProtocol
private let dappsMetadataStore: CodableStore<AppMetadata>
private let notifyConfigProvider: NotifyConfigProvider

init(networkingInteractor: NetworkInteracting,
kms: KeyManagementServiceProtocol,
logger: ConsoleLogging,
groupKeychainStorage: KeychainStorageProtocol,
notifyStorage: NotifyStorage,
dappsMetadataStore: CodableStore<AppMetadata>,
notifyConfigProvider: NotifyConfigProvider
) {
self.networkingInteractor = networkingInteractor
self.kms = kms
self.logger = logger
self.groupKeychainStorage = groupKeychainStorage
self.notifyStorage = notifyStorage
self.dappsMetadataStore = dappsMetadataStore
self.notifyConfigProvider = notifyConfigProvider
subscribeForSubscriptionResponse()
}
Expand Down
1 change: 1 addition & 0 deletions Sources/WalletConnectNotify/Notify.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class Notify {
}
Push.configure(pushHost: config.pushHost, environment: config.environment)
return NotifyClientFactory.create(
projectId: Networking.projectId,
groupIdentifier: config.groupIdentifier,
networkInteractor: Networking.interactor,
pairingRegisterer: Pair.registerer,
Expand Down
1 change: 0 additions & 1 deletion Sources/WalletConnectNotify/NotifyStorageIdntifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ enum NotifyStorageIdntifiers {
static let notifySubscription = "com.walletconnect.notify.notifySubscription"

static let notifyMessagesRecords = "com.walletconnect.sdk.notifyMessagesRecords"
static let dappsMetadataStore = "com.walletconnect.sdk.dappsMetadataStore"
static let coldStartStore = "com.walletconnect.sdk.coldStartStore"
}
Loading

0 comments on commit 081f2a3

Please sign in to comment.