Merge pull request #4011 from oa-s/#3989

Support for update WalletConnect v2 SDK #3989
pull/4056/head
Crypto Pank 3 years ago committed by GitHub
commit 88cb9116e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      AlphaWallet.xcodeproj/project.pbxproj
  2. 4
      AlphaWallet.xcworkspace/xcshareddata/swiftpm/Package.resolved
  3. 8
      AlphaWallet/AppCoordinator.swift
  4. 42
      AlphaWallet/InCoordinator.swift
  5. 1
      AlphaWallet/Settings/Types/Constants.swift
  6. 54
      AlphaWallet/WalletConnect/Coordinator/WalletConnectCoordinator.swift
  7. 40
      AlphaWallet/WalletConnect/Providers/v1/WalletConnectV1Provider.swift
  8. 76
      AlphaWallet/WalletConnect/Providers/v2/WalletConnectV2Provider.swift
  9. 22
      AlphaWallet/WalletConnect/WalletConnectServer.swift
  10. 6
      AlphaWallet/WalletConnect/WalletConnectServerProvider.swift
  11. 4
      AlphaWalletTests/Settings/ConfigTests.swift

@ -7291,7 +7291,7 @@
repositoryURL = "https://github.com/WalletConnect/WalletConnectSwiftV2";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.4.0;
minimumVersion = 0.5.0;
};
};
/* End XCRemoteSwiftPackageReference section */

@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/WalletConnect/WalletConnectSwiftV2",
"state": {
"branch": null,
"revision": "784eafb6c40965addf6d4c0ddee70ad413de4c84",
"version": "0.4.0"
"revision": "5ea0265d34f18ec48d71fc299adacecdacc904bb",
"version": "0.5.0"
}
}
]

