Add an app-enforced timeout for WalletConnect while waiting to make new connections

pull/3280/head
Hwee-Boon Yar 3 years ago
parent b5d2bedefa
commit 6d8a2b3f86
  1. 7
      AlphaWallet/Analytics/Models/AnalyticsTypes.swift
  2. 1
      AlphaWallet/Core/Features.swift
  3. 31
      AlphaWallet/WalletConnect/Coordinator/WalletConnectCoordinator.swift
  4. 23
      AlphaWallet/WalletConnect/WalletConnectServer.swift

@ -52,6 +52,7 @@ enum Analytics {
case walletConnectConnect = "WalletConnect Connect"
case walletConnectCancel = "WalletConnect Cancel"
case walletConnectDisconnect = "WalletConnect Disconnect"
case walletConnectConnectionTimeout = "WalletConnect Connection Timeout"
case clearBrowserCache = "Clear Browser Cache"
case rectifySendTransactionErrorInActionSheet = "Rectify Send Txn Error"
case nameWallet = "Name Wallet"
@ -161,4 +162,8 @@ enum Analytics {
case `import`
case watch
}
}
enum WalletConnectAction: String {
case bridgeUrl
}
}

@ -15,4 +15,5 @@ enum Features {
static let isAlertsEnabled = false
static let isErc1155Enabled = true
static let isUsingPrivateNetwork = false
static let isUsingAppEnforcedTimeoutForMakingWalletConnectConnections = false
}

