From 5776678139874ca46f0bd9df71506f173fd93c53 Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu Date: Sun, 9 Oct 2022 10:34:26 +0100 Subject: [PATCH] feat: added support for JSON RPC Error object --- .../Request/APIRequest+Methods.swift | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift b/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift index 1230ec771..b01e51f6c 100644 --- a/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift +++ b/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift @@ -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.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. @@ -47,3 +61,76 @@ extension APIRequest { return try JSONDecoder().decode(APIResponse.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 + } + } +}