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

New WebsocketProvider and Subscriptions implementation #446

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
33 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
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
4 changes: 2 additions & 2 deletions Sources/web3swift/HookedFunctions/Web3+BrowserFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension web3.BrowserFunctions {

public func sign(_ personalMessage: Data, account: String, password: String = "web3swift") -> String? {
do {
guard let keystoreManager = self.web3.provider.attachedKeystoreManager else {return nil}
guard let keystoreManager = self.web3.keystoreManager else {return nil}
guard let from = EthereumAddress(account, ignoreChecksum: true) else {return nil}
guard let signature = try Web3Signer.signPersonalMessage(personalMessage, keystore: keystoreManager, account: from, password: password) else {return nil}
return signature.toHexString().addHexPrefix()
Expand Down Expand Up @@ -164,7 +164,7 @@ extension web3.BrowserFunctions {
do {
var transaction = trans
guard let from = transactionOptions.from else {return nil}
guard let keystoreManager = self.web3.provider.attachedKeystoreManager else {return nil}
guard let keystoreManager = self.web3.keystoreManager else {return nil}
guard let gasPricePolicy = transactionOptions.gasPrice else {return nil}
guard let gasLimitPolicy = transactionOptions.gasLimit else {return nil}
guard let noncePolicy = transactionOptions.nonce else {return nil}
Expand Down
6 changes: 3 additions & 3 deletions Sources/web3swift/HookedFunctions/Web3+Wallet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import BigInt
extension web3.Web3Wallet {

public func getAccounts() throws -> [EthereumAddress] {
guard let keystoreManager = self.web3.provider.attachedKeystoreManager else {
guard let keystoreManager = self.web3.keystoreManager else {
throw Web3Error.walletError
}
guard let ethAddresses = keystoreManager.addresses else {
Expand All @@ -30,7 +30,7 @@ extension web3.Web3Wallet {

public func signTX(transaction:inout EthereumTransaction, account: EthereumAddress, password: String = "web3swift") throws -> Bool {
do {
guard let keystoreManager = self.web3.provider.attachedKeystoreManager else {
guard let keystoreManager = self.web3.keystoreManager else {
throw Web3Error.walletError
}
try Web3Signer.signTX(transaction: &transaction, keystore: keystoreManager, account: account, password: password)
Expand All @@ -53,7 +53,7 @@ extension web3.Web3Wallet {

public func signPersonalMessage(_ personalMessage: Data, account: EthereumAddress, password: String = "web3swift") throws -> Data {
do {
guard let keystoreManager = self.web3.provider.attachedKeystoreManager else
guard let keystoreManager = self.web3.keystoreManager else
{
throw Web3Error.walletError
}
Expand Down
12 changes: 11 additions & 1 deletion Sources/web3swift/Promises/Promise+HttpProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,21 @@ extension Web3HttpProvider {
if request.method == nil {
return Promise(error: Web3Error.nodeError(desc: "RPC method is nill"))
}

guard ![.subscribe, .unsubscribe].contains(request.method!) else {
return Promise(error: Web3Error.inputError(desc: "Unsupported method \(request.method!)"))
}
return Web3HttpProvider.post(request, providerURL: self.url, queue: queue, session: self.session)
}

public func sendAsync(_ requests: JSONRPCrequestBatch, queue: DispatchQueue = .main) -> Promise<JSONRPCresponseBatch> {
guard requests.requests.allSatisfy({
guard let method = $0.method else {
return true
}
return ![.subscribe, .unsubscribe].contains(method)
}) else {
return Promise(error: Web3Error.inputError(desc: "Unsupported method in requests"))
}
return Web3HttpProvider.post(requests, providerURL: self.url, queue: queue, session: self.session)
}
}
Expand Down
72 changes: 72 additions & 0 deletions Sources/web3swift/Promises/Promise+Web3+Eth+Filters.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// Promise+Web3+Eth+Filters.swift
//
//
// Created by Ostap Danylovych on 06.01.2022.
//

import Foundation
import PromiseKit
import BigInt

public enum BlockNumber {
case pending
case latest
case earliest
case exact(BigUInt)

public var stringValue: String {
switch self {
case .pending:
return "pending"
case .latest:
return "latest"
case .earliest:
return "earliest"
case .exact(let number):
return String(number, radix: 16).addHexPrefix()
}
}
}

extension web3.Eth {
private func _dispatchRequest<T>(_ request: JSONRPCrequest) -> Promise<T> {
web3.dispatch(request).map(on: web3.requestDispatcher.queue) { response in
guard let value: T = response.getValue() else {
if response.error != nil {
throw Web3Error.nodeError(desc: response.error!.message)
}
throw Web3Error.nodeError(desc: "Invalid value from Ethereum node")
}
return value
}
}

public func newFilterPromise(addresses: [EthereumAddress],
fromBlock: BlockNumber? = .latest,
toBlock: BlockNumber? = .latest,
topics: [String]) -> Promise<String> {
let addresses = addresses.map { $0.address.lowercased() }
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.newFilter, parameters: [addresses, fromBlock?.stringValue, toBlock?.stringValue, topics]))
}

public func newBlockFilterPromise() -> Promise<String> {
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.newBlockFilter, parameters: []))
}

public func newPendingTransactionFilterPromise() -> Promise<String> {
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.newPendingTransactionFilter, parameters: []))
}

public func uninstallFilterPromise(filterID: String) -> Promise<Bool> {
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.uninstallFilter, parameters: [filterID]))
}

public func getFilterChangesPromise(filterID: String) -> Promise<FilterChanges> {
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.getFilterChanges, parameters: [filterID]))
}

public func getFilterLogsPromise(filterID: String) -> Promise<FilterChanges> {
return _dispatchRequest(JSONRPCRequestFabric.prepareRequest(.getFilterLogs, parameters: [filterID]))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import PromiseKit
extension web3.Eth {
public func getAccountsPromise() -> Promise<[EthereumAddress]> {
let queue = web3.requestDispatcher.queue
if (self.web3.provider.attachedKeystoreManager != nil) {
if (self.web3.keystoreManager != nil) {
let promise = Promise<[EthereumAddress]>.pending()
queue.async {
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extension web3.Eth {
assembledTransaction = forAssemblyPipeline.0
mergedOptions = forAssemblyPipeline.1

if self.web3.provider.attachedKeystoreManager == nil {
if self.web3.keystoreManager == nil {
guard let request = EthereumTransaction.createRequest(method: .sendTransaction, transaction: assembledTransaction, transactionOptions: mergedOptions) else
{
throw Web3Error.processingError(desc: "Failed to create a request to send transaction")
Expand All @@ -63,7 +63,7 @@ extension web3.Eth {
throw Web3Error.inputError(desc: "No 'from' field provided")
}
do {
try Web3Signer.signTX(transaction: &assembledTransaction, keystore: self.web3.provider.attachedKeystoreManager!, account: from, password: password)
try Web3Signer.signTX(transaction: &assembledTransaction, keystore: self.web3.keystoreManager!, account: from, password: password)
} catch {
throw Web3Error.inputError(desc: "Failed to locally sign a transaction")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension web3.Personal {
public func createAccountPromise(password:String = "web3swift") -> Promise<EthereumAddress> {
let queue = web3.requestDispatcher.queue
do {
if self.web3.provider.attachedKeystoreManager == nil {
if self.web3.keystoreManager == nil {
let request = JSONRPCRequestFabric.prepareRequest(.createAccount, parameters: [password])
return self.web3.dispatch(request).map(on: queue) {response in
guard let value: EthereumAddress = response.getValue() else {
Expand Down
4 changes: 2 additions & 2 deletions Sources/web3swift/Promises/Promise+Web3+Personal+Sign.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extension web3.Personal {
public func signPersonalMessagePromise(message: Data, from: EthereumAddress, password:String = "web3swift") -> Promise<Data> {
let queue = web3.requestDispatcher.queue
do {
if self.web3.provider.attachedKeystoreManager == nil {
if self.web3.keystoreManager == nil {
let hexData = message.toHexString().addHexPrefix()
let request = JSONRPCRequestFabric.prepareRequest(.personalSign, parameters: [from.address.lowercased(), hexData])
return self.web3.dispatch(request).map(on: queue) {response in
Expand All @@ -27,7 +27,7 @@ extension web3.Personal {
return value
}
}
guard let signature = try Web3Signer.signPersonalMessage(message, keystore: self.web3.provider.attachedKeystoreManager!, account: from, password: password) else { throw Web3Error.inputError(desc: "Failed to locally sign a message") }
guard let signature = try Web3Signer.signPersonalMessage(message, keystore: self.web3.keystoreManager!, account: from, password: password) else { throw Web3Error.inputError(desc: "Failed to locally sign a message") }
let returnPromise = Promise<Data>.pending()
queue.async {
returnPromise.resolver.fulfill(signature)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension web3.Personal {
public func unlockAccountPromise(account: String, password:String = "web3swift", seconds: UInt64 = 300) -> Promise<Bool> {
let queue = web3.requestDispatcher.queue
do {
if self.web3.provider.attachedKeystoreManager == nil {
if self.web3.keystoreManager == nil {
let request = JSONRPCRequestFabric.prepareRequest(.unlockAccount, parameters: [account.lowercased(), password, seconds])
return self.web3.dispatch(request).map(on: queue) {response in
guard let value: Bool = response.getValue() else {
Expand Down
2 changes: 1 addition & 1 deletion Sources/web3swift/Promises/Promise+Web3+TxPool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension web3.TxPool {
let rp = web3.dispatch(request)
let queue = web3.requestDispatcher.queue
return rp.map(on: queue ) { response in
guard let value: TxPoolStatus = response.result as? TxPoolStatus else {
guard let value: TxPoolStatus = response.getValue() else {
if response.error != nil {
throw Web3Error.nodeError(desc: response.error!.message)
}
Expand Down
48 changes: 27 additions & 21 deletions Sources/web3swift/Web3/Web3+Eth+Websocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,39 @@ import BigInt
import PromiseKit
import Starscream

public struct SubscribeOnLogsParams: Encodable {
public let address: [String]?
public let topics: [String]?
}

extension web3.Eth {

public func getWebsocketProvider(forDelegate delegate: Web3SocketDelegate) throws -> InfuraWebsocketProvider {
var infuraWSProvider: InfuraWebsocketProvider
if !(provider is InfuraWebsocketProvider) {
guard let infuraNetwork = provider.network else {
throw Web3Error.processingError(desc: "Wrong network")
}
guard let infuraProvider = InfuraWebsocketProvider(infuraNetwork, delegate: delegate, keystoreManager: provider.attachedKeystoreManager) else {
throw Web3Error.processingError(desc: "Wrong network")
}
infuraWSProvider = infuraProvider
} else {
infuraWSProvider = provider as! InfuraWebsocketProvider
private func _subscribe<R>(filter: SubscribeEventFilter,
listener: @escaping Web3SubscriptionListener<R>) throws -> Subscription {
guard let provider = provider as? Web3SubscriptionProvider else {
throw Web3Error.processingError(desc: "Provider is not subscribable")
}
infuraWSProvider.connectSocket()
return infuraWSProvider
return provider.subscribe(filter: filter, queue: web3.requestDispatcher.queue, listener: listener)
}

public func subscribeOnNewHeads(listener: @escaping Web3SubscriptionListener<BlockHeader>) throws -> Subscription {
try _subscribe(filter: .newHeads, listener: listener)
}

public func getLatestPendingTransactions(forDelegate delegate: Web3SocketDelegate) throws {
let provider = try getWebsocketProvider(forDelegate: delegate)
try provider.setFilterAndGetChanges(method: .newPendingTransactionFilter)
public func subscribeOnLogs(addresses: [EthereumAddress]? = nil,
topics: [String]? = nil,
listener: @escaping Web3SubscriptionListener<EventLog>) throws -> Subscription {
let params = SubscribeOnLogsParams(address: addresses?.map { $0.address }, topics: topics)
return try _subscribe(filter: .logs(params: params), listener: listener)
}

public func subscribeOnPendingTransactions(forDelegate delegate: Web3SocketDelegate) throws {
let provider = try getWebsocketProvider(forDelegate: delegate)
try provider.subscribeOnNewPendingTransactions()
public func subscribeOnNewPendingTransactions(listener: @escaping Web3SubscriptionListener<String>) throws -> Subscription {
try _subscribe(filter: .newPendingTransactions, listener: listener)
}

public func subscribeOnSyncing(listener: @escaping Web3SubscriptionListener<SyncingInfo>) throws -> Subscription {
guard provider.network != Networks.Kovan else {
throw Web3Error.inputError(desc: "Can't sync on Kovan")
}
return try _subscribe(filter: .syncing, listener: listener)
}
}
6 changes: 1 addition & 5 deletions Sources/web3swift/Web3/Web3+HttpProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,20 @@ public protocol Web3Provider {
func sendAsync(_ request: JSONRPCrequest, queue: DispatchQueue) -> Promise<JSONRPCresponse>
func sendAsync(_ requests: JSONRPCrequestBatch, queue: DispatchQueue) -> Promise<JSONRPCresponseBatch>
var network: Networks? {get set}
var attachedKeystoreManager: KeystoreManager? {get set}
var url: URL {get}
var session: URLSession {get}
}


/// The default http provider.
public class Web3HttpProvider: Web3Provider {
public var url: URL
public var network: Networks?
public var attachedKeystoreManager: KeystoreManager? = nil
public var session: URLSession = {() -> URLSession in
let config = URLSessionConfiguration.default
let urlSession = URLSession(configuration: config)
return urlSession
}()
public init?(_ httpProviderURL: URL, network net: Networks? = nil, keystoreManager manager: KeystoreManager? = nil) {
public init?(_ httpProviderURL: URL, network net: Networks? = nil) {
do {
guard httpProviderURL.scheme == "http" || httpProviderURL.scheme == "https" else {return nil}
url = httpProviderURL
Expand All @@ -51,7 +48,6 @@ public class Web3HttpProvider: Web3Provider {
} catch {
return nil
}
attachedKeystoreManager = manager
}
}

Loading