diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 05a062092..6db21cbfb 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 76744CF526FDFB6B00B77ED9 /* ResponderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76744CF426FDFB6B00B77ED9 /* ResponderView.swift */; }; 76744CF726FE4D5400B77ED9 /* ActiveSessionItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */; }; 76744CF926FE4D7400B77ED9 /* ActiveSessionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */; }; + 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; + 844943A1278EC49700CC26BB /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 844943A0278EC49700CC26BB /* Web3 */; }; 845B30EF27859686002E4094 /* ExampleAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B30EE27859686002E4094 /* ExampleAppTests.swift */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 8460DD002750D6F50081F94C /* SessionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */; }; @@ -58,6 +60,7 @@ 76744CF426FDFB6B00B77ED9 /* ResponderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponderView.swift; sourceTree = ""; }; 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionItem.swift; sourceTree = ""; }; 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionCell.swift; sourceTree = ""; }; + 84494387278D9C1B00CC26BB /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = ""; }; 845B30EC27859686002E4094 /* ExampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 845B30EE27859686002E4094 /* ExampleAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleAppTests.swift; sourceTree = ""; }; 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; @@ -71,6 +74,7 @@ buildActionMask = 2147483647; files = ( 764E1D5826F8DBAB00A1FB15 /* WalletConnect in Frameworks */, + 844943A1278EC49700CC26BB /* Web3 in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,6 +92,7 @@ isa = PBXGroup; children = ( 761C649B26FB7B7F004239D1 /* ResponderViewController.swift */, + 84494387278D9C1B00CC26BB /* UIAlertController.swift */, 76744CF426FDFB6B00B77ED9 /* ResponderView.swift */, 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */, 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */, @@ -204,6 +209,7 @@ name = ExampleApp; packageProductDependencies = ( 764E1D5726F8DBAB00A1FB15 /* WalletConnect */, + 844943A0278EC49700CC26BB /* Web3 */, ); productName = ExampleApp; productReference = 764E1D3C26F8D3FC00A1FB15 /* ExampleApp.app */; @@ -254,6 +260,9 @@ Base, ); mainGroup = 764E1D3326F8D3FC00A1FB15; + packageReferences = ( + 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */, + ); productRefGroup = 764E1D3D26F8D3FC00A1FB15 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -303,6 +312,7 @@ 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */, 7603D74D2703429A00DD27A2 /* ProposerView.swift in Sources */, 764E1D5A26F8DF1B00A1FB15 /* ScannerViewController.swift in Sources */, + 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -572,11 +582,27 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Boilertalk/Web3.swift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.5.3; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ 764E1D5726F8DBAB00A1FB15 /* WalletConnect */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 844943A0278EC49700CC26BB /* Web3 */ = { + isa = XCSwiftPackageProductDependency; + package = 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */; + productName = Web3; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 764E1D3426F8D3FC00A1FB15 /* Project object */; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5df42d3fc..6563deae7 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "BigInt", + "repositoryURL": "https://github.com/attaswift/BigInt.git", + "state": { + "branch": null, + "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", + "version": "5.3.0" + } + }, { "package": "CryptoSwift", "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", @@ -9,6 +18,33 @@ "revision": "4b0565384d3c4c588af09e660535b2c7c9bf5b39", "version": "1.4.2" } + }, + { + "package": "PromiseKit", + "repositoryURL": "https://github.com/mxcl/PromiseKit.git", + "state": { + "branch": null, + "revision": "93c8d41ce96ed78f36c3948be396d76f3ca3de1b", + "version": "6.16.2" + } + }, + { + "package": "secp256k1", + "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift.git", + "state": { + "branch": null, + "revision": "823281fe9def21b384099b72a9a53ca988317b20", + "version": "0.1.4" + } + }, + { + "package": "Web3", + "repositoryURL": "https://github.com/Boilertalk/Web3.swift", + "state": { + "branch": null, + "revision": "1a6830ecc093f0f19054fed4c135dfee7bebe2b2", + "version": "0.5.3" + } } ] }, diff --git a/Example/ExampleApp/Proposer/ProposerViewController.swift b/Example/ExampleApp/Proposer/ProposerViewController.swift index aaf233611..60d18a86c 100644 --- a/Example/ExampleApp/Proposer/ProposerViewController.swift +++ b/Example/ExampleApp/Proposer/ProposerViewController.swift @@ -11,9 +11,9 @@ final class ProposerViewController: UIViewController { icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) return WalletConnectClient( metadata: metadata, - projectId: "", - isController: false, - relayHost: "relay.walletconnect.org", + projectId: "52af113ee0c1e1a20f4995730196c13e", + isController: true, + relayHost: "relay.dev.walletconnect.com", clientName: "proposer" ) }() diff --git a/Example/ExampleApp/Responder/Request/RequestViewController.swift b/Example/ExampleApp/Responder/Request/RequestViewController.swift index 61fe15ed2..0bac90b73 100644 --- a/Example/ExampleApp/Responder/Request/RequestViewController.swift +++ b/Example/ExampleApp/Responder/Request/RequestViewController.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import WalletConnect +import Web3 class RequestViewController: UIViewController { var onSign: (()->())? @@ -23,13 +24,8 @@ class RequestViewController: UIViewController { super.viewDidLoad() requestView.approveButton.addTarget(self, action: #selector(signAction), for: .touchUpInside) requestView.rejectButton.addTarget(self, action: #selector(rejectAction), for: .touchUpInside) - let method = sessionRequest.method - requestView.nameLabel.text = method - var paramsDescription = "" - if method == "personal_sign" { - paramsDescription = try! sessionRequest.params.get([String].self).description - } - requestView.descriptionLabel.text = paramsDescription + requestView.nameLabel.text = sessionRequest.method + requestView.descriptionLabel.text = getParamsDescription() } required init?(coder: NSCoder) { @@ -47,6 +43,19 @@ class RequestViewController: UIViewController { onReject?() dismiss(animated: true) } + + private func getParamsDescription() -> String { + let method = sessionRequest.method + if method == "personal_sign" { + return try! sessionRequest.params.get([String].self).description + } else if method == "eth_signTypedData" { + return try! sessionRequest.params.get([String].self).description + } else if method == "eth_sendTransaction" { + let params = try! sessionRequest.params.get([EthereumTransaction].self) + return params[0].description + } + fatalError("not implemented") + } } final class RequestView: UIView { diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index dfbcc2d60..33c0cbfb9 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -1,6 +1,8 @@ import UIKit import WalletConnect import WalletConnectUtils +import Web3 +import CryptoSwift final class ResponderViewController: UIViewController { @@ -12,15 +14,16 @@ final class ResponderViewController: UIViewController { icons: ["https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"]) return WalletConnectClient( metadata: metadata, - projectId: "", + projectId: "52af113ee0c1e1a20f4995730196c13e", isController: true, - relayHost: "relay.walletconnect.org", + relayHost: "relay.dev.walletconnect.com", clientName: "responder" ) }() - let account = "0x022c0c42a80bd19EA4cF0F94c4F9F96645759716" + lazy var account = privateKey.address.hex(eip55: true) var sessionItems: [ActiveSessionItem] = [] var currentProposal: Session.Proposal? + let privateKey: EthereumPrivateKey = try! EthereumPrivateKey(hexPrivateKey: "0xe56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") private let responderView: ResponderView = { ResponderView() @@ -78,10 +81,10 @@ final class ResponderViewController: UIViewController { private func showSessionRequest(_ sessionRequest: Request) { let requestVC = RequestViewController(sessionRequest) - requestVC.onSign = { [weak self] in - let result = "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" - let response = JSONRPCResponse(id: sessionRequest.id, result: AnyCodable(result)) - self?.client.respond(topic: sessionRequest.topic, response: .response(response)) + requestVC.onSign = { [unowned self] in + let result = signEth(request: sessionRequest) + let response = JSONRPCResponse(id: sessionRequest.id, result: result) + client.respond(topic: sessionRequest.topic, response: .response(response)) } requestVC.onReject = { [weak self] in self?.client.respond(topic: sessionRequest.topic, response: .error(JSONRPCErrorResponse(id: sessionRequest.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) @@ -224,24 +227,60 @@ extension ResponderViewController: WalletConnectClientDelegate { self.responderView.tableView.reloadData() } } -} - -extension UIAlertController { - - static func createInputAlert(confirmHandler: @escaping (String) -> Void) -> UIAlertController { - let alert = UIAlertController(title: "Paste URI", message: "Enter a WalletConnect URI to connect.", preferredStyle: .alert) - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) - let confirmAction = UIAlertAction(title: "Connect", style: .default) { _ in - if let input = alert.textFields?.first?.text, !input.isEmpty { - confirmHandler(input) - } - } - alert.addTextField { textField in - textField.placeholder = "wc://a14aefb980188fc35ec9..." + + func signEth(request: Request) -> AnyCodable { + let method = request.method + if method == "personal_sign" { + let params = try! request.params.get([String].self) + let messageToSign = params[0] + let signHash = signHash(messageToSign) + let (v, r, s) = try! self.privateKey.sign(hash: signHash) + let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) + return AnyCodable(result) + } else if method == "eth_signTypedData" { + let params = try! request.params.get([String].self) + print(params) + let messageToSign = params[1] + let signHash = signHash(messageToSign) + let (v, r, s) = try! self.privateKey.sign(hash: signHash) + let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) + return AnyCodable(result) + } else if method == "eth_sendTransaction" { + let params = try! request.params.get([EthereumTransaction].self) + var transaction = params[0] + transaction.gas = EthereumQuantity(quantity: BigUInt("1234")) + print(transaction.description) + let signedTx = try! transaction.sign(with: self.privateKey, chainId: 4) + let (r, s, v) = (signedTx.r, signedTx.s, signedTx.v) + let result = r.hex() + s.hex().dropFirst(2) + String(v.quantity, radix: 16) + return AnyCodable(result) + } else { + print("TODO") } - alert.addAction(cancelAction) - alert.addAction(confirmAction) - alert.preferredAction = confirmAction - return alert + fatalError() + } + + func signHash(_ message: String) -> Bytes { + let prefix = "\u{19}Ethereum Signed Message:\n" + let messageData = Data(hex: message) + let prefixData = (prefix + String(messageData.count)).data(using: .utf8)! + let prefixedMessageData = prefixData + messageData + let dataToHash: Bytes = .init(hex: prefixedMessageData.toHexString()) + return SHA3(variant: .keccak256).calculate(for: dataToHash) + } + +} + +extension EthereumTransaction { + var description: String { + return """ + from: \(String(describing: from!.hex(eip55: true))) + to: \(String(describing: to!.hex(eip55: true))), + value: \(String(describing: value!.hex())), + gasPrice: \(String(describing: gasPrice?.hex())), + gas: \(String(describing: gas?.hex())), + data: \(data.hex()), + nonce: \(String(describing: nonce?.hex())) + """ } } diff --git a/Example/ExampleApp/Responder/UIAlertController.swift b/Example/ExampleApp/Responder/UIAlertController.swift new file mode 100644 index 000000000..1ceeb50b4 --- /dev/null +++ b/Example/ExampleApp/Responder/UIAlertController.swift @@ -0,0 +1,21 @@ +import UIKit + +extension UIAlertController { + + static func createInputAlert(confirmHandler: @escaping (String) -> Void) -> UIAlertController { + let alert = UIAlertController(title: "Paste URI", message: "Enter a WalletConnect URI to connect.", preferredStyle: .alert) + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + let confirmAction = UIAlertAction(title: "Connect", style: .default) { _ in + if let input = alert.textFields?.first?.text, !input.isEmpty { + confirmHandler(input) + } + } + alert.addTextField { textField in + textField.placeholder = "wc://a14aefb980188fc35ec9..." + } + alert.addAction(cancelAction) + alert.addAction(confirmAction) + alert.preferredAction = confirmAction + return alert + } +} diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index de221ae10..98666ddcb 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -17,10 +17,10 @@ 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 proposerController = UINavigationController(rootViewController: ProposerViewController()) +// proposerController.tabBarItem = UITabBarItem(title: "Dapp", image: UIImage(systemName: "appclip"), selectedImage: nil) let tabBarController = UITabBarController() - tabBarController.viewControllers = [responderController, proposerController] + tabBarController.viewControllers = [responderController] return tabBarController } } diff --git a/Sources/Relayer/SocketConnectionObserving.swift b/Sources/Relayer/SocketConnectionObserving.swift index fff213085..bb5ca1cfb 100644 --- a/Sources/Relayer/SocketConnectionObserving.swift +++ b/Sources/Relayer/SocketConnectionObserving.swift @@ -6,7 +6,7 @@ protocol SocketConnectionObserving { var onDisconnect: (()->())? {get set} } -class SocketConnectionObserver: NSObject, URLSessionDelegate, SocketConnectionObserving { +class SocketConnectionObserver: NSObject, URLSessionWebSocketDelegate, SocketConnectionObserving { var onConnect: (()->())? var onDisconnect: (()->())?