@ -144,6 +144,18 @@ class WalletConnectCoordinator: NSObject, Coordinator {
coordinator.start()
addCoordinator(coordinator)
}
private func displayErrorMessage(_ errorMessage: String) {
if let presentedController = notificationAlertController {
presentedController.dismiss(animated: true) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.notificationAlertController = strongSelf.presentationViewController.displaySuccess(message: errorMessage)
}
} else {
notificationAlertController = presentationViewController.displaySuccess(message: errorMessage)
}
resetSessionsToRemoveLoadingIfNeeded()
}
}
extension WalletConnectCoordinator: WalletConnectSessionCoordinatorDelegate {
@ -275,18 +287,19 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
func server(_ server: WalletConnectServer, didFail error: Error) {
info("WalletConnect didFail error: \(error)")
let errorMessage = R.string.localizable.walletConnectFailureTitle()
displayErrorMessage(errorMessage)
}
if let presentedController = notificationAlertController {
presentedController.dismiss(animated: true) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.notificationAlertController = strongSelf.presentationViewController.displaySuccess(message: errorMessage)
}
func server(_ server: WalletConnectServer, tookTooLongToConnectToUrl url: WalletConnectURL) {
if Features.isUsingAppEnforcedTimeoutForMakingWalletConnectConnections {
info("WalletConnect app-enforced timeout for waiting for new connection")
analyticsCoordinator.log(action: Analytics.Action.walletConnectConnectionTimeout, properties: [Analytics.WalletConnectAction.bridgeUrl.rawValue: url.bridgeURL.absoluteString])
//TODO replace with proper error message, localized
let errorMessage = "Too long"
displayErrorMessage(errorMessage)
} else {
notificationAlertController = presentationViewController.displaySuccess(message: errorMessage)
info("WalletConnect app-enforced timeout for waiting for new connection. Disabled")
}
resetSessionsToRemoveLoadingIfNeeded()
}
func server(_ server: WalletConnectServer, shouldConnectFor connection: WalletConnectConnection, completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void) {

@ -21,6 +21,7 @@ protocol WalletConnectServerDelegate: AnyObject {
func server(_ server: WalletConnectServer, shouldConnectFor connection: WalletConnectConnection, completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void)
func server(_ server: WalletConnectServer, action: WalletConnectServer.Action, request: WalletConnectRequest)
func server(_ server: WalletConnectServer, didFail error: Error)
func server(_ server: WalletConnectServer, tookTooLongToConnectToUrl url: WalletConnectURL)
}
typealias WalletConnectRequest = WalletConnectSwift.Request
@ -34,6 +35,8 @@ extension WalletConnectSession {
}
class WalletConnectServer {
private static let connectionTimeout: TimeInterval = 10
enum ConnectionChoice {
case connect(RPCServer)
case cancel
@ -63,6 +66,7 @@ class WalletConnectServer {
private let walletMeta = Session.ClientMeta(name: Keys.server, description: nil, icons: [], url: URL(string: Constants.website)!)
private let wallet: AlphaWallet.Address
private var connectionTimeoutTimers: [WalletConnectURL: Timer] = .init()
static var server: Server?
//NOTE: We are using singleton server value because while every creation server object dones't release prev instances, WalletConnect meamory issue.
private var server: Server {
@ -101,6 +105,18 @@ class WalletConnectServer {
}
func connect(url: WalletConnectURL) throws {
let timer = Timer.scheduledTimer(withTimeInterval: Self.connectionTimeout, repeats: false) { timer in
let isStillWatching = self.connectionTimeoutTimers[url] != nil
debug("WalletConnect app-enforced connection timer is up for: \(url.absoluteString) isStillWatching: \(isStillWatching)")
if isStillWatching {
//TODO be good if we can do `server.communicator.disconnect(from: url)` here on in the delegate. But `communicator` is not accessible
self.delegate?.server(self, tookTooLongToConnectToUrl: url)
} else {
//no-op
}
}
connectionTimeoutTimers[url] = timer
try server.connect(to: url)
}
@ -173,6 +189,7 @@ extension RequestHandlerToAvoidMemoryLeak: RequestHandler {
extension WalletConnectServer: WalletConnectServerRequestHandlerDelegate {
func handler(_ handler: RequestHandlerToAvoidMemoryLeak, request: WalletConnectSwift.Request) {
debug("WalletConnect handler request: \(request.method) url: \(request.url.absoluteString)")
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return }
guard let delegate = strongSelf.delegate, let id = request.id else { return }
@ -190,10 +207,12 @@ extension WalletConnectServer: WalletConnectServerRequestHandlerDelegate {
}
func handler(_ handler: RequestHandlerToAvoidMemoryLeak, canHandle request: WalletConnectSwift.Request) -> Bool {
debug("WalletConnect canHandle: \(request.method) url: \(request.url.absoluteString)")
return true
}
private func convert(request: WalletConnectSwift.Request) -> Promise<Action.ActionType> {
debug("WalletConnect convert request: \(request.method) url: \(request.url.absoluteString)")
guard let sessions = sessions.value else { return .init(error: WalletConnectError.connectionInvalid) }
guard let session = sessions.first(where: { $0.url == request.url }) else { return .init(error: WalletConnectError.connectionInvalid) }
guard let rpcServer = urlToServer[request.url] else { return .init(error: WalletConnectError.connectionInvalid) }
@ -250,6 +269,7 @@ extension WalletConnectServer: ServerDelegate {
}
func server(_ server: Server, didFailToConnect url: WalletConnectURL) {
debug("WalletConnect didFailToConnect: \(url)")
DispatchQueue.main.async {
guard let delegate = self.delegate else { return }
@ -275,6 +295,8 @@ extension WalletConnectServer: ServerDelegate {
}
func server(_ server: Server, shouldStart session: Session, completion: @escaping (Session.WalletInfo) -> Void) {
connectionTimeoutTimers[session.url] = nil
DispatchQueue.main.async {
if let delegate = self.delegate {
let connection = WalletConnectConnection(dAppInfo: session.dAppInfo, url: session.url)
@ -295,6 +317,7 @@ extension WalletConnectServer: ServerDelegate {
}
func server(_ server: Server, didConnect session: Session) {
debug("WalletConnect didConnect: \(session.url.absoluteString)")
DispatchQueue.main.async {
guard var sessions = self.sessions.value else { return }
if let index = sessions.firstIndex(where: { $0.dAppInfo.peerId == session.dAppInfo.peerId }) {

Loading…
Cancel
Save