@@ -10,9 +10,11 @@ import Starscream
10
10
import PromiseKit
11
11
import BigInt
12
12
import Foundation
13
+ import EthereumAddress
13
14
14
15
extension web3 . Eth {
15
- public func getLatestPendingTransactions( forDelegate delegate: Web3SocketDelegate ) throws {
16
+
17
+ public func getWebsocketProvider( forDelegate delegate: Web3SocketDelegate ) throws -> InfuraWebsocketProvider {
16
18
var infuraWSProvider : InfuraWebsocketProvider
17
19
if !( provider is InfuraWebsocketProvider ) {
18
20
guard let infuraNetwork = provider. network else {
@@ -26,7 +28,17 @@ extension web3.Eth {
26
28
infuraWSProvider = provider as! InfuraWebsocketProvider
27
29
}
28
30
infuraWSProvider. connectSocket ( )
29
- try infuraWSProvider. subscribeOn ( method: . newPendingTransactionFilter)
31
+ return infuraWSProvider
32
+ }
33
+
34
+ public func getLatestPendingTransactions( forDelegate delegate: Web3SocketDelegate ) throws {
35
+ let provider = try getWebsocketProvider ( forDelegate: delegate)
36
+ try provider. filter ( method: . newPendingTransactionFilter)
37
+ }
38
+
39
+ public func subscribeOnPendingTransactions( forDelegate delegate: Web3SocketDelegate ) throws {
40
+ let provider = try getWebsocketProvider ( forDelegate: delegate)
41
+ try provider. subscribeOnNewPendingTransactions ( )
30
42
}
31
43
}
32
44
@@ -35,20 +47,40 @@ public protocol IWebsocketProvider {
35
47
var delegate : Web3SocketDelegate { get set }
36
48
func connectSocket( ) throws
37
49
func disconnectSocket( ) throws
50
+ func writeMessage( string: String )
51
+ func writeMessage( data: Data )
38
52
}
39
53
40
54
public enum InfuraWebsocketMethod : String , Encodable {
41
55
42
56
case newPendingTransactionFilter = " eth_newPendingTransactionFilter "
43
57
case getFilterChanges = " eth_getFilterChanges "
58
+ case newFilter = " eth_newFilter "
59
+ case newBlockFilter = " eth_newBlockFilter "
60
+ case getFilterLogs = " eth_getFilterLogs "
61
+ case uninstallFilter = " eth_uninstallFilter "
62
+ case subscribe = " eth_subscribe "
63
+ case unsubscribe = " eth_unsubscribe "
44
64
45
- public var requiredNumOfParameters : Int {
65
+ public var requiredNumOfParameters : Int ? {
46
66
get {
47
67
switch self {
48
68
case . newPendingTransactionFilter:
49
69
return 0
50
70
case . getFilterChanges:
51
71
return 1
72
+ case . newFilter:
73
+ return nil
74
+ case . newBlockFilter:
75
+ return 0
76
+ case . getFilterLogs:
77
+ return nil
78
+ case . uninstallFilter:
79
+ return 1
80
+ case . subscribe:
81
+ return nil
82
+ case . unsubscribe:
83
+ return 1
52
84
}
53
85
}
54
86
}
@@ -88,11 +120,14 @@ public struct InfuraWebsocketRequest: Encodable {
88
120
89
121
public protocol Web3SocketDelegate {
90
122
func received( message: Any )
123
+ func gotError( error: Error )
91
124
}
92
125
93
126
public final class InfuraWebsocketProvider : WebsocketProvider {
94
- public var subscriptionKey : String ?
95
- private var subscriptionTimer : Timer ?
127
+ public var filterID : String ?
128
+ public var subscriptionIDs = Set < String > ( )
129
+ private var subscriptionIDforUnsubscribing : String ? = nil
130
+ private var filterTimer : Timer ?
96
131
97
132
public init ? ( _ network: Networks ,
98
133
delegate: Web3SocketDelegate ,
@@ -115,52 +150,110 @@ public final class InfuraWebsocketProvider: WebsocketProvider {
115
150
guard let socketProvider = InfuraWebsocketProvider ( network,
116
151
delegate: delegate,
117
152
keystoreManager: manager) else { return nil }
118
- socketProvider. socket . connect ( )
153
+ socketProvider. connectSocket ( )
119
154
return socketProvider
120
155
}
121
156
122
- public func subscribeOn( method: InfuraWebsocketMethod , params: [ Encodable ] ? = nil ) throws {
123
- do {
124
- subscriptionTimer? . invalidate ( )
125
- subscriptionKey = nil
126
- let params = params ?? [ ]
127
- let paramsCount = params. count
128
- guard method. requiredNumOfParameters == paramsCount else {
129
- throw Web3Error . inputError ( desc: " Wrong number of params: need - \( method. requiredNumOfParameters) , got - \( paramsCount) " )
130
- }
131
- let request = JSONRPCRequestFabric . prepareRequest ( method, parameters: params)
132
- let encoder = JSONEncoder ( )
133
- let requestData = try encoder. encode ( request)
134
- socket. write ( data: requestData)
135
- subscriptionTimer = Timer . scheduledTimer ( timeInterval: 1.0 , target: self , selector: #selector( self . getSubscriptionChanges) , userInfo: nil , repeats: true )
136
- } catch {
137
- throw Web3Error . connectionError
157
+ public func writeMessage( method: InfuraWebsocketMethod , params: [ Encodable ] ) throws {
158
+ let request = JSONRPCRequestFabric . prepareRequest ( method, parameters: params)
159
+ let encoder = JSONEncoder ( )
160
+ let requestData = try encoder. encode ( request)
161
+ writeMessage ( data: requestData)
162
+ }
163
+
164
+ public func filter( method: InfuraWebsocketMethod , params: [ Encodable ] ? = nil ) throws {
165
+ filterTimer? . invalidate ( )
166
+ filterID = nil
167
+ let params = params ?? [ ]
168
+ let paramsCount = params. count
169
+ guard method. requiredNumOfParameters == paramsCount || method. requiredNumOfParameters == nil else {
170
+ throw Web3Error . inputError ( desc: " Wrong number of params: need - \( method. requiredNumOfParameters!) , got - \( paramsCount) " )
138
171
}
172
+ try writeMessage ( method: method, params: params)
173
+ filterTimer = Timer . scheduledTimer ( timeInterval: 0.1 , target: self , selector: #selector( getFilterChanges) , userInfo: nil , repeats: true )
139
174
}
140
175
141
- @objc public func getSubscriptionChanges( ) {
142
- DispatchQueue . global ( ) . async { [ unowned self] in
143
- if let key = self . subscriptionKey {
144
- self . subscriptionTimer? . invalidate ( )
145
- let method = InfuraWebsocketMethod . getFilterChanges
146
- let request = JSONRPCRequestFabric . prepareRequest ( method, parameters: [ key] )
147
- let encoder = JSONEncoder ( )
148
- if let requestData = try ? encoder. encode ( request) {
149
- self . socket. write ( data: requestData)
150
- }
151
- }
176
+ @objc public func getFilterChanges( ) throws {
177
+ if let id = self . filterID {
178
+ filterTimer? . invalidate ( )
179
+ let method = InfuraWebsocketMethod . getFilterChanges
180
+ try writeMessage ( method: method, params: [ id] )
181
+ }
182
+ }
183
+
184
+ public func getFilterLogs( ) throws {
185
+ if let id = self . filterID {
186
+ let method = InfuraWebsocketMethod . getFilterLogs
187
+ try writeMessage ( method: method, params: [ id] )
188
+ }
189
+ }
190
+
191
+ public func unistallFilter( ) throws {
192
+ if let id = self . filterID {
193
+ let method = InfuraWebsocketMethod . uninstallFilter
194
+ try writeMessage ( method: method, params: [ id] )
152
195
}
153
196
}
154
197
198
+ public func subscribe( params: [ Encodable ] ) throws {
199
+ let method = InfuraWebsocketMethod . subscribe
200
+ try writeMessage ( method: method, params: params)
201
+ }
202
+
203
+ public func unsubscribe( subscriptionID: String ) throws {
204
+ let method = InfuraWebsocketMethod . unsubscribe
205
+ subscriptionIDforUnsubscribing = subscriptionID
206
+ try writeMessage ( method: method, params: [ subscriptionID] )
207
+ }
208
+
209
+ public func subscribeOnNewHeads( ) throws {
210
+ let method = InfuraWebsocketMethod . subscribe
211
+ let params = [ " newHeads " ]
212
+ try writeMessage ( method: method, params: params)
213
+ }
214
+
215
+ public func subscribeOnNewPendingTransactions( ) throws {
216
+ let method = InfuraWebsocketMethod . subscribe
217
+ let params = [ " newPendingTransactions " ]
218
+ try writeMessage ( method: method, params: params)
219
+ }
220
+
221
+ public func subscribeOnSyncing( ) throws {
222
+ guard network != Networks . Kovan else {
223
+ throw Web3Error . inputError ( desc: " Can't sync on Kovan " )
224
+ }
225
+ let method = InfuraWebsocketMethod . subscribe
226
+ let params = [ " syncing " ]
227
+ try writeMessage ( method: method, params: params)
228
+ }
229
+
155
230
override public func websocketDidReceiveMessage( socket: WebSocketClient , text: String ) {
156
231
if let data = text. data ( using: String . Encoding. utf8) ,
157
- let dictionary = try ? JSONSerialization . jsonObject ( with: data, options: [ ] ) as? [ String : AnyObject ] {
158
- if subscriptionKey == nil ,
232
+ let dictionary = try ? JSONSerialization . jsonObject ( with: data, options: [ ] ) as? [ String : Any ] {
233
+ if filterID == nil ,
159
234
let result = dictionary [ " result " ] as? String {
160
- subscriptionKey = result
161
- } else {
162
- let result = dictionary [ " result " ] as Any
235
+ // setting filter id
236
+ filterID = result
237
+ } else if let params = dictionary [ " params " ] as? [ String : Any ] ,
238
+ let subscription = params [ " subscription " ] as? String ,
239
+ let result = params [ " result " ] {
240
+ // subscription result
241
+ subscriptionIDs. insert ( subscription)
163
242
delegate. received ( message: result)
243
+ } else if let unsubscribed = dictionary [ " result " ] as? Bool {
244
+ // unsubsribe result
245
+ if unsubscribed == true , let id = subscriptionIDforUnsubscribing {
246
+ subscriptionIDs. remove ( id)
247
+ } else if let id = subscriptionIDforUnsubscribing {
248
+ delegate. gotError ( error: Web3Error . processingError ( desc: " Can \' t unsubscribe \( id) " ) )
249
+ } else {
250
+ delegate. received ( message: unsubscribed)
251
+ }
252
+ } else if let message = dictionary [ " result " ] {
253
+ // filter result
254
+ delegate. received ( message: message)
255
+ } else {
256
+ delegate. gotError ( error: Web3Error . processingError ( desc: " Can \' t get known result. Message is: \( text) " ) )
164
257
}
165
258
}
166
259
}
@@ -169,15 +262,11 @@ public final class InfuraWebsocketProvider: WebsocketProvider {
169
262
/// The default websocket provider.
170
263
public class WebsocketProvider : Web3Provider , IWebsocketProvider , WebSocketDelegate {
171
264
public func sendAsync( _ request: JSONRPCrequest , queue: DispatchQueue ) -> Promise < JSONRPCresponse > {
172
- if request. method == nil {
173
- return Promise ( error: Web3Error . nodeError ( desc: " RPC method is nil " ) )
174
- }
175
-
176
- return Web3HttpProvider . post ( request, providerURL: self . url, queue: queue, session: self . session)
265
+ return Promise ( error: Web3Error . inputError ( desc: " Sending is unsupported for Websocket provider. Please, use \' sendMessage \' " ) )
177
266
}
178
267
179
268
public func sendAsync( _ requests: JSONRPCrequestBatch , queue: DispatchQueue ) -> Promise < JSONRPCresponseBatch > {
180
- return Web3HttpProvider . post ( requests , providerURL : self . url , queue : queue , session : self . session )
269
+ return Promise ( error : Web3Error . inputError ( desc : " Sending is unsupported for Websocket provider. Please, use \' sendMessage \' " ) )
181
270
}
182
271
183
272
public var network : Networks ?
@@ -202,16 +291,22 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg
202
291
socket = WebSocket ( url: endpoint)
203
292
socket. delegate = self
204
293
if net == nil {
205
- let request = JSONRPCRequestFabric . prepareRequest ( . getNetwork, parameters: [ ] )
206
-
207
- if let response = try ? Web3HttpProvider . post ( request,
208
- providerURL: endpoint,
209
- queue: DispatchQueue . global ( qos: . userInteractive) ,
210
- session: session) . wait ( ) ,
211
- response. error == nil ,
212
- let result: String = response. getValue ( ) ,
213
- let intNetworkNumber = Int ( result) {
214
- network = Networks . fromInt ( intNetworkNumber)
294
+ let endpointString = endpoint. absoluteString
295
+ if endpointString. hasPrefix ( " wss:// " ) && endpointString. hasSuffix ( " .infura.io/ws " ) {
296
+ let networkString = endpointString. replacingOccurrences ( of: " wss:// " , with: " " )
297
+ . replacingOccurrences ( of: " .infura.io/ws " , with: " " )
298
+ switch networkString {
299
+ case " mainnet " :
300
+ network = Networks . Mainnet
301
+ case " rinkeby " :
302
+ network = Networks . Rinkeby
303
+ case " ropsten " :
304
+ network = Networks . Ropsten
305
+ case " kovan " :
306
+ network = Networks . Kovan
307
+ default :
308
+ break
309
+ }
215
310
}
216
311
} else {
217
312
network = net
@@ -231,13 +326,21 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg
231
326
keystoreManager manager: KeystoreManager ? = nil ,
232
327
network net: Networks ? = nil ) -> WebsocketProvider {
233
328
let socketProvider = WebsocketProvider ( endpoint: endpoint,
234
- delegate: delegate,
235
- keystoreManager: manager,
236
- network: net)
329
+ delegate: delegate,
330
+ keystoreManager: manager,
331
+ network: net)
237
332
socketProvider. connectSocket ( )
238
333
return socketProvider
239
334
}
240
335
336
+ public func writeMessage( string: String ) {
337
+ socket. write ( string: string)
338
+ }
339
+
340
+ public func writeMessage( data: Data ) {
341
+ socket. write ( data: data)
342
+ }
343
+
241
344
public func websocketDidReceiveMessage( socket: WebSocketClient , text: String ) {
242
345
print ( " got some text: \( text) " )
243
346
delegate. received ( message: text)
0 commit comments