Skip to content

Commit

Permalink
Merge pull request #86 from yuzushioh/httpclient
Browse files Browse the repository at this point in the history
Remove APIKit and Result
  • Loading branch information
yuzushioh authored May 6, 2018
2 parents d428c24 + 4295206 commit da46234
Show file tree
Hide file tree
Showing 27 changed files with 553 additions and 265 deletions.
1 change: 0 additions & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
github "ishkawa/APIKit"
github "krzyzanowskim/CryptoSwift"
2 changes: 0 additions & 2 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
github "antitypical/Result" "3.2.4"
github "ishkawa/APIKit" "3.2.1"
github "krzyzanowskim/CryptoSwift" "0.9.0"
80 changes: 44 additions & 36 deletions EthereumKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion EthereumKit/Helper/Crypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public final class Crypto {
/// - Throws: EthereumKitError.failedToSign in case private key was invalid
public static func sign(_ hash: Data, privateKey: Data) throws -> Data {
let encrypter = EllipticCurveEncrypterSecp256k1()
guard var signatureInInternalFormat = encrypter.sign(hash: hash, privateKey: privateKey) else { throw EthereumKitError.failedToSign }
guard var signatureInInternalFormat = encrypter.sign(hash: hash, privateKey: privateKey) else { throw EthereumKitError.cryptoError(.failedToSign) }
return encrypter.export(signature: &signatureInInternalFormat)
}

Expand Down
27 changes: 23 additions & 4 deletions EthereumKit/Helper/EthereumKitError.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
public enum EthereumKitError: Error {
case apiClientError(APIClientError)
case failedToSign
case failedToEncode(Any)
case keyDerivateionFailed

public enum RequestError: Error {
case invalidURL
case invalidParameters(Any)
}

public enum ResponseError: Error {
case jsonrpcError(JSONRPCError)
case connectionError(Error)
case unexpected(Error)
case unacceptableStatusCode(Int)
case noContentProvided
}

public enum CryptoError: Error {
case failedToSign
case failedToEncode(Any)
case keyDerivateionFailed
}

case requestError(RequestError)
case responseError(ResponseError)
case cryptoError(CryptoError)
}
5 changes: 4 additions & 1 deletion EthereumKit/Helper/RLP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ public struct RLP {
encoded = nil
}

guard let data = encoded else { throw EthereumKitError.failedToEncode(element) }
guard let data = encoded else {
throw EthereumKitError.cryptoError(.failedToEncode(element))
}

return data
}

Expand Down
4 changes: 4 additions & 0 deletions EthereumKit/Helper/Result.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public enum Result<Object> {
case success(Object)
case failure(EthereumKitError)
}
2 changes: 1 addition & 1 deletion EthereumKit/Key/HDPrivateKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public struct HDPrivateKey {
)

guard let derivedKey = keyDeriver.derived(at: index, hardened: hardens) else {
throw EthereumKitError.keyDerivateionFailed
throw EthereumKitError.cryptoError(.keyDerivateionFailed)
}

