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

Make use of Result on completion #221

Merged
merged 1 commit into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions web3sTests/Client/EthereumClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ class EthereumClientTests: XCTestCase {
_ = try await client?.eth_getBalance(address: EthereumAddress("0xnig42niog2"), block: .Latest)
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumClientError, .unexpectedReturnValue)
XCTAssertEqual(error as? EthereumClientError, .executionError(
.init(code: -32602, message: "invalid argument 0: hex string has length 10, want 40 for common.Address", data: nil)
))
}
}

Expand Down Expand Up @@ -227,7 +229,9 @@ class EthereumClientTests: XCTestCase {
let _ = try await client?.eth_getTransaction(byHash: "0x01234")
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumClientError, .unexpectedReturnValue)
XCTAssertEqual(error as? EthereumClientError, .executionError(
.init(code: -32602, message: "invalid argument 0: json: cannot unmarshal hex string of odd length into Go value of type common.Hash", data: nil)
))
}
}

Expand Down
114 changes: 77 additions & 37 deletions web3swift/src/Client/EthereumClient+Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ extension EthereumClient {
_ transaction: EthereumTransaction,
resolution: CallResolution = .noOffchain(failOnExecutionError: true),
block: EthereumBlock = .Latest,
completion: @escaping ((EthereumClientError?, String?) -> Void)
completionHandler: @escaping (Result<String, EthereumClientError>) -> Void
) {
guard let transactionData = transaction.data else {
return completion(EthereumClientError.noInputData, nil)
completionHandler(.failure(.noInputData))
return
}

struct CallParams: Encodable {
Expand Down Expand Up @@ -67,46 +68,51 @@ extension EthereumClient {
block: block.stringValue
)

EthereumRPC.execute(
session: session,
url: url,
method: "eth_call",
params: params,
receive: String.self
) { (error, response) in
if let resDataString = response as? String {
completion(nil, resDataString)
} else if case let .executionError(result) = error as? JSONRPCError {
switch resolution {
case .noOffchain:
completion(.executionError(result.error), nil)
case .offchainAllowed(let redirects):
if let lookup = result.offchainLookup, lookup.address == transaction.to {
self.offchainRead(
lookup: lookup,
maxReads: redirects
).sink(receiveCompletion: { offchainCompletion in
if case .failure = offchainCompletion {
completion(.noResultFound, nil)
EthereumRPC.execute(session: session,
url: url,
method: "eth_call",
params: params,
receive: String.self) { result in
switch result {
case .success(let data):
if let resDataString = data as? String {
completionHandler(.success(resDataString))
} else {
completionHandler(.failure(.unexpectedReturnValue))
}
case .failure(let error):
if case let .executionError(result) = error as? JSONRPCError {
switch resolution {
case .noOffchain:
completionHandler(.failure(.executionError(result.error)))
case .offchainAllowed(let redirects):
if let lookup = result.offchainLookup, lookup.address == transaction.to {
self.offchainRead(
lookup: lookup,
maxReads: redirects
).sink(receiveCompletion: { offchainCompletion in
if case .failure = offchainCompletion {
completionHandler(.failure(.noResultFound))
}
}, receiveValue: { data in
self.eth_call(
.init(
to: lookup.address,
data: lookup.encodeCall(withResponse: data)
),
resolution: .noOffchain(failOnExecutionError: true),
block: block, completionHandler: completionHandler
)
}
}, receiveValue: { data in
self.eth_call(
.init(
to: lookup.address,
data: lookup.encodeCall(withResponse: data)
),
resolution: .noOffchain(failOnExecutionError: true),
block: block, completion: completion
)
.store(in: &cancellables)
} else {
completionHandler(.failure(.executionError(result.error)))
}
)
.store(in: &cancellables)
} else {
completion(.executionError(result.error), nil)
}
} else {
completionHandler(.failure(.unexpectedReturnValue))
}
} else {
completion(.unexpectedReturnValue, nil)
}
}
}
Expand Down Expand Up @@ -211,6 +217,40 @@ extension EthereumClient {
}
}

// MARK: - Async/Await
extension EthereumClient {
public func eth_call(_ transaction: EthereumTransaction,
resolution: CallResolution = .noOffchain(failOnExecutionError: true),
block: EthereumBlock = .Latest) async throws -> String {
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String, Error>) in
eth_call(
transaction,
resolution: resolution,
block: block,
completionHandler: continuation.resume)
}
}
}

// MARK: - Deprecated
extension EthereumClient {
@available(*, deprecated, renamed: "eth_call(_:resolution:block:completionHandler:)")
public func eth_call( _ transaction: EthereumTransaction,
resolution: CallResolution = .noOffchain(failOnExecutionError: true),
block: EthereumBlock = .Latest,
completion: @escaping ((EthereumClientError?, String?) -> Void)
) {
eth_call(transaction, resolution: resolution, block: block) { result in
switch result {
case .success(let data):
completion(nil, data)
case .failure(let error):
completion(error, nil)
}
}
}
}

fileprivate struct OffchainReadJSONBody: Encodable {
let sender: EthereumAddress
let data: String
Expand Down
Loading