Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding support for WebSocket #714

Draft
wants to merge 36 commits into
base: develop-4.0
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b26f3a6
moved keystore manager to web3 main object
odanylovych Dec 29, 2021
d3b410c
renamed attachedKeystoreManager
odanylovych Dec 29, 2021
791a863
added Web3SubscriptionProvider protocol
odanylovych Dec 31, 2021
9368301
some fixes for subscription provider
odanylovych Dec 31, 2021
5659133
Merge branch 'skywinder:develop' into develop
odanylovych Dec 31, 2021
98b0796
optional websocket delegator
odanylovych Jan 4, 2022
21779a2
moved common methods to default websocket provider
odanylovych Jan 4, 2022
d65eae1
merged jsonrpc methods
odanylovych Jan 4, 2022
c716c26
added verification for supported request methods
odanylovych Jan 4, 2022
e0bff8d
implemented sendAsync in WebsocketProvider
odanylovych Jan 5, 2022
a8a2120
implemented sendAsync for batch requests
odanylovych Jan 5, 2022
0ed27cf
added filter requests
odanylovych Jan 6, 2022
400913e
added subscribe methods to Eth
odanylovych Jan 7, 2022
ec49477
set queue when calling subscribe
odanylovych Jan 10, 2022
a3f7b5c
added internalQueue to WebsocketProvider
odanylovych Jan 10, 2022
46abd66
removed unneeded IWebsocketProvider
odanylovych Jan 10, 2022
4e6a096
added queue as param in subscribe
odanylovych Jan 10, 2022
90329c3
added method to convert SubscribeEventFilter to params
odanylovych Jan 10, 2022
9044573
reworked writeTimer to pendingRequests
odanylovych Jan 10, 2022
1ba9885
moved infura specific constructor
odanylovych Jan 11, 2022
4db90c1
removed url session from websocket
odanylovych Jan 11, 2022
bf9997c
fixes for subscribe methods
odanylovych Jan 12, 2022
86088cf
tests for subscribe methods
odanylovych Jan 12, 2022
b570545
added init to ErrorMessage, made batch requests public
odanylovych Jan 19, 2022
69b0a3f
created JSONRPCresponse.Result struct
odanylovych Jan 20, 2022
428251d
minor improvement: removed guard statement
odanylovych Feb 23, 2022
2d90894
removed LogItem and used EventLog
odanylovych Feb 24, 2022
54cf7e9
Merge remote-tracking branch 'upstream/develop' into develop
odanylovych May 3, 2022
d1add3b
added hash field in BlockHeader struct
odanylovych May 6, 2022
2c3cf29
Merge remote-tracking branch 'upstream/develop' into develop
odanylovych May 6, 2022
907d3da
included new files in project
odanylovych May 7, 2022
7c90489
updated websockets documentation
odanylovych May 13, 2022
4cfa03b
made EthereumTransaction.encode public
odanylovych May 17, 2022
e5a6579
chore: merge with develop
JeneaVranceanu Dec 20, 2022
2a0625e
chore: merge branch 'develop-upstream' into feat/webscoket-support
JeneaVranceanu Dec 20, 2022
6861408
fix: returned back the type of `network` to `Networks?` (optional)
JeneaVranceanu Dec 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
added Web3SubscriptionProvider protocol
  • Loading branch information
odanylovych committed Dec 31, 2021
commit 791a863f53cef284e9a724b3df8888f93cf4a049
2 changes: 2 additions & 0 deletions Sources/web3swift/Web3/Web3+Eth+Websocket.swift
Original file line number Diff line number Diff line change
@@ -39,4 +39,6 @@ extension web3.Eth {
let provider = try getWebsocketProvider(forDelegate: delegate)
try provider.subscribeOnNewPendingTransactions()
}