return HDPrivateKey(
Expand Down
24 changes: 0 additions & 24 deletions EthereumKit/Networking/Client/APIClient.swift

This file was deleted.

20 changes: 0 additions & 20 deletions EthereumKit/Networking/Client/APIClientError.swift

This file was deleted.

14 changes: 0 additions & 14 deletions EthereumKit/Networking/Client/EtherscanClient.swift

This file was deleted.

18 changes: 0 additions & 18 deletions EthereumKit/Networking/Client/JSONRPCClient.swift

This file was deleted.

119 changes: 97 additions & 22 deletions EthereumKit/Networking/Geth.swift
Original file line number Diff line number Diff line change
@@ -1,48 +1,123 @@
import Result

/// Geth is responsible for interacting with Ethereum blockchain
public final class Geth {

private let etherClient: JSONRPCClient
private let etherscanClient: EtherscanClient
private let configuration: Configuration
private let httpClient: HTTPClient

/// init initialize Geth instance
///
/// - Parameter configuration: configuration to use in http client
public init(configuration: Configuration) {
etherClient = JSONRPCClient(configuration: configuration)
etherscanClient = EtherscanClient(configuration: configuration)
self.configuration = configuration
self.httpClient = HTTPClient(configuration: configuration)
}

// MARK: - JSONRPC APIs

public func getGasPrice(handler: @escaping (Result<Wei, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.GetGasPrice(), handler: handler)
/// getGasPrice returns currenct gas price
///
/// - Parameters:
/// - completionHandler:
public func getGasPrice(completionHandler: @escaping (Result<Wei>) -> Void) {
httpClient.send(JSONRPC.GetGasPrice(), completionHandler: completionHandler)
}

public func getBalance(of address: String, blockParameter: BlockParameter = .latest, handler: @escaping (Result<Balance, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.GetBalance(address: address, blockParameter: blockParameter), handler: handler)
/// getBalance returns currenct balance of specified address.
///
/// - Parameters:
/// - address: address you want to get the balance of
/// - blockParameter:
/// - completionHandler:
public func getBalance(of address: String, blockParameter: BlockParameter = .latest, completionHandler: @escaping (Result<Balance>) -> Void) {
httpClient.send(JSONRPC.GetBalance(address: address, blockParameter: blockParameter), completionHandler: completionHandler)
}

public func getTransactionCount(of address: String, blockParameter: BlockParameter = .latest, handler: @escaping (Result<Int, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.GetTransactionCount(address: address, blockParameter: blockParameter), handler: handler)
/// getTransactionCount returns the current nonce of specified address
///
/// - Parameters:
/// - address: address to check
/// - blockParameter:
/// - completionHandler:
public func getTransactionCount(of address: String, blockParameter: BlockParameter = .latest, completionHandler: @escaping (Result<Int>) -> Void) {
httpClient.send(JSONRPC.GetTransactionCount(address: address, blockParameter: blockParameter), completionHandler: completionHandler)
}

public func sendRawTransaction(rawTransaction: String, handler: @escaping (Result<SentTransaction, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.SendRawTransaction(rawTransaction: rawTransaction), handler: handler)
/// sendRawTransaction sends the raw transaction string
///
/// - Parameters:
/// - rawTransaction: raw transaction encoded in rlp hex format
/// - completionHandler:
public func sendRawTransaction(rawTransaction: String, completionHandler: @escaping (Result<SentTransaction>) -> Void) {
httpClient.send(JSONRPC.SendRawTransaction(rawTransaction: rawTransaction), completionHandler: completionHandler)
}

public func call(from: String? = nil, to: String, gasLimit: Gas.GasLimit? = nil, gasPrice: Gas.GasPrice? = nil, value: Int? = nil, data: String? = nil, blockParameter: BlockParameter = .latest, handler: @escaping (Result<String, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.Call(from: from, to: to, gas: gasLimit.map { $0.value }, gasPrice: gasPrice.map { $0.value }, value: value, data: data, blockParameter: blockParameter), handler: handler)
/// getBlockNumber returns the latest block number
///
/// - Parameter completionHandler:
public func getBlockNumber(completionHandler: @escaping (Result<Int>) -> Void) {
httpClient.send(JSONRPC.GetBlockNumber(), completionHandler: completionHandler)
}

public func getEstimateGas(from: String? = nil, to: String, gasLimit: Gas.GasLimit? = nil, gasPrice: Gas.GasPrice? = nil, value: Int? = nil, data: String? = nil, handler: @escaping (Result<Wei, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.GetEstimatGas(from: from, to: to, gas: gasLimit.map { $0.value }, gasPrice: gasPrice.map { $0.value }, value: value, data: data), handler: handler)
/// call sends transaction to a contract method
///
/// - Parameters:
/// - from: which address to send from
/// - to: which address to send to
/// - gasLimit: gas limit
/// - gasPrice: gas price
/// - value: value in wei
/// - data: data to include in tx
/// - blockParameter:
/// - completionHandler:
public func call(from: String? = nil, to: String, gasLimit: Gas.GasLimit? = nil, gasPrice: Gas.GasPrice? = nil, value: Int? = nil, data: String? = nil, blockParameter: BlockParameter = .latest, completionHandler: @escaping (Result<String>) -> Void) {
let request = JSONRPC.Call(
from: from,
to: to,
gas: gasLimit.map { $0.value },
gasPrice: gasPrice.map { $0.value },
value: value,
data: data,
blockParameter: blockParameter
)

httpClient.send(request, completionHandler: completionHandler)
}

public func getBlockNumber(handler: @escaping (Result<Int, EthereumKitError>) -> Void) {
etherClient.send(JSONRPC.GetBlockNumber(), handler: handler)
/// getEstimateGas returns estimated gas for the tx
///
/// - Parameters:
/// - from: which address to send from
/// - to: which address to send to
/// - gasLimit: gas limit
/// - gasPrice: gas price
/// - value: value in wei
/// - data: data to include in tx
/// - completionHandler:
public func getEstimateGas(from: String? = nil, to: String, gasLimit: Gas.GasLimit? = nil, gasPrice: Gas.GasPrice? = nil, value: Int? = nil, data: String? = nil, completionHandler: @escaping (Result<Wei>) -> Void) {
let request = JSONRPC.GetEstimatGas(
from: from,
to: to,
gas: gasLimit.map { $0.value },
gasPrice: gasPrice.map { $0.value },
value: value,
data: data
)

httpClient.send(request, completionHandler: completionHandler)
}

// MARK: - Etherscan APIs

public func getTransactions(address: String, sort: Etherscan.GetTransactions.Sort = .des, startBlock: Int64 = 0, endBlock: Int64 = 99999999, handler: @escaping (Result<Transactions, EthereumKitError>) -> Void) {
etherscanClient.send(Etherscan.GetTransactions(address: address, sort: sort, startBlock: startBlock, endBlock: endBlock), handler: handler)
/// getTransactions returns the list of transaction for the specified address.
///
/// - Parameters:
/// - address: address to get transactions from
/// - completionHandler:
public func getTransactions(address: String, completionHandler: @escaping (Result<Transactions>) -> Void) {
let request = Etherscan.GetTransactions(
configuration: .init(baseURL: configuration.etherscanURL, apiKey: configuration.etherscanAPIKey),
address: address
)
httpClient.send(request, completionHandler: completionHandler)
}
}
5 changes: 5 additions & 0 deletions EthereumKit/Networking/HTTPClient/Cancellable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public protocol Cancellable: class {
func cancel()
}

extension URLSessionTask: Cancellable {}
55 changes: 55 additions & 0 deletions EthereumKit/Networking/HTTPClient/HTTPClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// HTTPClient is responsible for sending and canceling url requests
public class HTTPClient: HTTPClientType {

/// batchFactory is used for creating batch request for JSONRPC request
private let batchFactory = BatchFactory(version: "2.0")

/// configuration is used for configuring request information
public let configuration: Configuration


/// init initialize HTTPClient
///
/// - Parameter configuration: configuration to use to configure requests
public init(configuration: Configuration) {
self.configuration = configuration
}

/// send method sends specified jsonrpc request and returns a session task.
///
/// - Parameters:
/// - request: request to send. needs to be conformed to JSONRPCRequest protocol
/// - completionHandler:
/// - Returns: session task used to send request
@discardableResult
public func send<Request: JSONRPCRequest>(_ request: Request, completionHandler: @escaping (Result<Request.Response>) -> Void) -> Cancellable? {
let httpRequest = HTTPJSONRPCRequest(batch: batchFactory.create(request), endpoint: URL(string: configuration.nodeEndpoint)!)
return send(httpRequest, completionHandler: completionHandler)
}

/// send method sends specified json request and returns a session task.
///
/// - Parameters:
/// - request: request to send, needs to be conformed to RequestType protocol
/// - completionHandler:
/// - Returns: session task used to send request
@discardableResult
public func send<Request: RequestType>(_ request: Request, completionHandler: @escaping (Result<Request.Response>) -> Void) -> Cancellable? {
switch request.build() {
case .success(let urlRequest):
let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
let result = request.buildResponse(from: data, response: response, error: error)
DispatchQueue.main.async {
completionHandler(result)
}
}

task.resume()
return task

case .failure(let error):
completionHandler(.failure(error))
return nil
}
}
}
11 changes: 11 additions & 0 deletions EthereumKit/Networking/HTTPClient/HTTPClientType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public protocol HTTPClientType {
@discardableResult
func send<Request: RequestType>(
_ request: Request,
completionHandler: @escaping (Result<Request.Response>) -> Void) -> Cancellable?

@discardableResult
func send<Request: JSONRPCRequest>(
_ request: Request,
completionHandler: @escaping (Result<Request.Response>) -> Void) -> Cancellable?
}
13 changes: 13 additions & 0 deletions EthereumKit/Networking/HTTPClient/Method.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public enum Method: String {
case get
case post

public var prefersQueryParameters: Bool {
switch self {
case .get:
return true
case .post:
return false
}
}
}
Loading

0 comments on commit da46234

Please sign in to comment.