@ -3,6 +3,7 @@
import Foundation
import UIKit
import PromiseKit
import Combine
class AppCoordinator: NSObject, Coordinator {
private let config = Config()
@ -87,14 +88,14 @@ class AppCoordinator: NSObject, Coordinator {
return service
}()
private lazy var selectedWalletSessionsSubject = CurrentValueSubject<ServerDictionary<WalletSession>, Never>(.init())
private lazy var walletConnectCoordinator: WalletConnectCoordinator = {
let coordinator = WalletConnectCoordinator(keystore: keystore, navigationController: navigationController, analyticsCoordinator: analyticsService, config: config)
let coordinator = WalletConnectCoordinator(keystore: keystore, navigationController: navigationController, analyticsCoordinator: analyticsService, config: config, sessionsSubject: selectedWalletSessionsSubject)
return coordinator
}()
init(window: UIWindow, analyticsService: AnalyticsServiceType, keystore: Keystore, navigationController: UINavigationController = .withOverridenBarAppearence()) throws {
self.navigationController = navigationController
self.window = window
self.analyticsService = analyticsService
@ -213,7 +214,8 @@ class AppCoordinator: NSObject, Coordinator {
walletBalanceCoordinator: walletBalanceCoordinator,
coinTickersFetcher: coinTickersFetcher,
tokenActionsService: tokenActionsService,
walletConnectCoordinator: walletConnectCoordinator
walletConnectCoordinator: walletConnectCoordinator,
sessionsSubject: selectedWalletSessionsSubject
)
coordinator.delegate = self

@ -23,7 +23,6 @@ class InCoordinator: NSObject, Coordinator {
private let assetDefinitionStore: AssetDefinitionStore
private let appTracker: AppTracker
private var transactionsStorages = ServerDictionary<TransactionsStorage>()
private (set) var walletSessions = ServerDictionary<WalletSession>()
private let analyticsCoordinator: AnalyticsCoordinator
private let restartQueue: RestartTaskQueue
private var callForAssetAttributeCoordinators = ServerDictionary<CallForAssetAttributeCoordinator>() {
@ -97,6 +96,7 @@ class InCoordinator: NSObject, Coordinator {
}()
private let accountsCoordinator: AccountsCoordinator
private var cancellable = Set<AnyCancellable>()
private let sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>
var presentationNavigationController: UINavigationController {
if let nc = tabBarController.viewControllers?.first as? UINavigationController {
@ -125,8 +125,10 @@ class InCoordinator: NSObject, Coordinator {
walletBalanceCoordinator: WalletBalanceCoordinatorType,
coinTickersFetcher: CoinTickersFetcherType,
tokenActionsService: TokenActionsServiceType,
walletConnectCoordinator: WalletConnectCoordinator
walletConnectCoordinator: WalletConnectCoordinator,
sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never> = .init(.init())
) {
self.sessionsSubject = sessionsSubject
self.walletConnectCoordinator = walletConnectCoordinator
self.navigationController = navigationController
self.wallet = wallet
@ -185,7 +187,7 @@ class InCoordinator: NSObject, Coordinator {
}
private func createActivitiesService() -> ActivitiesServiceType {
return ActivitiesService(config: config, sessions: walletSessions, assetDefinitionStore: assetDefinitionStore, eventsActivityDataStore: eventsActivityDataStore, eventsDataStore: eventsDataStore, transactionCollection: transactionsCollection, queue: queue, tokensDataStore: tokensDataStore)
return ActivitiesService(config: config, sessions: sessionsSubject.value, assetDefinitionStore: assetDefinitionStore, eventsActivityDataStore: eventsActivityDataStore, eventsDataStore: eventsDataStore, transactionCollection: transactionsCollection, queue: queue, tokensDataStore: tokensDataStore)
}
private func setupWatchingTokenScriptFileChangesToFetchEvents() {
@ -257,13 +259,15 @@ class InCoordinator: NSObject, Coordinator {
}
private func setupWalletSessions() {
walletSessions = .init()
var walletSessions: ServerDictionary<WalletSession> = .init()
for each in config.enabledServers {
let balanceCoordinator = BalanceCoordinator(wallet: wallet, server: each, walletBalanceCoordinator: walletBalanceCoordinator)
let session = WalletSession(account: wallet, server: each, config: config, balanceCoordinator: balanceCoordinator)
walletSessions[each] = session
}
sessionsSubject.send(walletSessions)
}
private lazy var transactionsCollection: TransactionCollection = createTransactionsCollection()
@ -378,11 +382,11 @@ class InCoordinator: NSObject, Coordinator {
}
private func createTokensCoordinator(promptBackupCoordinator: PromptBackupCoordinator, activitiesService: ActivitiesServiceType) -> TokensCoordinator {
promptBackupCoordinator.listenToNativeCryptoCurrencyBalance(withWalletSessions: walletSessions)
promptBackupCoordinator.listenToNativeCryptoCurrencyBalance(withWalletSessions: sessionsSubject.value)
pollEthereumEvents(tokensDataStore: tokensDataStore)
let coordinator = TokensCoordinator(
sessions: walletSessions,
sessions: sessionsSubject.value,
keystore: keystore,
config: config,
tokensDataStore: tokensDataStore,
@ -406,7 +410,7 @@ class InCoordinator: NSObject, Coordinator {
private func createTransactionCoordinator(promptBackupCoordinator: PromptBackupCoordinator, transactionsCollection: TransactionCollection) -> TransactionCoordinator {
let transactionDataCoordinator = TransactionDataCoordinator(
sessions: walletSessions,
sessions: sessionsSubject.value,
transactionCollection: transactionsCollection,
keystore: keystore,
tokensDataStore: tokensDataStore,
@ -415,7 +419,7 @@ class InCoordinator: NSObject, Coordinator {
let coordinator = TransactionCoordinator(
analyticsCoordinator: analyticsCoordinator,
sessions: walletSessions,
sessions: sessionsSubject.value,
transactionsCollection: transactionsCollection,
dataCoordinator: transactionDataCoordinator
)
@ -428,7 +432,7 @@ class InCoordinator: NSObject, Coordinator {
}
private func createActivityCoordinator(activitiesService: ActivitiesServiceType) -> ActivitiesCoordinator {
let coordinator = ActivitiesCoordinator(analyticsCoordinator: analyticsCoordinator, sessions: walletSessions, activitiesService: activitiesService, keystore: keystore, wallet: wallet)
let coordinator = ActivitiesCoordinator(analyticsCoordinator: analyticsCoordinator, sessions: sessionsSubject.value, activitiesService: activitiesService, keystore: keystore, wallet: wallet)
coordinator.delegate = self
coordinator.start()
coordinator.rootViewController.tabBarItem = UITabBarController.Tabs.activities.tabBarItem
@ -450,7 +454,7 @@ class InCoordinator: NSObject, Coordinator {
let coordinator = SettingsCoordinator(
keystore: keystore,
config: config,
sessions: walletSessions,
sessions: sessionsSubject.value,
restartQueue: restartQueue,
promptBackupCoordinator: promptBackupCoordinator,
analyticsCoordinator: analyticsCoordinator,
@ -482,7 +486,7 @@ class InCoordinator: NSObject, Coordinator {
viewControllers.append(transactionCoordinator.navigationController)
}
let browserCoordinator = createBrowserCoordinator(sessions: walletSessions, browserOnly: false, analyticsCoordinator: analyticsCoordinator)
let browserCoordinator = createBrowserCoordinator(sessions: sessionsSubject.value, browserOnly: false, analyticsCoordinator: analyticsCoordinator)
viewControllers.append(browserCoordinator.navigationController)
let settingsCoordinator = createSettingsCoordinator(keystore: keystore, promptBackupCoordinator: promptBackupCoordinator)
@ -511,12 +515,12 @@ class InCoordinator: NSObject, Coordinator {
}
func showPaymentFlow(for type: PaymentFlow, server: RPCServer, navigationController: UINavigationController) {
switch (type, walletSessions[server].account.type) {
switch (type, sessionsSubject.value[server].account.type) {
case (.send, .real), (.request, _):
let coordinator = PaymentCoordinator(
navigationController: navigationController,
flow: type,
session: walletSessions[server],
session: sessionsSubject.value[server],
keystore: keystore,
tokensDataStore: tokensDataStore,
ethPrice: nativeCryptoCurrencyPrices[server],
@ -553,7 +557,7 @@ class InCoordinator: NSObject, Coordinator {
func importPaidSignedOrder(signedOrder: SignedOrder, tokenObject: TokenObject, inViewController viewController: ImportMagicTokenViewController, completion: @escaping (Bool) -> Void) {
guard let navigationController = viewController.navigationController else { return }
let session = walletSessions[tokenObject.server]
let session = sessionsSubject.value[tokenObject.server]
claimOrderCoordinatorCompletionBlock = completion
let coordinator = ClaimPaidOrderCoordinator(navigationController: navigationController, keystore: keystore, session: session, tokenObject: tokenObject, signedOrder: signedOrder, ethPrice: nativeCryptoCurrencyPrices[session.server], analyticsCoordinator: analyticsCoordinator)
coordinator.delegate = self
@ -578,7 +582,7 @@ class InCoordinator: NSObject, Coordinator {
private func createNativeCryptoCurrencyPriceSubscribable(forServer server: RPCServer) -> Subscribable<Double> {
let etherToken = MultipleChainsTokensDataStore.functional.etherToken(forServer: server).addressAndRPCServer
let subscription = walletSessions[server].balanceCoordinator.subscribableTokenBalance(etherToken)
let subscription = sessionsSubject.value[server].balanceCoordinator.subscribableTokenBalance(etherToken)
return subscription.map({ viewModel -> Double? in
return viewModel.ticker?.price_usd
}, on: .main)
@ -593,7 +597,7 @@ class InCoordinator: NSObject, Coordinator {
}
private func createCryptoCurrencyBalanceSubscribable(forServer server: RPCServer) -> Subscribable<BigInt> {
let subscription = walletSessions[server].balanceCoordinator.subscribableEthBalanceViewModel
let subscription = sessionsSubject.value[server].balanceCoordinator.subscribableEthBalanceViewModel
return subscription.map({ viewModel -> BigInt? in
return viewModel.value
}, on: .main)
@ -860,7 +864,7 @@ extension InCoordinator: WalletConnectCoordinatorDelegate {
extension InCoordinator: CanOpenURL {
private func open(url: URL, in viewController: UIViewController) {
//TODO duplication of code to set up a BrowserCoordinator when creating the application's tabbar
let browserCoordinator = createBrowserCoordinator(sessions: walletSessions, browserOnly: true, analyticsCoordinator: analyticsCoordinator)
let browserCoordinator = createBrowserCoordinator(sessions: sessionsSubject.value, browserOnly: true, analyticsCoordinator: analyticsCoordinator)
let controller = browserCoordinator.navigationController
browserCoordinator.open(url: url, animated: false)
controller.makePresentationFullScreenForiOS13Migration()
@ -984,7 +988,7 @@ extension InCoordinator: ActivityViewControllerDelegate {
func speedupTransaction(transactionId: String, server: RPCServer, viewController: ActivityViewController) {
guard let transaction = transactionsStorages[server].transaction(withTransactionId: transactionId) else { return }
let ethPrice = nativeCryptoCurrencyPrices[transaction.server]
let session = walletSessions[transaction.server]
let session = sessionsSubject.value[transaction.server]
guard let coordinator = ReplaceTransactionCoordinator(analyticsCoordinator: analyticsCoordinator, keystore: keystore, ethPrice: ethPrice, presentingViewController: viewController, session: session, transaction: transaction, mode: .speedup) else { return }
coordinator.delegate = self
coordinator.start()
@ -994,7 +998,7 @@ extension InCoordinator: ActivityViewControllerDelegate {
func cancelTransaction(transactionId: String, server: RPCServer, viewController: ActivityViewController) {
guard let transaction = transactionsStorages[server].transaction(withTransactionId: transactionId) else { return }
let ethPrice = nativeCryptoCurrencyPrices[transaction.server]
let session = walletSessions[transaction.server]
let session = sessionsSubject.value[transaction.server]
guard let coordinator = ReplaceTransactionCoordinator(analyticsCoordinator: analyticsCoordinator, keystore: keystore, ethPrice: ethPrice, presentingViewController: viewController, session: session, transaction: transaction, mode: .cancel) else { return }
coordinator.delegate = self
coordinator.start()

@ -206,6 +206,7 @@ public struct Constants {
static let icons = [
"https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"
]
static let connectionTimeout: TimeInterval = 10
}
//CurrencyFormatter

@ -9,6 +9,7 @@ import UIKit
import WalletConnectSwift
import PromiseKit
import Result
import Combine
protocol RequestAddCustomChainProvider: NSObjectProtocol {
func requestAddCustomChain(server: RPCServer, callbackId: SwitchCustomChainCallbackId, customChain: WalletAddEthereumChainObject)
@ -17,25 +18,28 @@ protocol RequestSwitchChainProvider: NSObjectProtocol {
func requestSwitchChain(server: RPCServer, currentUrl: URL?, callbackID: SwitchCustomChainCallbackId, targetChain: WalletSwitchEthereumChainObject)
}
protocol WalletConnectCoordinatorDelegate: CanOpenURL, SendTransactionAndFiatOnRampDelegate, WalletSessionListProvider, NativeCryptoCurrencyPricesProvider, RequestAddCustomChainProvider, RequestSwitchChainProvider {
protocol WalletConnectCoordinatorDelegate: CanOpenURL, SendTransactionAndFiatOnRampDelegate/*, WalletSessionListProvider*/, NativeCryptoCurrencyPricesProvider, RequestAddCustomChainProvider, RequestSwitchChainProvider {
func universalScannerSelected(in coordinator: WalletConnectCoordinator)
}
class WalletConnectCoordinator: NSObject, Coordinator {
private lazy var walletConnectV2service: WalletConnectV2Provider = {
let walletConnectV2service = WalletConnectV2Provider(keystore: keystore)
let walletConnectV2service = WalletConnectV2Provider(sessionsSubject: sessionsSubject)
walletConnectV2service.delegate = self
walletConnectV2service.sessionProvider = delegate
return walletConnectV2service
}()
private lazy var provider: WalletConnectServerProviderType = {
let provider = WalletConnectServerProvider()
let walletConnectV1service = WalletConnectV1Provider(keystore: keystore)
private lazy var walletConnectV1service: WalletConnectV1Provider = {
let walletConnectV1service = WalletConnectV1Provider(sessionsSubject: sessionsSubject)
walletConnectV1service.delegate = self
return walletConnectV1service
}()
private lazy var provider: WalletConnectServerProviderType = {
let provider = WalletConnectServerProvider()
provider.register(service: walletConnectV1service)
provider.register(service: walletConnectV2service)
@ -43,12 +47,6 @@ class WalletConnectCoordinator: NSObject, Coordinator {
}()
private let navigationController: UINavigationController
var coordinators: [Coordinator] = []
var sessionsSubscribable: Subscribable<[AlphaWallet.WalletConnect.Session]> {
provider.sessionsSubscribable
}
private let keystore: Keystore
private let analyticsCoordinator: AnalyticsCoordinator
private let config: Config
@ -58,13 +56,16 @@ class WalletConnectCoordinator: NSObject, Coordinator {
ServersCoordinator.serversOrdered.filter { config.enabledServers.contains($0) }
}
private weak var sessionsViewController: WalletConnectSessionsViewController?
weak var delegate: WalletConnectCoordinatorDelegate? {
didSet {
walletConnectV2service.sessionProvider = delegate
}
private var sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>
weak var delegate: WalletConnectCoordinatorDelegate?
var coordinators: [Coordinator] = []
var sessionsSubscribable: Subscribable<[AlphaWallet.WalletConnect.Session]> {
provider.sessionsSubscribable
}
init(keystore: Keystore, navigationController: UINavigationController, analyticsCoordinator: AnalyticsCoordinator, config: Config) {
init(keystore: Keystore, navigationController: UINavigationController, analyticsCoordinator: AnalyticsCoordinator, config: Config, sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>) {
self.sessionsSubject = sessionsSubject
self.config = config
self.keystore = keystore
self.navigationController = navigationController
@ -244,17 +245,15 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
}
}
func server(_ server: WalletConnectServerType, didConnect walletConnectSession: AlphaWallet.WalletConnect.Session) {
func server(_ server: WalletConnectServer, didConnect walletConnectSession: AlphaWallet.WalletConnect.Session) {
infoLog("WalletConnect didConnect session: \(walletConnectSession.identifier)")
resetSessionsToRemoveLoadingIfNeeded()
}
func server(_ server: WalletConnectServerType, action: AlphaWallet.WalletConnect.Action, request: AlphaWallet.WalletConnect.Session.Request, session walletConnectSession: AlphaWallet.WalletConnect.Session) {
func server(_ server: WalletConnectServer, action: AlphaWallet.WalletConnect.Action, request: AlphaWallet.WalletConnect.Session.Request, session walletConnectSession: AlphaWallet.WalletConnect.Session) {
infoLog("WalletConnect action: \(action)")
guard let walletSession = request.server.flatMap({ server in
delegate.flatMap { $0.walletSessions[safe: server] }
}) else {
guard let walletSession = request.server.flatMap({ sessionsSubject.value[safe: $0] }) else {
try? server.respond(.init(error: .requestRejected), request: request)
return
}
@ -327,10 +326,9 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
infoLog("WalletConnect switchChain: \(targetChain)")
//NOTE: DappRequestSwitchExistingChainCoordinator requires current selected server, (from dapp impl) if we pass server that isn't currently selected it will ask to switch server 2 times, for this we return server that is already selected
func firstEnabledRPCServer() -> RPCServer? {
let session = targetChain.server
.flatMap { server in delegate.flatMap { $0.walletSessions[safe: server]?.server } }
let server = targetChain.server.flatMap { server in sessionsSubject.value[safe: server]?.server }
return session ?? walletConnectSession.servers.first
return server ?? walletConnectSession.servers.first
}
guard let server = firstEnabledRPCServer(), targetChain.server != nil else {
@ -413,13 +411,13 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
}
}
func server(_ server: WalletConnectServerType, didFail error: Error) {
func server(_ server: WalletConnectServer, didFail error: Error) {
infoLog("WalletConnect didFail error: \(error)")
let errorMessage = R.string.localizable.walletConnectFailureTitle()
displayErrorMessage(errorMessage)
}
func server(_ server: WalletConnectServerType, tookTooLongToConnectToUrl url: AlphaWallet.WalletConnect.ConnectionUrl) {
func server(_ server: WalletConnectServer, tookTooLongToConnectToUrl url: AlphaWallet.WalletConnect.ConnectionUrl) {
if Features.isUsingAppEnforcedTimeoutForMakingWalletConnectConnections {
infoLog("WalletConnect app-enforced timeout for waiting for new connection")
analyticsCoordinator.log(action: Analytics.Action.walletConnectConnectionTimeout, properties: [
@ -432,7 +430,7 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
}
}
func server(_ server: WalletConnectServerType, shouldConnectFor sessionProposal: AlphaWallet.WalletConnect.SessionProposal, completion: @escaping (AlphaWallet.WalletConnect.SessionProposalResponse) -> Void) {
func server(_ server: WalletConnectServer, shouldConnectFor sessionProposal: AlphaWallet.WalletConnect.SessionProposal, completion: @escaping (AlphaWallet.WalletConnect.SessionProposalResponse) -> Void) {
infoLog("WalletConnect shouldConnectFor connection: \(sessionProposal)")
firstly {
WalletConnectToSessionCoordinator.promise(navigationController, coordinator: self, sessionProposal: sessionProposal, serverChoices: serverChoices, analyticsCoordinator: analyticsCoordinator, config: config)

@ -9,10 +9,9 @@ import Foundation
import WalletConnectSwift
import AlphaWalletAddress
import PromiseKit
import Combine
class WalletConnectV1Provider: WalletConnectServerType {
static let connectionTimeout: TimeInterval = 10
class WalletConnectV1Provider: WalletConnectServer {
enum Keys {
static let storageFileKey = "walletConnectSessions-v1"
}
@ -45,33 +44,36 @@ class WalletConnectV1Provider: WalletConnectServerType {
return handler
}()
private let keystore: Keystore
private var subscription: Subscribable<Set<Wallet>>.SubscribableKey!
private let sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>
private var cancelable = Set<AnyCancellable>()
init(keystore: Keystore) {
self.keystore = keystore
self.storage = .init(fileName: Keys.storageFileKey, defaultValue: [])
init(sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>, storage: SubscribableFileStorage<[SingleServerWalletConnectSession]> = .init(fileName: Keys.storageFileKey, defaultValue: [])) {
self.sessionsSubject = sessionsSubject
self.storage = storage
server.register(handler: requestHandler)
subscription = keystore.subscribableWallets.subscribe { [weak self] _ in
sessionsSubject
.filter { !$0.isEmpty }
.sink { [weak self] sessions in
guard let strongSelf = self else { return }
let wallets = Array(Set(sessions.values.map { $0.account.address.eip55String }))
for each in strongSelf.storage.value {
let walletInfo = strongSelf.walletInfo(choice: .connect(each.server))
let walletInfo = strongSelf.walletInfo(choice: .connect(each.server), wallets: wallets)
try? strongSelf.server.updateSession(each.session, with: walletInfo)
}
}
}.store(in: &cancelable)
}
deinit {
debugLog("[WalletConnect] WalletConnectServer.deinit")
debugLog("[WalletConnect] WalletConnectV1Provider.deinit")
server.unregister(handler: requestHandler)
}
func connect(url: AlphaWallet.WalletConnect.ConnectionUrl) throws {
guard case .v1(let wcUrl) = url else { return }
let timer = Timer.scheduledTimer(withTimeInterval: Self.connectionTimeout, repeats: false) { _ in
let timer = Timer.scheduledTimer(withTimeInterval: Constants.WalletConnect.connectionTimeout, repeats: false) { _ in
let isStillWatching = self.connectionTimeoutTimers[wcUrl] != nil
debugLog("WalletConnect app-enforced connection timer is up for: \(wcUrl.absoluteString) isStillWatching: \(isStillWatching)")
if isStillWatching {
@ -93,8 +95,8 @@ class WalletConnectV1Provider: WalletConnectServerType {
func updateSession(session: AlphaWallet.WalletConnect.Session, servers: [RPCServer]) throws {
guard let index = storage.value.firstIndex(where: { $0 == session }), let server = servers.first else { return }
storage.value[index].server = server
let walletInfo = walletInfo(choice: .connect(server))
let wallets = Array(Set(sessionsSubject.value.values.map { $0.account.address.eip55String }))
let walletInfo = walletInfo(choice: .connect(server), wallets: wallets)
try self.server.updateSession(storage.value[index].session, with: walletInfo)
}
@ -139,11 +141,10 @@ class WalletConnectV1Provider: WalletConnectServerType {
return server.openSessions().contains(where: { $0.dAppInfo.peerId == nativeSession.session.dAppInfo.peerId })
}
private func walletInfo(choice: AlphaWallet.WalletConnect.SessionProposalResponse) -> Session.WalletInfo {
private func walletInfo(choice: AlphaWallet.WalletConnect.SessionProposalResponse, wallets: [String]) -> Session.WalletInfo {
func peerId(approved: Bool) -> String {
return approved ? UUID().uuidString : String()
}
let wallets = keystore.wallets.map { $0.address.eip55String }
return Session.WalletInfo(
approved: choice.shouldProceed,
@ -205,6 +206,7 @@ extension WalletConnectV1Provider: ServerDelegate {
func server(_ server: Server, shouldStart session: Session, completion: @escaping (Session.WalletInfo) -> Void) {
connectionTimeoutTimers[session.url] = nil
let wallets = Array(Set(sessionsSubject.value.values.map { $0.account.address.eip55String }))
DispatchQueue.main.async {
if let delegate = self.delegate {
@ -213,7 +215,7 @@ extension WalletConnectV1Provider: ServerDelegate {
delegate.server(self, shouldConnectFor: sessionProposal) { [weak self] choice in
guard let strongSelf = self, let server = choice.server else { return }
let info = strongSelf.walletInfo(choice: choice)
let info = strongSelf.walletInfo(choice: choice, wallets: wallets)
if let index = strongSelf.storage.value.firstIndex(where: { $0 == session }) {
strongSelf.storage.value[index] = .init(session: session, server: server)
} else {
@ -222,7 +224,7 @@ extension WalletConnectV1Provider: ServerDelegate {
completion(info)
}
} else {
let info = self.walletInfo(choice: .cancel)
let info = self.walletInfo(choice: .cancel, wallets: wallets)
completion(info)
}
}

@ -8,16 +8,13 @@
import Foundation
import WalletConnect
import WalletConnectUtils
import Combine
protocol NativeCryptoCurrencyPricesProvider: class {
var nativeCryptoCurrencyPrices: ServerDictionary<Subscribable<Double>> { get }
}
protocol WalletSessionListProvider: class {
var walletSessions: ServerDictionary<WalletSession> { get }
}
class WalletConnectV2Provider: WalletConnectServerType {
class WalletConnectV2Provider: WalletConnectServer {
private var pendingSessionProposal: Session.Proposal?
private var client: WalletConnectClient = {
@ -29,7 +26,7 @@ class WalletConnectV2Provider: WalletConnectServerType {
let projectId = Constants.Credentials.walletConnectProjectId
let relayHost = Constants.WalletConnect.relayURL.host!
return WalletConnectClient(metadata: metadata, projectId: projectId, isController: true, relayHost: relayHost)
return WalletConnectClient(metadata: metadata, projectId: projectId, relayHost: relayHost)
}()
private var pendingSessionStack: [Session.Proposal] = []
@ -42,7 +39,6 @@ class WalletConnectV2Provider: WalletConnectServerType {
}()
private let storage: SubscribableFileStorage<[MultiServerWalletConnectSession]>
weak var delegate: WalletConnectServerDelegate?
weak var sessionProvider: WalletSessionListProvider?
enum Keys {
static let storageFileKey = "walletConnectSessions-v2"
@ -51,21 +47,37 @@ class WalletConnectV2Provider: WalletConnectServerType {
private let config: Config = Config()
//NOTE: Since the connection url doesn't we are getting in `func connect(url: AlphaWallet.WalletConnect.ConnectionUrl) throws` isn't the same of what we got in
//`SessionProposal` we are not able to manage connection timeout. As well as we are not able to mach topics of urls. connection timeout isn't supported for now for v2.
private let keystore: Keystore
private var subscription: Subscribable<Set<Wallet>>.SubscribableKey!
init(keystore: Keystore) {
self.keystore = keystore
self.storage = .init(fileName: Keys.storageFileKey, defaultValue: [])
private var sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>
private var cancelable = Set<AnyCancellable>()
//NOTE: we support only single account session as WalletConnects request doesn't provide a wallets address to sign transaction or some other method, so we cant figure out wallet address to sign, so for now we use only active wallet session address
init(sessionsSubject: CurrentValueSubject<ServerDictionary<WalletSession>, Never>, storage: SubscribableFileStorage<[MultiServerWalletConnectSession]> = .init(fileName: Keys.storageFileKey, defaultValue: [])) {
self.sessionsSubject = sessionsSubject
self.storage = storage
client.delegate = self
subscription = keystore.subscribableWallets.subscribe { [weak self] wallets in
guard let strongSelf = self, let values = wallets else { return }
let wallets = Set(values.map { $0.address.eip55String })
for each in strongSelf.storage.value {
strongSelf.client.update(topic: each.identifier.description, accounts: wallets)
//NOTE: skip empty sessions event
sessionsSubject
.filter { !$0.isEmpty }
.sink { [weak self] sessions in
self?.reloadSessions(sessions: sessions)
}.store(in: &cancelable)
}
private func reloadSessions(sessions: ServerDictionary<WalletSession>) {
func allAccountsInEip155(sessionServers: [RPCServer]) -> [String] {
let availableSessions: [WalletSession] = sessions.values.filter { sessionServers.contains($0.server) }
let wallets = Set(sessions.values.map { $0.account })
//NOTE: idelly here is going to be one wallet address
return wallets.map { wallet -> [String] in
availableSessions.map { eip155URLCoder.encode(rpcServer: $0.server, address: wallet.address) }
}.flatMap { $0 }
}
for each in storage.value {
let accounts = Set(allAccountsInEip155(sessionServers: each.servers).compactMap { Account($0) })
guard !accounts.isEmpty else { return }
try? client.update(topic: each.identifier.description, accounts: accounts)
}
}
@ -235,7 +247,7 @@ extension WalletConnectV2Provider: WalletConnectClientDelegate {
}
}
func didUpdate(sessionTopic: String, accounts: Set<String>) {
func didUpdate(sessionTopic: String, accounts: Set<Account>) {
debugLog("[RESPONDER] WC: Did receive session update")
}
@ -276,7 +288,7 @@ extension WalletConnectV2Provider: WalletConnectClientDelegate {
func reject(sessionProposal: Session.Proposal) {
debugLog("[RESPONDER] WC: Did reject session proposal: \(sessionProposal)")
client.reject(proposal: sessionProposal, reason: Reason(code: 0, message: "reject"))
client.reject(proposal: sessionProposal, reason: RejectionReason.disapprovedChains)
completion()
}
@ -301,15 +313,16 @@ extension WalletConnectV2Provider: WalletConnectClientDelegate {
return
}
let accounts = strongSelf.allAccountsInEip155(request: sessionRequest)
guard !accounts.isEmpty else {
let accounts = strongSelf.allAccountsInEip155(sessionServers: sessionRequest.servers)
let accountSet = Set(accounts.compactMap { Account($0) })
debugLog("[RESPONDER] WC: Did accept session proposal: \(sessionProposal) accounts: \(accountSet)")
guard !accountSet.isEmpty else {
strongSelf.pendingSessionProposal = .none
reject(sessionProposal: sessionProposal)
return
}
debugLog("[RESPONDER] WC: Did accept session proposal: \(sessionProposal) accounts: \(Set(accounts))")
strongSelf.client.approve(proposal: sessionProposal, accounts: Set(accounts))
strongSelf.client.approve(proposal: sessionProposal, accounts: accountSet)
strongSelf.pendingSessionProposal = .none
completion()
@ -323,15 +336,12 @@ extension WalletConnectV2Provider: WalletConnectClientDelegate {
}
}
private func allAccountsInEip155(request: AlphaWallet.WalletConnect.SessionProposal) -> [String] {
let sessions: [WalletSession] = sessionProvider.flatMap {
$0.walletSessions.values.filter {
request.servers.contains($0.server)
}
} ?? []
private func allAccountsInEip155(sessionServers: [RPCServer]) -> [String] {
let availableSessions: [WalletSession] = sessionsSubject.value.values.filter { sessionServers.contains($0.server) }
let wallets = Set(availableSessions.map { $0.account })
return keystore.wallets.map { wallet -> [String] in
sessions.map {
return wallets.map { wallet -> [String] in
availableSessions.map {
eip155URLCoder.encode(rpcServer: $0.server, address: wallet.address)
}
}.flatMap { $0 }

@ -16,31 +16,23 @@ protocol WalletConnectResponder {
func respond(_ response: AlphaWallet.WalletConnect.Response, request: AlphaWallet.WalletConnect.Session.Request) throws
}
protocol UpdateableSessionServerType: class {
func updateSession(session: AlphaWallet.WalletConnect.Session, servers: [RPCServer]) throws
}
protocol ReconnectableSessionServerType: class {
func reconnectSession(session: AlphaWallet.WalletConnect.Session) throws
}
protocol WalletConnectServerType: WalletConnectResponder, UpdateableSessionServerType {
protocol WalletConnectServer: WalletConnectResponder {
var sessionsSubscribable: Subscribable<[AlphaWallet.WalletConnect.Session]> { get }
var delegate: WalletConnectServerDelegate? { get set }
func connect(url: AlphaWallet.WalletConnect.ConnectionUrl) throws
func session(forIdentifier identifier: AlphaWallet.WalletConnect.SessionIdentifier) -> AlphaWallet.WalletConnect.Session?
func reconnectSession(session: AlphaWallet.WalletConnect.Session) throws
func updateSession(session: AlphaWallet.WalletConnect.Session, servers: [RPCServer]) throws
func disconnectSession(session: AlphaWallet.WalletConnect.Session) throws
func disconnectSession(sessions: [NFDSession]) throws
func hasConnectedSession(session: AlphaWallet.WalletConnect.Session) -> Bool
}
protocol WalletConnectServerDelegate: AnyObject {
func server(_ server: WalletConnectServerType, didConnect session: AlphaWallet.WalletConnect.Session)
func server(_ server: WalletConnectServerType, shouldConnectFor sessionProposal: AlphaWallet.WalletConnect.SessionProposal, completion: @escaping (AlphaWallet.WalletConnect.SessionProposalResponse) -> Void)
func server(_ server: WalletConnectServerType, action: AlphaWallet.WalletConnect.Action, request: AlphaWallet.WalletConnect.Session.Request, session: AlphaWallet.WalletConnect.Session)
func server(_ server: WalletConnectServerType, didFail error: Error)
func server(_ server: WalletConnectServerType, tookTooLongToConnectToUrl url: AlphaWallet.WalletConnect.ConnectionUrl)
func server(_ server: WalletConnectServer, didConnect session: AlphaWallet.WalletConnect.Session)
func server(_ server: WalletConnectServer, shouldConnectFor sessionProposal: AlphaWallet.WalletConnect.SessionProposal, completion: @escaping (AlphaWallet.WalletConnect.SessionProposalResponse) -> Void)
func server(_ server: WalletConnectServer, action: AlphaWallet.WalletConnect.Action, request: AlphaWallet.WalletConnect.Session.Request, session: AlphaWallet.WalletConnect.Session)
func server(_ server: WalletConnectServer, didFail error: Error)
func server(_ server: WalletConnectServer, tookTooLongToConnectToUrl url: AlphaWallet.WalletConnect.ConnectionUrl)
}

@ -10,7 +10,7 @@ import Foundation
protocol WalletConnectServerProviderType: WalletConnectResponder {
var sessionsSubscribable: Subscribable<[AlphaWallet.WalletConnect.Session]> { get }
func register(service: WalletConnectServerType)
func register(service: WalletConnectServer)
func connect(url: AlphaWallet.WalletConnect.ConnectionUrl) throws
func session(forIdentifier identifier: AlphaWallet.WalletConnect.SessionIdentifier) -> AlphaWallet.WalletConnect.Session?
@ -34,9 +34,9 @@ class WalletConnectServerProvider: WalletConnectServerProviderType {
var sessionsSubscribable: Subscribable<[AlphaWallet.WalletConnect.Session]> = .init(nil)
private var services: [WalletConnectServerType] = []
private var services: [WalletConnectServer] = []
func register(service: WalletConnectServerType) {
func register(service: WalletConnectServer) {
services.append(service)
sessionsSubscribable.value = services.compactMap { $0.sessionsSubscribable.value }.flatMap { $0 }

@ -2,6 +2,7 @@
import XCTest
@testable import AlphaWallet
import Combine
extension WalletConnectCoordinator {
@ -10,8 +11,9 @@ extension WalletConnectCoordinator {
var sessions = ServerDictionary<WalletSession>()
let session = WalletSession.make()
sessions[session.server] = session
let sessionsSubject = CurrentValueSubject<ServerDictionary<WalletSession>, Never>(sessions)
return WalletConnectCoordinator(keystore: keystore, navigationController: .init(), analyticsCoordinator: FakeAnalyticsService(), config: .make())
return WalletConnectCoordinator(keystore: keystore, navigationController: .init(), analyticsCoordinator: FakeAnalyticsService(), config: .make(), sessionsSubject: sessionsSubject)
}
}

Loading…
Cancel
Save