Skip to content

Commit f1ab07d

Browse files
Method sendRequest<U>(with call: EthJSONRPC) async throws -> EthResponse<U> are now generic and error prone.
It must be used as `getValue<T>() -> T` like: `let someResponse: EthResponse<String> = sendRequest(with: .blockNumber)`
1 parent e4fcc1d commit f1ab07d

File tree

1 file changed

+95
-32
lines changed

1 file changed

+95
-32
lines changed

Sources/web3swift/Web3/Web3+Methods.swift

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//
66

77
import Foundation
8+
import BigInt
89

910
public typealias Hash = String // 32 bytes hash of block
1011
public typealias Receipt = Hash
@@ -27,7 +28,7 @@ public enum EthJSONRPC {
2728
case getTransactionReceipt(Receipt)
2829
case getLogs(EventFilterParameters)
2930
case personalSign(Address, Data)
30-
case call(EthereumTransaction)
31+
case call(TransactionParameters)
3132
case getTransactionCount(Address, BlockNumber)
3233
case getBalance(Address, BlockNumber)
3334

@@ -118,7 +119,7 @@ extension EthJSONRPC {
118119
}
119120

120121
extension EthJSONRPC {
121-
var responseType: JRONRPCResponstType.Type {
122+
var responseType: EthResponseType.Type {
122123
switch self {
123124
default: return String.self
124125
}
@@ -134,44 +135,90 @@ extension EthJSONRPC {
134135
}
135136

136137
extension EthJSONRPC {
137-
static func sendRequest(with call: EthJSONRPC) async throws -> some JRONRPCResponstType {
138-
switch call {
139-
case .gasPrice: return "eth_gasPrice"
140-
case .blockNumber: return "eth_blockNumber"
141-
case .getNetwork: return "net_version"
142-
case .getAccounts: return "eth_accounts"
143-
case .sendRawTransaction: return "eth_sendRawTransaction"
144-
case .sendTransaction: return "eth_sendTransaction"
145-
case .getTransactionByHash: return "eth_getTransactionByHash"
146-
case .getTransactionReceipt: return "eth_getTransactionReceipt"
147-
case .personalSign: return "eth_sign"
148-
case .getLogs: return "eth_getLogs"
149-
case .call: return "eth_call"
150-
case .estimateGas: return "eth_estimateGas"
151-
case .getTransactionCount: return "eth_getTransactionCount"
152-
case .getBalance: return "eth_getBalance"
153-
case .getStorageAt: return "eth_getStorageAt"
154-
case .getCode: return "eth_getCode"
155-
case .getBlockByHash: return "eth_getBlockByHash"
156-
case .getBlockByNumber: return "eth_getBlockByNumber"
157-
case .feeHistory: return "eth_feeHistory"
158-
159-
case .unlockAccount: return "personal_unlockAccount"
160-
case .createAccount: return "personal_createAccount"
161-
case .getTxPoolStatus: return "txpool_status"
162-
case .getTxPoolContent: return "txpool_content"
163-
case .getTxPoolInspect: return "txpool_inspect"
138+
var parameters: [RPCParameter] {
139+
switch self {
140+
case .gasPrice, .blockNumber, .getNetwork, .getAccounts, .estimateGas:
141+
return [RPCParameter]()
142+
case let .sendRawTransaction(hash):
143+
return [RPCParameter.string(hash)]
144+
case .sendTransaction(let transactionParameters):
145+
return [RPCParameter.transaction(transactionParameters)]
146+
case .getTransactionByHash(let hash):
147+
return [RPCParameter.string(hash)]
148+
case .getTransactionReceipt(let receipt):
149+
return [RPCParameter.string(receipt)]
150+
case .getLogs(let eventFilterParameters):
151+
return [RPCParameter.eventFilter(eventFilterParameters)]
152+
case .personalSign(let address, let data):
153+
// FIXME: Add second parameter
154+
return [RPCParameter.string(address)]
155+
case .call(let transactionParameters):
156+
return [RPCParameter.transaction(transactionParameters)]
157+
case .getTransactionCount(let address, let blockNumber):
158+
return [RPCParameter.string(address), RPCParameter.string(blockNumber.stringValue)]
159+
case .getBalance(let address, let blockNumber):
160+
return [RPCParameter.string(address), RPCParameter.string(blockNumber.stringValue)]
161+
case .getStorageAt(let address, let hash, let blockNumber):
162+
return [RPCParameter.string(address), RPCParameter.string(hash), RPCParameter.string(blockNumber.stringValue)]
163+
case .getCode(let address, let blockNumber):
164+
return [RPCParameter.string(address), RPCParameter.string(blockNumber.stringValue)]
165+
case .getBlockByHash(let hash, let bool):
166+
return [RPCParameter.string(hash), RPCParameter.bool(bool)]
167+
case .getBlockByNumber(let hash, let bool):
168+
return [RPCParameter.string(hash), RPCParameter.bool(bool)]
169+
case .feeHistory(let uInt, let blockNumber, let array):
170+
return [RPCParameter.uint(uInt), RPCParameter.string(blockNumber.stringValue), RPCParameter.doubleArray(array)]
171+
case .createAccount(let string):
172+
return [RPCParameter]()
173+
case .unlockAccount(let address, let string, let optional):
174+
return [RPCParameter]()
175+
case .getTxPoolStatus:
176+
return [RPCParameter]()
177+
case .getTxPoolContent:
178+
return [RPCParameter]()
179+
case .getTxPoolInspect:
180+
return [RPCParameter]()
164181
}
165182
}
166183
}
167184

185+
extension EthJSONRPC {
186+
var encodedBody: Data {
187+
let request = EthRequestBody(method: self.call, parameters: self.parameters)
188+
// this is safe to force try this here
189+
// Because request must failed to compile would not conformable with `Encodable` protocol
190+
return try! JSONEncoder().encode(request)
191+
}
192+
}
193+
194+
extension EthJSONRPC {
195+
static func sendRequest<U>(with call: EthJSONRPC) async throws -> EthResponse<U> {
196+
let request = setupRequest(for: call)
197+
let (data, response) = try await URLSession.shared.data(for: request)
198+
199+
// FIXME: Add appropriate error thrown
200+
guard let httpResponse = response as? HTTPURLResponse,
201+
200 ..< 400 ~= httpResponse.statusCode else { throw Web3Error.connectionError }
202+
203+
// FIXME: Add appropriate error thrown
204+
guard U.self == call.responseType else { throw Web3Error.unknownError }
205+
206+
// FIXME: What to do when `result` is just an hexString?
207+
// All works here must be end at leving it string.
208+
// Another way is to made type HexString with its own init and decode method
209+
return try JSONDecoder().decode(EthResponse<U>.self, from: data)
210+
}
211+
}
212+
168213
private extension EthJSONRPC {
169214
static func setupRequest(for call: EthJSONRPC) -> URLRequest {
215+
// FIXME: Make custom url
170216
let url = URL(string: "https://mainnet.infura.io/v3/4406c3acf862426c83991f1752c46dd8")!
171217
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData)
172-
urlRequest.httpMethod = call.method.rawValue
173218
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
174219
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
220+
urlRequest.httpMethod = call.method.rawValue
221+
urlRequest.httpBody = call.encodedBody
175222
return urlRequest
176223
}
177224
}
@@ -181,10 +228,26 @@ public enum REST: String {
181228
case GET
182229
}
183230

184-
protocol JRONRPCResponstType: Decodable { }
231+
public struct EthRequestBody: Encodable {
232+
var jsonrpc = "2.0"
233+
var id = Counter.increment()
234+
235+
var method: String
236+
var parameters: [RPCParameter]
237+
}
238+
239+
/// JSON RPC response structure for serialization and deserialization purposes.
240+
public struct EthResponse<T>: Decodable where T: EthResponseType {
241+
public var id: Int
242+
public var jsonrpc = "2.0"
243+
public var result: T
244+
}
245+
246+
public protocol EthResponseType: Decodable { }
185247

186-
extension String: JRONRPCResponstType { }
248+
extension BigUInt: EthResponseType { }
187249

250+
extension String: EthResponseType { }
188251

189252
public enum JSONRPCmethod: String, Encodable {
190253
// 0 parameter in call

0 commit comments

Comments
 (0)