diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/IntegrationTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/IntegrationTests.xcscheme new file mode 100644 index 000000000..0236cb7e7 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/IntegrationTests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DApp/ Accounts/AccountsView.swift b/Example/DApp/ Accounts/AccountsView.swift new file mode 100644 index 000000000..afd428a0b --- /dev/null +++ b/Example/DApp/ Accounts/AccountsView.swift @@ -0,0 +1,29 @@ +import UIKit + +final class AccountsView: UIView { + let tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .plain) + tableView.backgroundColor = .tertiarySystemBackground + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "accountCell") + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .systemBackground + addSubview(tableView) + + subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 16), + tableView.leadingAnchor.constraint(equalTo: leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor) + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Example/DApp/ Accounts/AccountsViewController.swift b/Example/DApp/ Accounts/AccountsViewController.swift new file mode 100644 index 000000000..bb20aa733 --- /dev/null +++ b/Example/DApp/ Accounts/AccountsViewController.swift @@ -0,0 +1,87 @@ +import UIKit +import WalletConnect + +struct AccountDetails { + let chain: String + let methods: [String] + let account: String +} + +final class AccountsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { + + let client = ClientDelegate.shared.client + let session: Session + var accountsDetails: [AccountDetails] = [] + var onDisconnect: (()->())? + + private let accountsView: AccountsView = { + AccountsView() + }() + + init(session: Session) { + self.session = session + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = accountsView + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.title = "Accounts" + + navigationItem.rightBarButtonItem = UIBarButtonItem( + title: "Disconnect", + style: .plain, + target: self, + action: #selector(disconnect) + ) + accountsView.tableView.dataSource = self + accountsView.tableView.delegate = self + client.logger.setLogging(level: .debug) + session.accounts.forEach { account in + let splits = account.split(separator: ":", omittingEmptySubsequences: false) + guard splits.count == 3 else { return } + let chain = String(splits[0] + ":" + splits[1]) + accountsDetails.append(AccountDetails(chain: chain, methods: Array(session.permissions.methods), account: account)) + } + } + + @objc + private func disconnect() { + client.disconnect(topic: session.topic, reason: Reason(code: 0, message: "disconnect")) + onDisconnect?() + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + accountsDetails.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "accountCell", for: indexPath) + let details = accountsDetails[indexPath.row] + cell.textLabel?.text = details.account + cell.imageView?.image = UIImage(named: details.chain) + cell.textLabel?.numberOfLines = 0 + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + showAccountRequestScreen(accountsDetails[indexPath.row]) + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 70 + } + + func showAccountRequestScreen(_ details: AccountDetails) { + let vc = AccountRequestViewController(session: session, accountDetails: details) + navigationController?.pushViewController(vc, animated: true) + } + +} diff --git a/Example/DApp/AccountRequest/AccountRequestView.swift b/Example/DApp/AccountRequest/AccountRequestView.swift new file mode 100644 index 000000000..3f446422f --- /dev/null +++ b/Example/DApp/AccountRequest/AccountRequestView.swift @@ -0,0 +1,69 @@ + +import UIKit +import Foundation + +class AccountRequestView: UIView { + let iconView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.backgroundColor = .systemFill + imageView.layer.cornerRadius = 32 + return imageView + }() + + let chainLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) + return label + }() + let accountLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .subheadline) + label.textColor = .secondaryLabel + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + let headerStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.spacing = 16 + stackView.alignment = .center + return stackView + }() + + let tableView = UITableView() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .systemBackground + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "method_cell") + addSubview(iconView) + addSubview(headerStackView) + addSubview(tableView) + + headerStackView.addArrangedSubview(chainLabel) + headerStackView.addArrangedSubview(accountLabel) + + subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + iconView.topAnchor.constraint(equalTo: topAnchor, constant: 64), + iconView.centerXAnchor.constraint(equalTo: centerXAnchor), + + headerStackView.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 32), + headerStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), + headerStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), + + tableView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 0), + tableView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0), + tableView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0), + tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Example/DApp/AccountRequest/AccountRequestViewController.swift b/Example/DApp/AccountRequest/AccountRequestViewController.swift new file mode 100644 index 000000000..f1d74c00f --- /dev/null +++ b/Example/DApp/AccountRequest/AccountRequestViewController.swift @@ -0,0 +1,166 @@ + + +import Foundation +import UIKit +import WalletConnect +import WalletConnectUtils + +class AccountRequestViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + private let session: Session + private let client: WalletConnectClient = ClientDelegate.shared.client + private let chainId: String + private let account: String + private let methods = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"] + private let accountRequestView = { + AccountRequestView() + }() + + init(session: Session, accountDetails: AccountDetails) { + self.session = session + self.chainId = accountDetails.chain + self.account = accountDetails.account + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = accountRequestView + } + + override func viewDidLoad() { + super.viewDidLoad() + accountRequestView.tableView.delegate = self + accountRequestView.tableView.dataSource = self + accountRequestView.iconView.image = UIImage(named: chainId) + accountRequestView.chainLabel.text = chainId + accountRequestView.accountLabel.text = account + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + methods.count + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return "Methods" + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "method_cell", for: indexPath) + cell.textLabel?.text = methods[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let method = methods[indexPath.row] + let requestParams = getRequest(for: method) + + + let request = Request(topic: session.topic, method: method, params: requestParams, chainId: chainId) + client.request(params: request) { _ in } + presentConfirmationAlert() + } + + private func presentConfirmationAlert() { + let alert = UIAlertController(title: "Request Sent", message: nil, preferredStyle: .alert) + let action = UIAlertAction(title: "OK", style: .cancel) + alert.addAction(action) + present(alert, animated: true) + } + + private func getRequest(for method: String) -> AnyCodable { + let account = "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" + if method == "eth_sendTransaction" { + let tx = Stub.tx + return AnyCodable(tx) + } else if method == "personal_sign" { + return AnyCodable(["0xdeadbeaf", account]) + } else if method == "eth_signTypedData" { + return AnyCodable([account, Stub.eth_signTypedData]) + } + fatalError("not implemented") + } +} + +struct Transaction: Codable { + let from, to, data, gas: String + let gasPrice, value, nonce: String +} + +fileprivate enum Stub { + static let tx = [Transaction(from: "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", + to: "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", + data: "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + gas: "0x76c0", + gasPrice: "0x9184e72a000", + value: "0x9184e72a", + nonce: "0x117")] + static let eth_signTypedData = """ +{ +"types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] +}, +"primaryType": "Mail", +"domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" +}, +"message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" +} +} +""" +} diff --git a/Example/DApp/AppDelegate.swift b/Example/DApp/AppDelegate.swift new file mode 100644 index 000000000..7256ad23d --- /dev/null +++ b/Example/DApp/AppDelegate.swift @@ -0,0 +1,27 @@ + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Example/DApp/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/DApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Example/DApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..9221b9bb1 --- /dev/null +++ b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/DApp/Assets.xcassets/Contents.json b/Example/DApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Example/DApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/DApp/Assets.xcassets/eip155:1.imageset/Contents.json b/Example/DApp/Assets.xcassets/eip155:1.imageset/Contents.json new file mode 100644 index 000000000..4664aca0a --- /dev/null +++ b/Example/DApp/Assets.xcassets/eip155:1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ethereum (1).png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/DApp/Assets.xcassets/eip155:1.imageset/ethereum (1).png b/Example/DApp/Assets.xcassets/eip155:1.imageset/ethereum (1).png new file mode 100644 index 000000000..35e1cb09f Binary files /dev/null and b/Example/DApp/Assets.xcassets/eip155:1.imageset/ethereum (1).png differ diff --git a/Example/DApp/Assets.xcassets/eip155:137.imageset/Contents.json b/Example/DApp/Assets.xcassets/eip155:137.imageset/Contents.json new file mode 100644 index 000000000..afd53b2a5 --- /dev/null +++ b/Example/DApp/Assets.xcassets/eip155:137.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "coin.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/DApp/Assets.xcassets/eip155:137.imageset/coin.png b/Example/DApp/Assets.xcassets/eip155:137.imageset/coin.png new file mode 100644 index 000000000..318e76f50 Binary files /dev/null and b/Example/DApp/Assets.xcassets/eip155:137.imageset/coin.png differ diff --git a/Example/DApp/Base.lproj/LaunchScreen.storyboard b/Example/DApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/Example/DApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DApp/ClientDelegate.swift b/Example/DApp/ClientDelegate.swift new file mode 100644 index 000000000..12fa01da4 --- /dev/null +++ b/Example/DApp/ClientDelegate.swift @@ -0,0 +1,65 @@ + +import WalletConnect +import Foundation + + +class ClientDelegate: WalletConnectClientDelegate { + var client: WalletConnectClient + var onSessionSettled: ((Session)->())? + var onPairingSettled: ((Pairing)->())? + var onSessionProposal: ((Session.Proposal)->())? + var onSessionRequest: ((Request)->())? + var onSessionRejected: ((String, Reason)->())? + var onSessionDelete: (()->())? + var onSessionUpgrade: ((String, Session.Permissions)->())? + var onSessionUpdate: ((String, Set)->())? + var onNotificationReceived: ((Session.Notification, String)->())? + var onPairingUpdate: ((String, AppMetadata)->())? + + static var shared: ClientDelegate = ClientDelegate() + private init() { + let metadata = AppMetadata( + name: "Swift Dapp", + description: "a description", + url: "wallet.connect", + icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) + self.client = WalletConnectClient( + metadata: metadata, + projectId: "52af113ee0c1e1a20f4995730196c13e", + isController: false, + relayHost: "relay.dev.walletconnect.com" + ) + client.delegate = self + } + + func didReject(pendingSessionTopic: String, reason: Reason) { + onSessionRejected?(pendingSessionTopic, reason) + } + func didSettle(session: Session) { + onSessionSettled?(session) + } + func didSettle(pairing: Pairing) { + onPairingSettled?(pairing) + } + func didReceive(sessionProposal: Session.Proposal) { + onSessionProposal?(sessionProposal) + } + func didReceive(sessionRequest: Request) { + onSessionRequest?(sessionRequest) + } + func didDelete(sessionTopic: String, reason: Reason) { + onSessionDelete?() + } + func didUpgrade(sessionTopic: String, permissions: Session.Permissions) { + onSessionUpgrade?(sessionTopic, permissions) + } + func didUpdate(sessionTopic: String, accounts: Set) { + onSessionUpdate?(sessionTopic, accounts) + } + func didReceive(notification: Session.Notification, sessionTopic: String) { + onNotificationReceived?(notification, sessionTopic) + } + func didUpdate(pairingTopic: String, appMetadata: AppMetadata) { + onPairingUpdate?(pairingTopic, appMetadata) + } +} diff --git a/Example/ExampleApp/Proposer/ProposerView.swift b/Example/DApp/Connect/ConnectView.swift similarity index 70% rename from Example/ExampleApp/Proposer/ProposerView.swift rename to Example/DApp/Connect/ConnectView.swift index 687b4d2de..ca7d7ad27 100644 --- a/Example/ExampleApp/Proposer/ProposerView.swift +++ b/Example/DApp/Connect/ConnectView.swift @@ -1,7 +1,8 @@ + +import Foundation import UIKit -final class ProposerView: UIView { - +final class ConnectView: UIView { let qrCodeView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit @@ -19,20 +20,11 @@ final class ProposerView: UIView { return button }() - let tableView: UITableView = { - let tableView = UITableView(frame: .zero, style: .insetGrouped) - tableView.backgroundColor = .tertiarySystemBackground - tableView.register(ActiveSessionCell.self, forCellReuseIdentifier: "sessionCell") - return tableView - }() - override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground - addSubview(qrCodeView) addSubview(copyButton) - addSubview(tableView) subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -46,11 +38,6 @@ final class ProposerView: UIView { copyButton.centerXAnchor.constraint(equalTo: centerXAnchor), copyButton.widthAnchor.constraint(equalTo: qrCodeView.widthAnchor), copyButton.heightAnchor.constraint(equalToConstant: 44), - - tableView.topAnchor.constraint(equalTo: copyButton.bottomAnchor, constant: 16), - tableView.leadingAnchor.constraint(equalTo: leadingAnchor), - tableView.trailingAnchor.constraint(equalTo: trailingAnchor), - tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor) ]) } diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift new file mode 100644 index 000000000..ce75d2e18 --- /dev/null +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -0,0 +1,54 @@ + +import Foundation +import UIKit + +class ConnectViewController: UIViewController { + let uriString: String + init(uri: String) { + self.uriString = uri + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private let connectView: ConnectView = { + ConnectView() + }() + + override func loadView() { + view = connectView + } + + override func viewDidLoad() { + super.viewDidLoad() + DispatchQueue.global().async { [unowned self] in + if let qrImage = generateQRCode(from: uriString) { + DispatchQueue.main.async { + connectView.qrCodeView.image = qrImage + connectView.copyButton.isHidden = false + } + } + } + connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) + connectView.copyButton.isHidden = true + } + + + @objc func copyURI() { + UIPasteboard.general.string = uriString + } + + + private func generateQRCode(from string: String) -> UIImage? { + let data = string.data(using: .ascii) + if let filter = CIFilter(name: "CIQRCodeGenerator") { + filter.setValue(data, forKey: "inputMessage") + if let output = filter.outputImage { + return UIImage(ciImage: output) + } + } + return nil + } +} diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist new file mode 100644 index 000000000..994cd9071 --- /dev/null +++ b/Example/DApp/Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + dApp + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift new file mode 100644 index 000000000..9f3943771 --- /dev/null +++ b/Example/DApp/SceneDelegate.swift @@ -0,0 +1,44 @@ + +import UIKit +import WalletConnect + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + window = UIWindow(windowScene: windowScene) + if let session = ClientDelegate.shared.client.getSettledSessions().first { + showAccountsScreen(session) + } else { + showSelectChainScreen() + } + } + + func showSelectChainScreen() { + let vc = SelectChainViewController() + vc.onSessionSettled = { [unowned self] session in + DispatchQueue.main.async { + showAccountsScreen(session) + } + } + window?.rootViewController = UINavigationController(rootViewController: vc) + window?.makeKeyAndVisible() + } + + func showAccountsScreen(_ session: Session) { + let vc = AccountsViewController(session: session) + vc.onDisconnect = { [unowned self] in + showSelectChainScreen() + } + window?.rootViewController = UINavigationController(rootViewController: vc) + window?.makeKeyAndVisible() + } + +} + diff --git a/Example/DApp/SelectChain/SelectChainView.swift b/Example/DApp/SelectChain/SelectChainView.swift new file mode 100644 index 000000000..81891e15a --- /dev/null +++ b/Example/DApp/SelectChain/SelectChainView.swift @@ -0,0 +1,48 @@ + +import Foundation +import UIKit + + +class SelectChainView: UIView { + let tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.backgroundColor = .tertiarySystemBackground + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "chain") + return tableView + }() + let connectButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Connect", for: .normal) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 8 + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "chain_cell") + backgroundColor = .systemBackground + addSubview(tableView) + addSubview(connectButton) + + subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 16), + tableView.leadingAnchor.constraint(equalTo: leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), + + connectButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -16), + connectButton.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor), + connectButton.heightAnchor.constraint(equalToConstant: 44), + connectButton.widthAnchor.constraint(equalToConstant: 120), + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + diff --git a/Example/DApp/SelectChain/SelectChainViewController.swift b/Example/DApp/SelectChain/SelectChainViewController.swift new file mode 100644 index 000000000..67fa3e014 --- /dev/null +++ b/Example/DApp/SelectChain/SelectChainViewController.swift @@ -0,0 +1,69 @@ + + +import Foundation +import WalletConnect +import UIKit + +struct Chain { + let name: String + let id: String +} + +class SelectChainViewController: UIViewController, UITableViewDataSource { + private let selectChainView: SelectChainView = { + SelectChainView() + }() + let chains = [Chain(name: "Ethereum", id: "eip155:1"), Chain(name: "Polygon", id: "eip155:137")] + let client = ClientDelegate.shared.client + var onSessionSettled: ((Session)->())? + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.title = "Available Chains" + selectChainView.tableView.dataSource = self + selectChainView.connectButton.addTarget(self, action: #selector(connect), for: .touchUpInside) + ClientDelegate.shared.onSessionSettled = { [unowned self] session in + onSessionSettled?(session) + } + } + + override func loadView() { + view = selectChainView + } + + @objc + private func connect() { + print("[PROPOSER] Connecting to a pairing...") + let permissions = Session.Permissions( + blockchains: ["eip155:1", "eip155:137"], + methods: ["eth_sendTransaction", "personal_sign", "eth_signTypedData"], + notifications: [] + ) + do { + if let uri = try client.connect(sessionPermissions: permissions) { + showConnectScreen(uriString: uri) + } + } catch { + print("[PROPOSER] Pairing connect error: \(error)") + } + } + + private func showConnectScreen(uriString: String) { + DispatchQueue.main.async { [unowned self] in + let vc = ConnectViewController(uri: uriString) + present(vc, animated: true, completion: nil) + } + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + chains.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "chain_cell", for: indexPath) + let chain = chains[indexPath.row] + cell.textLabel?.text = chain.name + cell.imageView?.image = UIImage(named: chain.id) + cell.selectionStyle = .none + return cell + } +} diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index dc0b3c745..110eb34a4 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 7603D74D2703429A00DD27A2 /* ProposerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7603D74C2703429A00DD27A2 /* ProposerView.swift */; }; - 761C649A26FB7ABB004239D1 /* ProposerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C649926FB7ABB004239D1 /* ProposerViewController.swift */; }; 761C649C26FB7B7F004239D1 /* ResponderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C649B26FB7B7F004239D1 /* ResponderViewController.swift */; }; 761C649E26FB7FD7004239D1 /* SessionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C649D26FB7FD7004239D1 /* SessionViewController.swift */; }; 761C64A326FB83CE004239D1 /* SessionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C64A226FB83CE004239D1 /* SessionView.swift */; }; @@ -28,6 +26,21 @@ 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 8460DD002750D6F50081F94C /* SessionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */; }; 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DD012750D7020081F94C /* SessionDetailsView.swift */; }; + 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE641E27981DED00142511 /* AppDelegate.swift */; }; + 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; + 84CE642827981DF000142511 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642727981DF000142511 /* Assets.xcassets */; }; + 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642927981DF000142511 /* LaunchScreen.storyboard */; }; + 84CE6430279820F600142511 /* AccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C649926FB7ABB004239D1 /* AccountsViewController.swift */; }; + 84CE6431279820F600142511 /* AccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7603D74C2703429A00DD27A2 /* AccountsView.swift */; }; + 84CE64392798228D00142511 /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 84CE64382798228D00142511 /* Web3 */; }; + 84CE643B2798229100142511 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 84CE643A2798229100142511 /* WalletConnect */; }; + 84CE643D2798322600142511 /* ConnectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE643C2798322600142511 /* ConnectViewController.swift */; }; + 84CE6444279AB5AD00142511 /* SelectChainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6443279AB5AD00142511 /* SelectChainViewController.swift */; }; + 84CE6446279ABBF300142511 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6445279ABBF300142511 /* ClientDelegate.swift */; }; + 84CE6448279AE68600142511 /* AccountRequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6447279AE68600142511 /* AccountRequestViewController.swift */; }; + 84CE644B279EA1FA00142511 /* AccountRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644A279EA1FA00142511 /* AccountRequestView.swift */; }; + 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; + 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; /* End PBXBuildFile section */ @@ -43,13 +56,13 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 7603D74C2703429A00DD27A2 /* ProposerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposerView.swift; sourceTree = ""; }; - 761C649926FB7ABB004239D1 /* ProposerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposerViewController.swift; sourceTree = ""; }; + 7603D74C2703429A00DD27A2 /* AccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsView.swift; sourceTree = ""; }; + 761C649926FB7ABB004239D1 /* AccountsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsViewController.swift; sourceTree = ""; }; 761C649B26FB7B7F004239D1 /* ResponderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponderViewController.swift; sourceTree = ""; wrapsLines = 0; }; 761C649D26FB7FD7004239D1 /* SessionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionViewController.swift; sourceTree = ""; }; 761C64A226FB83CE004239D1 /* SessionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionView.swift; sourceTree = ""; }; 761C64A526FCB0AA004239D1 /* SessionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionInfo.swift; sourceTree = ""; }; - 764E1D3C26F8D3FC00A1FB15 /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; 764E1D3F26F8D3FC00A1FB15 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 764E1D4126F8D3FC00A1FB15 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 764E1D4826F8D3FE00A1FB15 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -68,6 +81,19 @@ 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsViewController.swift; sourceTree = ""; }; 8460DD012750D7020081F94C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.swift; sourceTree = ""; }; + 84CE641C27981DED00142511 /* DApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 84CE641E27981DED00142511 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 84CE642027981DED00142511 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 84CE642727981DF000142511 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 84CE642A27981DF000142511 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 84CE642C27981DF000142511 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 84CE643C2798322600142511 /* ConnectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectViewController.swift; sourceTree = ""; }; + 84CE6443279AB5AD00142511 /* SelectChainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectChainViewController.swift; sourceTree = ""; }; + 84CE6445279ABBF300142511 /* ClientDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; + 84CE6447279AE68600142511 /* AccountRequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRequestViewController.swift; sourceTree = ""; }; + 84CE644A279EA1FA00142511 /* AccountRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRequestView.swift; sourceTree = ""; }; + 84CE644D279ED2FF00142511 /* SelectChainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectChainView.swift; sourceTree = ""; }; + 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -89,6 +115,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE641927981DED00142511 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 84CE643B2798229100142511 /* WalletConnect in Frameworks */, + 84CE64392798228D00142511 /* Web3 in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -100,8 +135,6 @@ 84F568C1279582D200D0A289 /* Signer.swift */, 84494387278D9C1B00CC26BB /* UIAlertController.swift */, 76744CF426FDFB6B00B77ED9 /* ResponderView.swift */, - 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */, - 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */, 764E1D5926F8DF1B00A1FB15 /* ScannerViewController.swift */, 761C64A426FCB08B004239D1 /* SessionProposal */, 8460DCFD274F98A90081F94C /* Request */, @@ -109,15 +142,6 @@ path = Responder; sourceTree = ""; }; - 761C64A126FB838D004239D1 /* Proposer */ = { - isa = PBXGroup; - children = ( - 761C649926FB7ABB004239D1 /* ProposerViewController.swift */, - 7603D74C2703429A00DD27A2 /* ProposerView.swift */, - ); - path = Proposer; - sourceTree = ""; - }; 761C64A426FCB08B004239D1 /* SessionProposal */ = { isa = PBXGroup; children = ( @@ -133,6 +157,7 @@ children = ( 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 845B30ED27859686002E4094 /* ExampleAppTests */, + 84CE641D27981DED00142511 /* DApp */, 764E1D3D26F8D3FC00A1FB15 /* Products */, 764E1D5326F8DAC800A1FB15 /* Frameworks */, 764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */, @@ -142,8 +167,9 @@ 764E1D3D26F8D3FC00A1FB15 /* Products */ = { isa = PBXGroup; children = ( - 764E1D3C26F8D3FC00A1FB15 /* ExampleApp.app */, + 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */, 845B30EC27859686002E4094 /* ExampleAppTests.xctest */, + 84CE641C27981DED00142511 /* DApp.app */, ); name = Products; sourceTree = ""; @@ -153,9 +179,10 @@ children = ( 764E1D3F26F8D3FC00A1FB15 /* AppDelegate.swift */, 764E1D4126F8D3FC00A1FB15 /* SceneDelegate.swift */, + 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */, + 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */, 8460DCFE2750D6DF0081F94C /* SessionDetails */, 761C64A026FB8387004239D1 /* Responder */, - 761C64A126FB838D004239D1 /* Proposer */, 764E1D4826F8D3FE00A1FB15 /* Assets.xcassets */, 764E1D4A26F8D3FE00A1FB15 /* LaunchScreen.storyboard */, 764E1D4D26F8D3FE00A1FB15 /* Info.plist */, @@ -197,12 +224,65 @@ path = SessionDetails; sourceTree = ""; }; + 84CE641D27981DED00142511 /* DApp */ = { + isa = PBXGroup; + children = ( + 84CE641E27981DED00142511 /* AppDelegate.swift */, + 84CE642027981DED00142511 /* SceneDelegate.swift */, + 84CE6449279EA1E600142511 /* AccountRequest */, + 84CE6445279ABBF300142511 /* ClientDelegate.swift */, + 84CE644C279ED2EC00142511 /* SelectChain */, + 84CE6450279ED41D00142511 /* Connect */, + 84CE644F279ED3FB00142511 /* Accounts */, + 84CE642727981DF000142511 /* Assets.xcassets */, + 84CE642927981DF000142511 /* LaunchScreen.storyboard */, + 84CE642C27981DF000142511 /* Info.plist */, + ); + path = DApp; + sourceTree = ""; + }; + 84CE6449279EA1E600142511 /* AccountRequest */ = { + isa = PBXGroup; + children = ( + 84CE6447279AE68600142511 /* AccountRequestViewController.swift */, + 84CE644A279EA1FA00142511 /* AccountRequestView.swift */, + ); + path = AccountRequest; + sourceTree = ""; + }; + 84CE644C279ED2EC00142511 /* SelectChain */ = { + isa = PBXGroup; + children = ( + 84CE6443279AB5AD00142511 /* SelectChainViewController.swift */, + 84CE644D279ED2FF00142511 /* SelectChainView.swift */, + ); + path = SelectChain; + sourceTree = ""; + }; + 84CE644F279ED3FB00142511 /* Accounts */ = { + isa = PBXGroup; + children = ( + 761C649926FB7ABB004239D1 /* AccountsViewController.swift */, + 7603D74C2703429A00DD27A2 /* AccountsView.swift */, + ); + path = " Accounts"; + sourceTree = ""; + }; + 84CE6450279ED41D00142511 /* Connect */ = { + isa = PBXGroup; + children = ( + 84CE643C2798322600142511 /* ConnectViewController.swift */, + 84CE6451279ED42B00142511 /* ConnectView.swift */, + ); + path = Connect; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 764E1D3B26F8D3FC00A1FB15 /* ExampleApp */ = { + 764E1D3B26F8D3FC00A1FB15 /* Wallet */ = { isa = PBXNativeTarget; - buildConfigurationList = 764E1D5026F8D3FE00A1FB15 /* Build configuration list for PBXNativeTarget "ExampleApp" */; + buildConfigurationList = 764E1D5026F8D3FE00A1FB15 /* Build configuration list for PBXNativeTarget "Wallet" */; buildPhases = ( 764E1D3826F8D3FC00A1FB15 /* Sources */, 764E1D3926F8D3FC00A1FB15 /* Frameworks */, @@ -212,13 +292,13 @@ ); dependencies = ( ); - name = ExampleApp; + name = Wallet; packageProductDependencies = ( 764E1D5726F8DBAB00A1FB15 /* WalletConnect */, 844943A0278EC49700CC26BB /* Web3 */, ); productName = ExampleApp; - productReference = 764E1D3C26F8D3FC00A1FB15 /* ExampleApp.app */; + productReference = 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */; productType = "com.apple.product-type.application"; }; 845B30EB27859686002E4094 /* ExampleAppTests */ = { @@ -239,6 +319,27 @@ productReference = 845B30EC27859686002E4094 /* ExampleAppTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 84CE641B27981DED00142511 /* DApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84CE642F27981DF000142511 /* Build configuration list for PBXNativeTarget "DApp" */; + buildPhases = ( + 84CE641827981DED00142511 /* Sources */, + 84CE641927981DED00142511 /* Frameworks */, + 84CE641A27981DED00142511 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DApp; + packageProductDependencies = ( + 84CE64382798228D00142511 /* Web3 */, + 84CE643A2798229100142511 /* WalletConnect */, + ); + productName = DApp; + productReference = 84CE641C27981DED00142511 /* DApp.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -255,6 +356,9 @@ CreatedOnToolsVersion = 13.2; TestTargetID = 764E1D3B26F8D3FC00A1FB15; }; + 84CE641B27981DED00142511 = { + CreatedOnToolsVersion = 13.2; + }; }; }; buildConfigurationList = 764E1D3726F8D3FC00A1FB15 /* Build configuration list for PBXProject "ExampleApp" */; @@ -273,8 +377,9 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 764E1D3B26F8D3FC00A1FB15 /* ExampleApp */, + 764E1D3B26F8D3FC00A1FB15 /* Wallet */, 845B30EB27859686002E4094 /* ExampleAppTests */, + 84CE641B27981DED00142511 /* DApp */, ); }; /* End PBXProject section */ @@ -296,6 +401,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE641A27981DED00142511 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */, + 84CE642827981DF000142511 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -306,7 +420,6 @@ 761C64A326FB83CE004239D1 /* SessionView.swift in Sources */, 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */, 76744CF526FDFB6B00B77ED9 /* ResponderView.swift in Sources */, - 761C649A26FB7ABB004239D1 /* ProposerViewController.swift in Sources */, 8460DD002750D6F50081F94C /* SessionDetailsViewController.swift in Sources */, 76744CF926FE4D7400B77ED9 /* ActiveSessionCell.swift in Sources */, 764E1D4026F8D3FC00A1FB15 /* AppDelegate.swift in Sources */, @@ -317,7 +430,6 @@ 764E1D4226F8D3FC00A1FB15 /* SceneDelegate.swift in Sources */, 84F568C2279582D200D0A289 /* Signer.swift in Sources */, 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */, - 7603D74D2703429A00DD27A2 /* ProposerView.swift in Sources */, 764E1D5A26F8DF1B00A1FB15 /* ScannerViewController.swift in Sources */, 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */, 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */, @@ -332,12 +444,30 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE641827981DED00142511 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84CE6430279820F600142511 /* AccountsViewController.swift in Sources */, + 84CE6446279ABBF300142511 /* ClientDelegate.swift in Sources */, + 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */, + 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */, + 84CE6448279AE68600142511 /* AccountRequestViewController.swift in Sources */, + 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */, + 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */, + 84CE644B279EA1FA00142511 /* AccountRequestView.swift in Sources */, + 84CE6431279820F600142511 /* AccountsView.swift in Sources */, + 84CE643D2798322600142511 /* ConnectViewController.swift in Sources */, + 84CE6444279AB5AD00142511 /* SelectChainViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 845B30F127859686002E4094 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 764E1D3B26F8D3FC00A1FB15 /* ExampleApp */; + target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; targetProxy = 845B30F027859686002E4094 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -351,6 +481,14 @@ name = LaunchScreen.storyboard; sourceTree = ""; }; + 84CE642927981DF000142511 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 84CE642A27981DF000142511 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -534,7 +672,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wallet.app/Wallet"; }; name = Debug; }; @@ -554,7 +692,67 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wallet.app/Wallet"; + }; + name = Release; + }; + 84CE642D27981DF000142511 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = DApp/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.DApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 84CE642E27981DF000142511 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = DApp/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.DApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -570,7 +768,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 764E1D5026F8D3FE00A1FB15 /* Build configuration list for PBXNativeTarget "ExampleApp" */ = { + 764E1D5026F8D3FE00A1FB15 /* Build configuration list for PBXNativeTarget "Wallet" */ = { isa = XCConfigurationList; buildConfigurations = ( 764E1D5126F8D3FE00A1FB15 /* Debug */, @@ -588,6 +786,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84CE642F27981DF000142511 /* Build configuration list for PBXNativeTarget "DApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84CE642D27981DF000142511 /* Debug */, + 84CE642E27981DF000142511 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -611,6 +818,15 @@ package = 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */; productName = Web3; }; + 84CE64382798228D00142511 /* Web3 */ = { + isa = XCSwiftPackageProductDependency; + package = 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */; + productName = Web3; + }; + 84CE643A2798229100142511 /* WalletConnect */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnect; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 764E1D3426F8D3FC00A1FB15 /* Project object */; diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme new file mode 100644 index 000000000..60a72b1ae --- /dev/null +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/ExampleApp.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme similarity index 94% rename from Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/ExampleApp.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme index dce1bdf8a..d9652ff9d 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/ExampleApp.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme @@ -16,7 +16,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15" BuildableName = "ExampleApp.app" - BlueprintName = "ExampleApp" + BlueprintName = "Wallet" ReferencedContainer = "container:ExampleApp.xcodeproj"> @@ -33,7 +33,7 @@ @@ -56,7 +56,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15" BuildableName = "ExampleApp.app" - BlueprintName = "ExampleApp" + BlueprintName = "Wallet" ReferencedContainer = "container:ExampleApp.xcodeproj"> @@ -73,7 +73,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15" BuildableName = "ExampleApp.app" - BlueprintName = "ExampleApp" + BlueprintName = "Wallet" ReferencedContainer = "container:ExampleApp.xcodeproj"> diff --git a/Example/ExampleApp/Responder/ActiveSessionCell.swift b/Example/ExampleApp/ActiveSessionCell.swift similarity index 100% rename from Example/ExampleApp/Responder/ActiveSessionCell.swift rename to Example/ExampleApp/ActiveSessionCell.swift diff --git a/Example/ExampleApp/Responder/ActiveSessionItem.swift b/Example/ExampleApp/ActiveSessionItem.swift similarity index 100% rename from Example/ExampleApp/Responder/ActiveSessionItem.swift rename to Example/ExampleApp/ActiveSessionItem.swift diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index 1fa4cd501..84b6f57d8 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Demo App + Wallet CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/Example/ExampleApp/Proposer/ProposerViewController.swift b/Example/ExampleApp/Proposer/ProposerViewController.swift deleted file mode 100644 index 65a3fee8a..000000000 --- a/Example/ExampleApp/Proposer/ProposerViewController.swift +++ /dev/null @@ -1,181 +0,0 @@ -import UIKit -import WalletConnect - -final class ProposerViewController: UIViewController { - - let client: WalletConnectClient = { - let metadata = AppMetadata( - name: "Example Proposer", - description: "a description", - url: "wallet.connect", - icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) - return WalletConnectClient( - metadata: metadata, - projectId: "52af113ee0c1e1a20f4995730196c13e", - isController: false, - relayHost: "relay.dev.walletconnect.com", - clientName: "proposer" - ) - }() - - var activeItems: [ActiveSessionItem] = [] - private var currentURI: String? - - private let proposerView: ProposerView = { - ProposerView() - }() - - override func loadView() { - view = proposerView - } - - override func viewDidLoad() { - super.viewDidLoad() - navigationItem.title = "Dapp" - - navigationItem.rightBarButtonItem = UIBarButtonItem( - title: "Connect", - style: .plain, - target: self, - action: #selector(connect) - ) - - proposerView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) - proposerView.copyButton.isHidden = true - - proposerView.tableView.dataSource = self - proposerView.tableView.delegate = self - - client.delegate = self - } - - @objc func copyURI() { - UIPasteboard.general.string = currentURI - } - - @objc - private func connect() { - print("[PROPOSER] Connecting to a pairing...") - let permissions = Session.Permissions( - blockchains: ["a chain"], - methods: ["a method"], - notifications: [] - ) - do { - if let uri = try client.connect(sessionPermissions: permissions) { - showQRCode(uriString: uri) - } - } catch { - print("[PROPOSER] Pairing connect error: \(error)") - } - } - - private func showQRCode(uriString: String) { - currentURI = uriString - DispatchQueue.global().async { [weak self] in - if let qrImage = self?.generateQRCode(from: uriString) { - DispatchQueue.main.async { - self?.proposerView.qrCodeView.image = qrImage - self?.proposerView.copyButton.isHidden = false - } - } - } - } - - private func generateQRCode(from string: String) -> UIImage? { - let data = string.data(using: .ascii) - if let filter = CIFilter(name: "CIQRCodeGenerator") { - filter.setValue(data, forKey: "inputMessage") - if let output = filter.outputImage { - return UIImage(ciImage: output) - } - } - return nil - } -} - -extension ProposerViewController: UITableViewDataSource, UITableViewDelegate { - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - activeItems.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "sessionCell", for: indexPath) as! ActiveSessionCell - cell.item = activeItems[indexPath.row] - return cell - } - - func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - activeItems.remove(at: indexPath.row) - tableView.deleteRows(at: [indexPath], with: .automatic) - } - } - - func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? { - "Disconnect" - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - print("did select row \(indexPath)") - } -} - -extension ProposerViewController: WalletConnectClientDelegate { - - func didReceive(sessionProposal: Session.Proposal) { - print("[PROPOSER] WC: Did receive session proposal") - } - - func didReceive(sessionRequest: Request) { - print("[PROPOSER] WC: Did receive session request") - } - - func didReceive(notification: Session.Notification, sessionTopic: String) { - - } - - func didUpgrade(sessionTopic: String, permissions: Session.Permissions) { - - } - - func didUpdate(sessionTopic: String, accounts: Set) { - - } - - func didUpdate(pairingTopic: String, appMetadata: AppMetadata) { - - } - - func didDelete(sessionTopic: String, reason: Reason) { - - } - - func didSettle(session: Session) { - print("[PROPOSER] WC: Did settle session") - } - - func didSettle(pairing: Pairing) { - print("[PROPOSER] WC: Did settle pairing") - let settledPairings = client.getSettledPairings() - let activePairings = settledPairings.map { pairing -> ActiveSessionItem in - let app = pairing.peer - return ActiveSessionItem( - dappName: app?.name ?? "", - dappURL: app?.url ?? "", - iconURL: app?.icons?.first ?? "", - topic: pairing.topic) - } - DispatchQueue.main.async { - self.activeItems = activePairings - self.proposerView.tableView.reloadData() - self.proposerView.qrCodeView.image = nil - self.proposerView.copyButton.isHidden = true - } - } - - func didReject(pendingSessionTopic: String, reason: Reason) { - print("[PROPOSER] WC: Did reject session") - } -} diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index c5049becd..855520c35 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -43,6 +43,7 @@ final class ResponderViewController: UIViewController { let settledSessions = client.getSettledSessions() sessionItems = getActiveSessionItem(for: settledSessions) client.delegate = self + client.logger.setLogging(level: .debug) } @objc @@ -119,7 +120,6 @@ extension ResponderViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { let item = sessionItems[indexPath.row] -// let deleteParams = SessionType.DeleteParams(topic: item.topic, reason: SessionType.Reason(code: 0, message: "disconnect")) client.disconnect(topic: item.topic, reason: Reason(code: 0, message: "disconnect")) sessionItems.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .automatic) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 3cdd41306..19514b446 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -17,8 +17,6 @@ extension UITabBarController { static func createExampleApp() -> UITabBarController { let responderController = UINavigationController(rootViewController: ResponderViewController()) responderController.tabBarItem = UITabBarItem(title: "Wallet", image: UIImage(systemName: "dollarsign.circle"), selectedImage: nil) - let proposerController = UINavigationController(rootViewController: ProposerViewController()) - proposerController.tabBarItem = UITabBarItem(title: "Dapp", image: UIImage(systemName: "appclip"), selectedImage: nil) let tabBarController = UITabBarController() tabBarController.viewControllers = [responderController] return tabBarController diff --git a/Example/fastlane/Fastfile b/Example/fastlane/Fastfile index b63a04a84..1cdff6748 100644 --- a/Example/fastlane/Fastfile +++ b/Example/fastlane/Fastfile @@ -4,6 +4,6 @@ default_platform(:ios) platform :ios do desc "Test Example App" lane :test_app do - scan(xcargs: "-allowProvisioningUpdates") + scan(xcargs: "-allowProvisioningUpdates", scheme: "Wallet") end end diff --git a/Sources/WalletConnect/Engine/PairingEngine.swift b/Sources/WalletConnect/Engine/PairingEngine.swift index 56d85e036..bff5522e0 100644 --- a/Sources/WalletConnect/Engine/PairingEngine.swift +++ b/Sources/WalletConnect/Engine/PairingEngine.swift @@ -2,6 +2,7 @@ import Foundation import Combine import WalletConnectUtils + final class PairingEngine { var onApprovalAcknowledgement: ((Pairing) -> Void)? @@ -163,7 +164,7 @@ final class PairingEngine { } } } - + private func setUpWCRequestHandling() { wcSubscriber.onReceivePayload = { [unowned self] subscriptionPayload in let requestId = subscriptionPayload.wcRequest.id diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 107af3cd2..f6e3b8fda 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -56,7 +56,7 @@ final class SessionEngine { sequencesStore.getAll().compactMap { guard let settled = $0.settled else { return nil } let permissions = Session.Permissions(blockchains: settled.permissions.blockchain.chains, methods: settled.permissions.jsonrpc.methods) - return Session(topic: $0.topic, peer: settled.peer.metadata!, permissions: permissions) + return Session(topic: $0.topic, peer: settled.peer.metadata!, permissions: permissions, accounts: settled.state.accounts) } } @@ -484,7 +484,7 @@ final class SessionEngine { peer: approveParams.responder.metadata, permissions: Session.Permissions( blockchains: pendingSession.proposal.permissions.blockchain.chains, - methods: pendingSession.proposal.permissions.jsonrpc.methods)) + methods: pendingSession.proposal.permissions.jsonrpc.methods), accounts: settledSession.settled!.state.accounts) let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in @@ -546,6 +546,7 @@ final class SessionEngine { } switch result { case .success: + guard let settledSession = try? sequencesStore.getSequence(forTopic: settledTopic) else {return} crypto.deleteAgreementSecret(for: topic) wcSubscriber.removeSubscription(topic: topic) sequencesStore.delete(topic: topic) @@ -554,7 +555,7 @@ final class SessionEngine { peer: proposal.proposer.metadata, permissions: Session.Permissions( blockchains: proposal.permissions.blockchain.chains, - methods: proposal.permissions.jsonrpc.methods)) + methods: proposal.permissions.jsonrpc.methods), accounts: settledSession.settled!.state.accounts) onApprovalAcknowledgement?(sessionSuccess) case .failure: wcSubscriber.removeSubscription(topic: topic) diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index 919cd58a8..d384f0dbf 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -105,6 +105,7 @@ class WalletConnectRelay: WalletConnectRelaying { func respond(topic: String, response: JsonRpcResponseTypes, completion: @escaping ((Error?)->())) { do { _ = try jsonRpcHistory.resolve(response: response) + let message = try jsonRpcSerializer.serialize(topic: topic, encodable: response.value) logger.debug("Responding....topic: \(topic)") networkRelayer.publish(topic: topic, payload: message) { error in diff --git a/Sources/WalletConnect/Session.swift b/Sources/WalletConnect/Session.swift index cd790bf29..6b371767b 100644 --- a/Sources/WalletConnect/Session.swift +++ b/Sources/WalletConnect/Session.swift @@ -7,6 +7,7 @@ public struct Session { public let topic: String public let peer: AppMetadata public let permissions: Permissions + public let accounts: Set } extension Session { diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index d1bba4470..c31be0470 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -31,7 +31,10 @@ public final class WalletConnectClient { private let secureStorage: SecureStorage private let pairingQueue = DispatchQueue(label: "com.walletconnect.sdk.client.pairing", qos: .userInitiated) private let history: JsonRpcHistory - +#if os(iOS) + private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid +#endif + // MARK: - Initializers /// Initializes and returns newly created WalletConnect Client Instance. Establishes a network connection with the relay @@ -68,8 +71,25 @@ public final class WalletConnectClient { self.sessionEngine = SessionEngine(relay: relay, crypto: crypto, subscriber: WCSubscriber(relay: relay, logger: logger), sequencesStore: sessionSequencesStore, isController: isController, metadata: metadata, logger: logger) setUpEnginesCallbacks() subscribeNotificationCenter() + registerBackgroundTask() } + func registerBackgroundTask() { +#if os(iOS) + backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Finish Network Tasks") { [weak self] in + self?.endBackgroundTask() + } +#endif + } + + func endBackgroundTask() { +#if os(iOS) + wakuRelay.disconnect(closeCode: .goingAway) + print("Background task ended.") + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid +#endif + } deinit { unsubscribeNotificationCenter() } @@ -228,6 +248,7 @@ public final class WalletConnectClient { pairingEngine.getSettledPairings() } + /// - Returns: Pending requests received with wc_sessionPayload public func getPendingRequests() -> [Request] { history.getPending() .filter{$0.request.method == .sessionPayload} @@ -251,9 +272,7 @@ public final class WalletConnectClient { self?.delegate?.didSettle(pairing: settledPairing) } sessionEngine.onSessionApproved = { [unowned self] settledSession in - let permissions = Session.Permissions.init(blockchains: settledSession.permissions.blockchains, methods: settledSession.permissions.methods) - let session = Session(topic: settledSession.topic, peer: settledSession.peer, permissions: permissions) - delegate?.didSettle(session: session) + delegate?.didSettle(session: settledSession) } sessionEngine.onApprovalAcknowledgement = { [weak self] session in self?.delegate?.didSettle(session: session) @@ -295,11 +314,6 @@ public final class WalletConnectClient { private func subscribeNotificationCenter() { #if os(iOS) - NotificationCenter.default.addObserver( - self, - selector: #selector(appDidEnterBackground), - name: UIApplication.didEnterBackgroundNotification, - object: nil) NotificationCenter.default.addObserver( self, selector: #selector(appWillEnterForeground), @@ -310,7 +324,6 @@ public final class WalletConnectClient { private func unsubscribeNotificationCenter() { #if os(iOS) - NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil) #endif } @@ -318,10 +331,7 @@ public final class WalletConnectClient { @objc private func appWillEnterForeground() { wakuRelay.connect() + registerBackgroundTask() } - - @objc - private func appDidEnterBackground() { - wakuRelay.disconnect(closeCode: .goingAway) - } + }