5
5
//
6
6
7
7
import Foundation
8
+ import BigInt
8
9
9
10
public typealias Hash = String // 32 bytes hash of block
10
11
public typealias Receipt = Hash
@@ -27,7 +28,7 @@ public enum EthJSONRPC {
27
28
case getTransactionReceipt( Receipt )
28
29
case getLogs( EventFilterParameters )
29
30
case personalSign( Address , Data )
30
- case call( EthereumTransaction )
31
+ case call( TransactionParameters )
31
32
case getTransactionCount( Address , BlockNumber )
32
33
case getBalance( Address , BlockNumber )
33
34
@@ -118,7 +119,7 @@ extension EthJSONRPC {
118
119
}
119
120
120
121
extension EthJSONRPC {
121
- var responseType : JRONRPCResponstType . Type {
122
+ var responseType : EthResponseType . Type {
122
123
switch self {
123
124
default : return String . self
124
125
}
@@ -134,44 +135,90 @@ extension EthJSONRPC {
134
135
}
135
136
136
137
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] ( )
164
181
}
165
182
}
166
183
}
167
184
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
+
168
213
private extension EthJSONRPC {
169
214
static func setupRequest( for call: EthJSONRPC ) -> URLRequest {
215
+ // FIXME: Make custom url
170
216
let url = URL ( string: " https://mainnet.infura.io/v3/4406c3acf862426c83991f1752c46dd8 " ) !
171
217
var urlRequest = URLRequest ( url: url, cachePolicy: . reloadIgnoringCacheData)
172
- urlRequest. httpMethod = call. method. rawValue
173
218
urlRequest. setValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
174
219
urlRequest. setValue ( " application/json " , forHTTPHeaderField: " Accept " )
220
+ urlRequest. httpMethod = call. method. rawValue
221
+ urlRequest. httpBody = call. encodedBody
175
222
return urlRequest
176
223
}
177
224
}
@@ -181,10 +228,26 @@ public enum REST: String {
181
228
case GET
182
229
}
183
230
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 { }
185
247
186
- extension String : JRONRPCResponstType { }
248
+ extension BigUInt : EthResponseType { }
187
249
250
+ extension String : EthResponseType { }
188
251
189
252
public enum JSONRPCmethod : String , Encodable {
190
253
// 0 parameter in call
0 commit comments