// TODO: make 4 subscribe methods
}
84 changes: 63 additions & 21 deletions Sources/web3swift/Web3/Web3+InfuraProviders.swift
Original file line number Diff line number Diff line change
@@ -40,8 +40,8 @@ public final class InfuraProvider: Web3HttpProvider {
/// Custom Websocket provider of Infura nodes.
public final class InfuraWebsocketProvider: WebsocketProvider {
public var filterID: String?
public var subscriptionIDs = Set<String>()
private var subscriptionIDforUnsubscribing: String? = nil
public var subscriptions = [String: (sub: WebsocketSubscription, cb: (Result<Decodable, Error>) -> Void)]()
private var requests = [UInt32: (Result<Decodable, Error>) -> Void]()
private var filterTimer: Timer?

/// if set debugMode True then show websocket events logs in the console
@@ -120,6 +120,16 @@ public final class InfuraWebsocketProvider: WebsocketProvider {
writeMessage(requestData)
}

private func send(id: UInt32, method: InfuraWebsocketMethod, params: [Encodable], cb: @escaping (Result<Decodable, Error>) -> Void) {
do {
try writeMessage(method: method, params: params)
} catch {
cb(.failure(error))
return
}
requests[id] = cb
}

public func setFilterAndGetChanges(method: InfuraWebsocketMethod, params: [Encodable]? = nil) throws {
filterTimer?.invalidate()
filterID = nil
@@ -188,15 +198,51 @@ public final class InfuraWebsocketProvider: WebsocketProvider {
}
}

public func subscribe(params: [Encodable]) throws {
override public func subscribe<R>(filter: SubscribeEventFilter,
listener: @escaping Web3SubscriptionListener<R>) throws -> Subscription {
let params: [Encodable]
switch filter {
case .newHeads:
params = ["newHeads"]
case .logs(let p):
params = ["logs", p]
case .newPendingTransactions:
params = ["newPendingTransactions"]
case .syncing:
params = ["syncing"]
}
let method = InfuraWebsocketMethod.subscribe
try writeMessage(method: method, params: params)
}

public func unsubscribe(subscriptionID: String) throws {
let method = InfuraWebsocketMethod.unsubscribe
subscriptionIDforUnsubscribing = subscriptionID
try writeMessage(method: method, params: [subscriptionID])
let subscription = WebsocketSubscription(unsubscribeCallback: { subscription in
let method = InfuraWebsocketMethod.unsubscribe
self.send(id: self.newID(), method: method, params: [subscription.id]) { result in
switch result {
case .success(let data):
let unsubscribed = data as! Bool
if unsubscribed {
self.subscriptions.removeValue(forKey: subscription.id)
} else {
self.delegate.gotError(error: Web3Error.processingError(desc: "Can\'t unsubscribe \(subscription.id)"))
}
case .failure(let error):
// TODO: handle error
fatalError("Not implemented")
}
}
})
let handler = { (result: Result<Decodable, Error>) in
listener(result.map { $0 as! R })
}
send(id: newID(), method: method, params: params) { result in
switch result {
case .success(let data):
let subscriptionID = data as! String
self.subscriptions[subscriptionID] = (subscription, handler)
case .failure(let error):
handler(.failure(error))
}
}
fatalError()
}

public func subscribeOnNewHeads() throws {
@@ -281,21 +327,17 @@ public final class InfuraWebsocketProvider: WebsocketProvider {
let result = dictionary["result"] as? String {
// setting filter id
filterID = result
} else if let id = dictionary["id"] as? UInt32 {
if let request = requests.removeValue(forKey: id) {
request(.success(dictionary["result"] as! Decodable))
}
} else if let params = dictionary["params"] as? [String: Any],
let subscription = params["subscription"] as? String,
let subscriptionID = params["subscription"] as? String,
let result = params["result"] {
// subscription result
subscriptionIDs.insert(subscription)
delegate.received(message: result)
} else if let unsubscribed = dictionary["result"] as? Bool {
// unsubsribe result
if unsubscribed == true, let id = subscriptionIDforUnsubscribing {
subscriptionIDs.remove(id)
} else if let id = subscriptionIDforUnsubscribing {
delegate.gotError(error: Web3Error.processingError(desc: "Can\'t unsubscribe \(id)"))
} else {
delegate.received(message: unsubscribed)
if let subscription = subscriptions[subscriptionID] {
subscription.cb(.success(result as! Decodable))
}
delegate.received(message: result)
} else if let message = dictionary["result"] {
// filter result
delegate.received(message: message)
26 changes: 26 additions & 0 deletions Sources/web3swift/Web3/Web3+SubscriptionProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Web3+SubscriptionProvider.swift
//
//
// Created by Ostap Danylovych on 29.12.2021.
//

import Foundation

public enum SubscribeEventFilter {
case newHeads
case logs(params: Encodable)
case newPendingTransactions
case syncing
}

public protocol Subscription {
func unsubscribe() throws
}

public typealias Web3SubscriptionListener<R: Decodable> = (Result<R, Error>) -> Void

public protocol Web3SubscriptionProvider: Web3Provider {
func subscribe<R>(filter: SubscribeEventFilter,
listener: @escaping Web3SubscriptionListener<R>) throws -> Subscription
}
27 changes: 26 additions & 1 deletion Sources/web3swift/Web3/Web3+WebsocketProvider.swift
Original file line number Diff line number Diff line change
@@ -91,8 +91,21 @@ public protocol Web3SocketDelegate {
func gotError(error: Error)
}

public struct WebsocketSubscription: Subscription {
public var id = ""
private let unsubscribeCallback: (Self) -> Void

public init(unsubscribeCallback: @escaping (Self) -> Void) {
self.unsubscribeCallback = unsubscribeCallback
}

public func unsubscribe() throws {
unsubscribeCallback(self)
}
}

/// The default websocket provider.
public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDelegate {
public class WebsocketProvider: Web3SubscriptionProvider, IWebsocketProvider, WebSocketDelegate {

public func sendAsync(_ request: JSONRPCrequest, queue: DispatchQueue) -> Promise<JSONRPCresponse> {
return Promise(error: Web3Error.inputError(desc: "Sending is unsupported for Websocket provider. Please, use \'sendMessage\'"))
@@ -119,6 +132,8 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg
private var messagesStringToWrite: [String] = []
private var messagesDataToWrite: [Data] = []

private var currentID: UInt32 = 0

public init?(_ endpoint: URL,
delegate wsdelegate: Web3SocketDelegate,
projectId: String? = nil,
@@ -202,6 +217,16 @@ public class WebsocketProvider: Web3Provider, IWebsocketProvider, WebSocketDeleg
writeTimer?.invalidate()
}

public func newID() -> UInt32 {
currentID = currentID == UInt32.max ? 1 : currentID + 1
return currentID
}

public func subscribe<R>(filter: SubscribeEventFilter,
listener: @escaping Web3SubscriptionListener<R>) throws -> Subscription {
fatalError("Not implemented")
}

public func connectSocket() {
writeTimer?.invalidate()
socket.connect()