Fix: WalletConnect mobile linking (i.e when using WalletConnect from iOS Safari, switching to the app with a deeplink) broken

pull/3378/head
Hwee-Boon Yar 3 years ago
parent 508262ec81
commit 6c62708def
  1. 4
      AlphaWallet.xcodeproj/project.pbxproj
  2. 10
      AlphaWallet/Core/Helpers/RemoteLogger.swift
  3. 38
      AlphaWallet/Market/Coordinators/UniversalLinkCoordinator.swift
  4. 16
      AlphaWalletTests/Market/UniversalLinkCoordinatorTests.swift

@ -475,6 +475,7 @@
5E7C793F7E346402CDAF771F /* AssetDefinitionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7FE30D58E4022AF04E48 /* AssetDefinitionStoreTests.swift */; }; 5E7C793F7E346402CDAF771F /* AssetDefinitionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7FE30D58E4022AF04E48 /* AssetDefinitionStoreTests.swift */; };
5E7C797BE2C8DB7EF6F217B3 /* OnboardingPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7103135DCCCAB96EE5FC /* OnboardingPage.swift */; }; 5E7C797BE2C8DB7EF6F217B3 /* OnboardingPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7103135DCCCAB96EE5FC /* OnboardingPage.swift */; };
5E7C797F3278A290BE47BFA2 /* TokenIdOrEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78302634885C93D9B6FA /* TokenIdOrEvent.swift */; }; 5E7C797F3278A290BE47BFA2 /* TokenIdOrEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78302634885C93D9B6FA /* TokenIdOrEvent.swift */; };
5E7C79880D398BF8D145D666 /* UniversalLinkCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C74A5A91E5DE208B33325 /* UniversalLinkCoordinatorTests.swift */; };
5E7C798A5D213DD92F24CBFB /* AssetImplicitAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78FAB9070B10A476DB29 /* AssetImplicitAttributes.swift */; }; 5E7C798A5D213DD92F24CBFB /* AssetImplicitAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78FAB9070B10A476DB29 /* AssetImplicitAttributes.swift */; };
5E7C799DED280839CE80FCD3 /* URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7DCF28B3F0342113E20E /* URLTests.swift */; }; 5E7C799DED280839CE80FCD3 /* URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7DCF28B3F0342113E20E /* URLTests.swift */; };
5E7C79BFFFB6A5FE833489C0 /* ActivitiesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C72E648B7C34EDA193BDA /* ActivitiesCoordinator.swift */; }; 5E7C79BFFFB6A5FE833489C0 /* ActivitiesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C72E648B7C34EDA193BDA /* ActivitiesCoordinator.swift */; };
@ -1345,6 +1346,7 @@
5E7C74822F5F71748184F6C1 /* EventInstance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventInstance.swift; sourceTree = "<group>"; }; 5E7C74822F5F71748184F6C1 /* EventInstance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventInstance.swift; sourceTree = "<group>"; };
5E7C7487BDF72352446E1266 /* ImportTokenViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportTokenViewControllerTests.swift; sourceTree = "<group>"; }; 5E7C7487BDF72352446E1266 /* ImportTokenViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportTokenViewControllerTests.swift; sourceTree = "<group>"; };
5E7C74A1A13A1A6CB9E61BAC /* TokenListFormatRowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenListFormatRowViewModel.swift; sourceTree = "<group>"; }; 5E7C74A1A13A1A6CB9E61BAC /* TokenListFormatRowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenListFormatRowViewModel.swift; sourceTree = "<group>"; };
5E7C74A5A91E5DE208B33325 /* UniversalLinkCoordinatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalLinkCoordinatorTests.swift; sourceTree = "<group>"; };
5E7C74A5B5B9D8AD0BD913C1 /* CreateInitialWalletViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateInitialWalletViewController.swift; sourceTree = "<group>"; }; 5E7C74A5B5B9D8AD0BD913C1 /* CreateInitialWalletViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateInitialWalletViewController.swift; sourceTree = "<group>"; };
5E7C74B424FB5DE3A4D6A2F4 /* DappsHomeEmptyViewViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DappsHomeEmptyViewViewModel.swift; sourceTree = "<group>"; }; 5E7C74B424FB5DE3A4D6A2F4 /* DappsHomeEmptyViewViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DappsHomeEmptyViewViewModel.swift; sourceTree = "<group>"; };
5E7C74B82783A94091A43470 /* EthTokenViewCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthTokenViewCellViewModel.swift; sourceTree = "<group>"; }; 5E7C74B82783A94091A43470 /* EthTokenViewCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthTokenViewCellViewModel.swift; sourceTree = "<group>"; };
@ -4166,6 +4168,7 @@
76F1DE8ADA3176D0277EDF20 /* OrderSigningTests.swift */, 76F1DE8ADA3176D0277EDF20 /* OrderSigningTests.swift */,
76F1D96298E216CBFC3DD78B /* UniversalLinkHandlerTests.swift */, 76F1D96298E216CBFC3DD78B /* UniversalLinkHandlerTests.swift */,
5E7C77C33FD8D5653F051136 /* ViewControllers */, 5E7C77C33FD8D5653F051136 /* ViewControllers */,
5E7C74A5A91E5DE208B33325 /* UniversalLinkCoordinatorTests.swift */,
); );
path = Market; path = Market;
sourceTree = "<group>"; sourceTree = "<group>";
@ -6275,6 +6278,7 @@
5E7C799DED280839CE80FCD3 /* URLTests.swift in Sources */, 5E7C799DED280839CE80FCD3 /* URLTests.swift in Sources */,
5E7C7A7FFA16D3505F94BB9E /* FakeActivitiesService.swift in Sources */, 5E7C7A7FFA16D3505F94BB9E /* FakeActivitiesService.swift in Sources */,
5E7C775BE0C1CF2B2FCAEA4B /* DecodedFunctionCall+DecodeTests.swift in Sources */, 5E7C775BE0C1CF2B2FCAEA4B /* DecodedFunctionCall+DecodeTests.swift in Sources */,
5E7C79880D398BF8D145D666 /* UniversalLinkCoordinatorTests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

@ -99,22 +99,22 @@ final class DDLogger: Logger {
} }
func debug(_ message: Any) { func debug(_ message: Any) {
DDLogDebug(message, ddlog: logger) DDLogDebug("xxx \(message)", ddlog: logger)
} }
func info(_ message: Any) { func info(_ message: Any) {
DDLogInfo(message, ddlog: logger) DDLogInfo("xxx \(message)", ddlog: logger)
} }
func warn(_ message: Any) { func warn(_ message: Any) {
DDLogWarn(message, ddlog: logger) DDLogWarn("xxx \(message)", ddlog: logger)
} }
func verbose(_ message: Any) { func verbose(_ message: Any) {
DDLogVerbose(message, ddlog: logger) DDLogVerbose("xxx \(message)", ddlog: logger)
} }
func error(_ message: Any) { func error(_ message: Any) {
DDLogError(message, ddlog: logger) DDLogError("xxx \(message)", ddlog: logger)
} }
} }

@ -26,7 +26,7 @@ enum MagicLinkURL {
init?(url: URL) { init?(url: URL) {
if let eip681Url = Self.hasEip681Path(in: url) { if let eip681Url = Self.hasEip681Path(in: url) {
self = .eip681(eip681Url) self = .eip681(eip681Url)
} else if let wcUrl = Self.hasWalletConnectPath(in: url) { } else if let wcUrl = Self.functional.hasWalletConnectPath(in: url) {
self = .walletConnect(wcUrl) self = .walletConnect(wcUrl)
} else { } else {
return nil return nil
@ -46,13 +46,43 @@ enum MagicLinkURL {
return nil return nil
} }
} }
}
extension MagicLinkURL {
class functional {}
}
//E.g. https://aw.app/wc:f607884e-63a5-4fa3-8e7d-af6f6fa9b51f@1?bridge=https%3A%2F%2Fn.bridge.walletconnect.org&key=cff9abba23cb9f843e9d623b891a5f8948b41f7d4afc7f7155aa252504cd8264 extension MagicLinkURL.functional {
private static func hasWalletConnectPath(in url: URL) -> WalletConnectURL? { //Multiple formats:
guard let magicLink = RPCServer(withMagicLink: url), url.path.starts(with: Self.walletConnectPath) else { return nil } //From WalletConnect mobile linking: e.g. https://aw.app/wc?uri=wc%3A588422fd-929d-438a-b337-31c3c9184d9b%401%3Fbridge%3Dhttps%253A%252F%252Fbridge.walletconnect.org%26key%3D8f9459f72aed0790282c47fe45f37ed5cb121bc17795f8f2a229a910bc447202
//From AlphaWallet iOS Safari extension's rewriting: eg. https://aw.app/wc:f607884e-63a5-4fa3-8e7d-af6f6fa9b51f@1?bridge=https%3A%2F%2Fn.bridge.walletconnect.org&key=cff9abba23cb9f843e9d623b891a5f8948b41f7d4afc7f7155aa252504cd8264
static func hasWalletConnectPath(in url: URL) -> WalletConnectURL? {
guard url.path.starts(with: MagicLinkURL.walletConnectPath) else { return nil }
if let url = extractWalletConnectUrlFromSafariExtensionRewrittenUrl(url) {
return url
} else if let url = extractWalletConnectUrlFromWalletConnectMobileLinking(url) {
return url
} else {
return nil
}
}
private static func extractWalletConnectUrlFromSafariExtensionRewrittenUrl(_ url: URL) -> WalletConnectURL? {
guard let magicLink = RPCServer(withMagicLink: url) else { return nil }
let wcUrl = url.absoluteString.replacingOccurrences(of: magicLink.magicLinkPrefix.absoluteString, with: "") let wcUrl = url.absoluteString.replacingOccurrences(of: magicLink.magicLinkPrefix.absoluteString, with: "")
return WalletConnectURL(wcUrl) return WalletConnectURL(wcUrl)
} }
private static func extractWalletConnectUrlFromWalletConnectMobileLinking(_ url: URL) -> WalletConnectURL? {
guard let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems else { return nil }
guard let string = queryItems.first(where: { $0.name == "uri" })?.value else { return nil }
if let walletConnectUrl = WalletConnectURL(string) {
return walletConnectUrl
} else {
//no-op. According to WalletConnect docs, this is just to get iOS to switch over to the app for signing, etc. e.g. https://aw.app/wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@1
return nil
}
}
} }
// swiftlint:disable type_body_length // swiftlint:disable type_body_length

@ -0,0 +1,16 @@
// Copyright © 2021 Stormbird PTE. LTD.
@testable import AlphaWallet
import XCTest
class UniversalLinkCoordinatorTests: XCTestCase {
func testHasWalletConnectPathFromSafariExtensionRewriting() {
let url = URL(string: "https://aw.app/wc:f607884e-63a5-4fa3-8e7d-af6f6fa9b51f@1?bridge=https%3A%2F%2Fn.bridge.walletconnect.org&key=cff9abba23cb9f843e9d623b891a5f8948b41f7d4afc7f7155aa252504cd8264")!
XCTAssertNotNil(MagicLinkURL.functional.hasWalletConnectPath(in: url))
}
func testHasWalletConnectPathFromMobileLinking() {
let url = URL(string: "https://aw.app/wc?uri=wc%3A588422fd-929d-438a-b337-31c3c9184d9b%401%3Fbridge%3Dhttps%253A%252F%252Fbridge.walletconnect.org%26key%3D8f9459f72aed0790282c47fe45f37ed5cb121bc17795f8f2a229a910bc447202")!
XCTAssertNotNil(MagicLinkURL.functional.hasWalletConnectPath(in: url))
}
}
Loading…
Cancel
Save