|
| 1 | +// web3swift |
| 2 | +// |
| 3 | +// Created by Alex Vlasov. |
| 4 | +// Copyright © 2018 Alex Vlasov. All rights reserved. |
| 5 | +// |
| 6 | + |
| 7 | +import Foundation |
| 8 | +import BigInt |
| 9 | +import Starscream |
| 10 | + |
| 11 | +/// Custom Web3 HTTP provider of Infura nodes. |
| 12 | +public final class InfuraProvider: Web3HttpProvider { |
| 13 | + public init?(_ net:Networks, accessToken token: String? = nil, keystoreManager manager: KeystoreManager? = nil) { |
| 14 | + var requestURLstring = "https://" + net.name + ".infura.io/" |
| 15 | + if token != nil { |
| 16 | + requestURLstring = requestURLstring + token! |
| 17 | + } |
| 18 | + let providerURL = URL(string: requestURLstring) |
| 19 | + super.init(providerURL!, network: net, keystoreManager: manager) |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +/// Custom Websocket provider of Infura nodes. |
| 24 | +public final class InfuraWebsocketProvider: WebsocketProvider { |
| 25 | + public var filterID: String? |
| 26 | + public var subscriptionIDs = Set<String>() |
| 27 | + private var subscriptionIDforUnsubscribing: String? = nil |
| 28 | + private var filterTimer: Timer? |
| 29 | + |
| 30 | + public init?(_ network: Networks, |
| 31 | + delegate: Web3SocketDelegate, |
| 32 | + keystoreManager manager: KeystoreManager? = nil) { |
| 33 | + guard network == Networks.Kovan |
| 34 | + || network == Networks.Rinkeby |
| 35 | + || network == Networks.Ropsten |
| 36 | + || network == Networks.Mainnet else {return nil} |
| 37 | + let networkName = network.name |
| 38 | + let urlString = "wss://\(networkName).infura.io/ws" |
| 39 | + guard let socketURL = URL(string: urlString) else {return nil} |
| 40 | + super.init(endpoint: socketURL, |
| 41 | + delegate: delegate, |
| 42 | + keystoreManager: manager) |
| 43 | + } |
| 44 | + |
| 45 | + public static func connectToSocket(_ network: Networks, |
| 46 | + delegate: Web3SocketDelegate, |
| 47 | + keystoreManager manager: KeystoreManager? = nil) -> InfuraWebsocketProvider? { |
| 48 | + guard let socketProvider = InfuraWebsocketProvider(network, |
| 49 | + delegate: delegate, |
| 50 | + keystoreManager: manager) else {return nil} |
| 51 | + socketProvider.connectSocket() |
| 52 | + return socketProvider |
| 53 | + } |
| 54 | + |
| 55 | + public func writeMessage(method: InfuraWebsocketMethod, params: [Encodable]) throws { |
| 56 | + let request = JSONRPCRequestFabric.prepareRequest(method, parameters: params) |
| 57 | + let encoder = JSONEncoder() |
| 58 | + let requestData = try encoder.encode(request) |
| 59 | + writeMessage(data: requestData) |
| 60 | + } |
| 61 | + |
| 62 | + public func filter(method: InfuraWebsocketMethod, params: [Encodable]? = nil) throws { |
| 63 | + filterTimer?.invalidate() |
| 64 | + filterID = nil |
| 65 | + let params = params ?? [] |
| 66 | + let paramsCount = params.count |
| 67 | + guard method.requiredNumOfParameters == paramsCount || method.requiredNumOfParameters == nil else { |
| 68 | + throw Web3Error.inputError(desc: "Wrong number of params: need - \(method.requiredNumOfParameters!), got - \(paramsCount)") |
| 69 | + } |
| 70 | + try writeMessage(method: method, params: params) |
| 71 | + filterTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(getFilterChanges), userInfo: nil, repeats: true) |
| 72 | + } |
| 73 | + |
| 74 | + @objc public func getFilterChanges() throws { |
| 75 | + if let id = self.filterID { |
| 76 | + filterTimer?.invalidate() |
| 77 | + let method = InfuraWebsocketMethod.getFilterChanges |
| 78 | + try writeMessage(method: method, params: [id]) |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + public func getFilterLogs() throws { |
| 83 | + if let id = self.filterID { |
| 84 | + let method = InfuraWebsocketMethod.getFilterLogs |
| 85 | + try writeMessage(method: method, params: [id]) |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + public func unistallFilter() throws { |
| 90 | + if let id = self.filterID { |
| 91 | + let method = InfuraWebsocketMethod.uninstallFilter |
| 92 | + try writeMessage(method: method, params: [id]) |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + public func subscribe(params: [Encodable]) throws { |
| 97 | + let method = InfuraWebsocketMethod.subscribe |
| 98 | + try writeMessage(method: method, params: params) |
| 99 | + } |
| 100 | + |
| 101 | + public func unsubscribe(subscriptionID: String) throws { |
| 102 | + let method = InfuraWebsocketMethod.unsubscribe |
| 103 | + subscriptionIDforUnsubscribing = subscriptionID |
| 104 | + try writeMessage(method: method, params: [subscriptionID]) |
| 105 | + } |
| 106 | + |
| 107 | + public func subscribeOnNewHeads() throws { |
| 108 | + let method = InfuraWebsocketMethod.subscribe |
| 109 | + let params = ["newHeads"] |
| 110 | + try writeMessage(method: method, params: params) |
| 111 | + } |
| 112 | + |
| 113 | + public func subscribeOnNewPendingTransactions() throws { |
| 114 | + let method = InfuraWebsocketMethod.subscribe |
| 115 | + let params = ["newPendingTransactions"] |
| 116 | + try writeMessage(method: method, params: params) |
| 117 | + } |
| 118 | + |
| 119 | + public func subscribeOnSyncing() throws { |
| 120 | + guard network != Networks.Kovan else { |
| 121 | + throw Web3Error.inputError(desc: "Can't sync on Kovan") |
| 122 | + } |
| 123 | + let method = InfuraWebsocketMethod.subscribe |
| 124 | + let params = ["syncing"] |
| 125 | + try writeMessage(method: method, params: params) |
| 126 | + } |
| 127 | + |
| 128 | + override public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { |
| 129 | + if let data = text.data(using: String.Encoding.utf8), |
| 130 | + let dictionary = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { |
| 131 | + if filterID == nil, |
| 132 | + let result = dictionary["result"] as? String { |
| 133 | + // setting filter id |
| 134 | + filterID = result |
| 135 | + } else if let params = dictionary["params"] as? [String: Any], |
| 136 | + let subscription = params["subscription"] as? String, |
| 137 | + let result = params["result"] { |
| 138 | + // subscription result |
| 139 | + subscriptionIDs.insert(subscription) |
| 140 | + delegate.received(message: result) |
| 141 | + } else if let unsubscribed = dictionary["result"] as? Bool { |
| 142 | + // unsubsribe result |
| 143 | + if unsubscribed == true, let id = subscriptionIDforUnsubscribing { |
| 144 | + subscriptionIDs.remove(id) |
| 145 | + } else if let id = subscriptionIDforUnsubscribing { |
| 146 | + delegate.gotError(error: Web3Error.processingError(desc: "Can\'t unsubscribe \(id)")) |
| 147 | + } else { |
| 148 | + delegate.received(message: unsubscribed) |
| 149 | + } |
| 150 | + } else if let message = dictionary["result"] { |
| 151 | + // filter result |
| 152 | + delegate.received(message: message) |
| 153 | + } else { |
| 154 | + delegate.gotError(error: Web3Error.processingError(desc: "Can\'t get known result. Message is: \(text)")) |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | +} |
0 commit comments