Skip to content

Commit

Permalink
Merge pull request #635 from JeneaVranceanu/fix/parsing-json-rpc-errors
Browse files Browse the repository at this point in the history
feat: added support for JSON RPC Error object
  • Loading branch information
yaroslavyaroslav authored Oct 9, 2022
2 parents 4174ad6 + 5776678 commit 7dee353
Showing 1 changed file with 89 additions and 2 deletions.
91 changes: 89 additions & 2 deletions Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,24 @@ extension APIRequest {
}
}

if let error = (try? JSONDecoder().decode(JsonRpcErrorObject.self, from: data))?.error {
guard let parsedErrorCode = error.parsedErrorCode else {
throw Web3Error.nodeError(desc: "\(error.message)\nError code: \(error.code)")
}
let description = "\(parsedErrorCode.errorName). Error code: \(error.code). \(error.message)"
switch parsedErrorCode {
case .parseError, .invalidParams:
throw Web3Error.inputError(desc: description)
case .methodNotFound, .invalidRequest:
throw Web3Error.processingError(desc: description)
case .internalError, .serverError:
throw Web3Error.nodeError(desc: description)
}
}

/// This bit of code is purposed to work with literal types that comes in ``Response`` in hexString type.
/// Currently it's just `Data` and any kind of Integers `(U)Int`, `Big(U)Int`.
if Result.self == Data.self || Result.self == UInt.self || Result.self == Int.self || Result.self == BigInt.self || Result.self == BigUInt.self {
guard let LiteralType = Result.self as? LiteralInitiableFromString.Type else { throw Web3Error.typeError }
if let LiteralType = Result.self as? LiteralInitiableFromString.Type {
guard let responseAsString = try? JSONDecoder().decode(APIResponse<String>.self, from: data) else { throw Web3Error.dataError }
guard let literalValue = LiteralType.init(from: responseAsString.result) else { throw Web3Error.dataError }
/// `literalValue` conforms `LiteralInitiableFromString`, that conforming to an `APIResponseType` type, so it's never fails.
Expand All @@ -47,3 +61,76 @@ extension APIRequest {
return try JSONDecoder().decode(APIResponse<Result>.self, from: data)
}
}

/// JSON RPC Error object. See official specification https://www.jsonrpc.org/specification#error_object
fileprivate struct JsonRpcErrorObject: Decodable {
public let error: RpcError?

class RpcError: Decodable {
let message: String
let code: Int
var parsedErrorCode: JsonRpcErrorCode? {
JsonRpcErrorCode.from(code)
}
}
}

/// For error codes specification see chapter `5.1 Error object`
/// https://www.jsonrpc.org/specification#error_object
fileprivate enum JsonRpcErrorCode {
/// -32700
/// Invalid JSON was received by the server. An error occurred on the server while parsing the JSON
case parseError
/// -32600
/// The JSON sent is not a valid Request object.
case invalidRequest
/// -32601
/// The method does not exist / is not available.
case methodNotFound
/// -32602
/// Invalid method parameter(s).
case invalidParams
/// -32603
/// Internal JSON-RPC error.
case internalError
/// Values in range of -32000 to -32099
/// Reserved for implementation-defined server-errors.
case serverError(Int)

var errorName: String {
switch self {
case .parseError:
return "Parsing error"
case .invalidRequest:
return "Invalid request"
case .methodNotFound:
return "Method not found"
case .invalidParams:
return "Invalid parameters"
case .internalError:
return "Internal error"
case .serverError(_):
return "Server error"
}
}

static func from(_ code: Int) -> JsonRpcErrorCode? {
switch code {
case -32700:
return .parseError
case -32600:
return .invalidRequest
case -32601:
return .methodNotFound
case -32602:
return .invalidParams
case -32603:
return .internalError
default:
if (-32000)...(-32099) ~= code {
return .serverError(code)
}
return nil
}
}
}

0 comments on commit 7dee353

Please sign in to comment.