Skip to content

Commit b455811

Browse files
chore: 'Web3Error's now have descriptions; (#841)
performed refactoring of APIRequests+Methods.swift, ENSBaseRegistrar, ENSRegistry, ENSResolver, ENSReverseRegistrar, ETHRegistrarController,
1 parent 097adaf commit b455811

38 files changed

+305
-390
lines changed

Sources/Web3Core/Contract/ContractProtocol.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ extension DefaultContractProtocol {
379379

380380
public func decodeEthError(_ data: Data) -> [String: Any]? {
381381
guard data.count >= 4,
382-
let err = errors.first(where: { $0.value.methodEncoding == data[0..<4] })?.value else {
382+
let err = errors.first(where: { $0.value.selectorEncoded == data[0..<4] })?.value else {
383383
return nil
384384
}
385385
return err.decodeEthError(data[4...])
@@ -398,7 +398,7 @@ extension DefaultContractProtocol {
398398
throw Web3Error.inputError(desc: "RPC failed: contract is missing an address.")
399399
}
400400
guard let data = self.method(method, parameters: parameters, extraData: nil) else {
401-
throw Web3Error.dataError
401+
throw Web3Error.dataError(desc: "Failed to encode method \(method) with given parameters: \(String(describing: parameters))")
402402
}
403403
let transaction = CodableTransaction(to: address, data: data)
404404

Sources/Web3Core/EthereumABI/ABIElements.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ extension ABI.Element.Function {
207207
public func encodeParameters(_ parameters: [Any]) -> Data? {
208208
guard parameters.count == inputs.count,
209209
let data = ABIEncoder.encode(types: inputs, values: parameters) else { return nil }
210-
return methodEncoding + data
210+
return selectorEncoded + data
211211
}
212212
}
213213

@@ -300,16 +300,18 @@ extension ABI.Element.EthError {
300300
/// - data: bytes returned by a function call that stripped error signature hash.
301301
/// - Returns: a dictionary containing decoded data mappend to indices and names of returned values or nil if decoding failed.
302302
public func decodeEthError(_ data: Data) -> [String: Any]? {
303-
guard inputs.count * 32 <= data.count,
303+
guard data.count > 0,
304+
data.count % 32 == 0,
305+
inputs.count * 32 <= data.count,
304306
let decoded = ABIDecoder.decode(types: inputs, data: data) else {
305307
return nil
306308
}
307309

308310
var result = [String: Any]()
309-
for (index, out) in inputs.enumerated() {
311+
for (index, input) in inputs.enumerated() {
310312
result["\(index)"] = decoded[index]
311-
if !out.name.isEmpty {
312-
result[out.name] = decoded[index]
313+
if !input.name.isEmpty {
314+
result[input.name] = decoded[index]
313315
}
314316
}
315317
return result
@@ -372,7 +374,7 @@ extension ABI.Element {
372374

373375
extension ABI.Element.Function {
374376
public func decodeInputData(_ rawData: Data) -> [String: Any]? {
375-
return ABIDecoder.decodeInputData(rawData, methodEncoding: methodEncoding, inputs: inputs)
377+
return ABIDecoder.decodeInputData(rawData, methodEncoding: selectorEncoded, inputs: inputs)
376378
}
377379

378380
/// Decodes data returned by a function call.
@@ -520,8 +522,8 @@ extension ABIDecoder {
520522
/// - Returns: decoded dictionary of input arguments mapped to their indices and arguments' names if these are not empty.
521523
/// If decoding of at least one argument fails, `rawData` size is invalid or `methodEncoding` doesn't match - `nil` is returned.
522524
static func decodeInputData(_ rawData: Data,
523-
methodEncoding: Data? = nil,
524-
inputs: [ABI.Element.InOut]) -> [String: Any]? {
525+
methodEncoding: Data? = nil,
526+
inputs: [ABI.Element.InOut]) -> [String: Any]? {
525527
let data: Data
526528
let sig: Data?
527529

Sources/Web3Core/EthereumABI/Sequence+ABIExtension.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public extension Sequence where Element == ABI.Element {
2828
for case let .function(function) in self where function.name != nil {
2929
appendFunction(function.name!, function)
3030
appendFunction(function.signature, function)
31-
appendFunction(function.methodString.addHexPrefix().lowercased(), function)
31+
appendFunction(function.selector.addHexPrefix().lowercased(), function)
3232

3333
/// ABI cannot have two functions with exactly the same name and input arguments
3434
if (functions[function.signature]?.count ?? 0) > 1 {

Sources/Web3Core/EthereumAddress/EthereumAddress.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public struct EthereumAddress: Equatable {
4545
}
4646
}
4747

48-
/// Checksummed address with `0x` HEX prefix.
48+
/// Checksummed address with `0x` hex prefix.
4949
/// If the ``type`` is ``EthereumAddress/AddressType/contractDeployment`` only `0x` prefix is returned.
5050
public var address: String {
5151
switch self.type {

Sources/Web3Core/EthereumNetwork/Request/APIRequest+Methods.swift

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,24 +97,16 @@ extension APIRequest {
9797
data = try await send(uRLRequest: uRLRequest, with: provider.session)
9898
} catch Web3Error.rpcError(let error) {
9999
let responseAsString = try checkError(method: method, error: error)
100+
// TODO: why do we try to cast error response to Result.self?
100101
guard let LiteralType = Result.self as? LiteralInitiableFromString.Type,
101102
let literalValue = LiteralType.init(from: responseAsString),
102103
let result = literalValue as? Result else {
103-
throw Web3Error.dataError
104+
throw Web3Error.rpcError(error)
104105
}
105106
return APIResponse(id: 2, result: result)
106107
}
107108

108-
/// Checks if `Result` type can be initialized from HEX-encoded bytes.
109-
/// If it can - we attempt initializing a value of `Result` type.
110-
if let LiteralType = Result.self as? LiteralInitiableFromString.Type {
111-
guard let responseAsString = try? JSONDecoder().decode(APIResponse<String>.self, from: data) else { throw Web3Error.dataError }
112-
guard let literalValue = LiteralType.init(from: responseAsString.result) else { throw Web3Error.dataError }
113-
/// `literalValue` conforms `LiteralInitiableFromString` (which conforms to an `APIResponseType` type) so it never fails.
114-
guard let result = literalValue as? Result else { throw Web3Error.typeError }
115-
return APIResponse(id: responseAsString.id, jsonrpc: responseAsString.jsonrpc, result: result)
116-
}
117-
return try JSONDecoder().decode(APIResponse<Result>.self, from: data)
109+
return try initalizeLiteralTypeApiResponse(data) ?? JSONDecoder().decode(APIResponse<Result>.self, from: data)
118110
}
119111

120112
public static func send(uRLRequest: URLRequest, with session: URLSession) async throws -> Data {
@@ -145,6 +137,26 @@ extension APIRequest {
145137

146138
return data
147139
}
140+
141+
/// Attempts decoding and parsing of a response into literal types as `Data`, `(U)Int`, `Big(U)Int`.
142+
/// - Parameter data: response to parse.
143+
/// - Returns: parsed response or `nil`. Throws ``Web3Error``.
144+
internal static func initalizeLiteralTypeApiResponse<Result>(_ data: Data) throws -> APIResponse<Result>? {
145+
guard let LiteralType = Result.self as? LiteralInitiableFromString.Type else {
146+
return nil
147+
}
148+
guard let responseAsString = try? JSONDecoder().decode(APIResponse<String>.self, from: data) else {
149+
throw Web3Error.dataError(desc: "Failed to decode received data as `APIResponse<String>`. Given data: \(data.toHexString().addHexPrefix()).")
150+
}
151+
guard let literalValue = LiteralType.init(from: responseAsString.result) else {
152+
throw Web3Error.dataError(desc: "Failed to initialize \(LiteralType.self) from String `\(responseAsString.result)`.")
153+
}
154+
/// `literalValue` conforms `LiteralInitiableFromString`, that conforming to an `APIResultType` type, so it's never fails.
155+
guard let result = literalValue as? Result else {
156+
throw Web3Error.typeError(desc: "Initialized value of type \(LiteralType.self) cannot be cast as \(Result.self).")
157+
}
158+
return APIResponse(id: responseAsString.id, jsonrpc: responseAsString.jsonrpc, result: result)
159+
}
148160
}
149161

150162
/// JSON RPC Error object. See official specification https://www.jsonrpc.org/specification#error_object

Sources/Web3Core/KeystoreManager/EtherscanTransactionChecker.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ public struct EtherscanTransactionChecker: TransactionChecker {
1212

1313
public init(urlSession: URLSession, apiKey: String) {
1414
self.urlSession = URLSessionProxyImplementation(urlSession: urlSession)
15-
self.apiKey = apiKey
15+
self.apiKey = apiKey.trim()
1616
}
1717

1818
internal init(urlSession: URLSessionProxy, apiKey: String) {
1919
self.urlSession = urlSession
20-
self.apiKey = apiKey
20+
self.apiKey = apiKey.trim()
2121
}
2222

2323
public func hasTransactions(ethereumAddress: EthereumAddress) async throws -> Bool {
2424
let urlString = "https://api.etherscan.io/api?module=account&action=txlist&address=\(ethereumAddress.address)&startblock=0&page=1&offset=1&sort=asc&apikey=\(apiKey)"
25-
guard let url = URL(string: urlString) else {
25+
guard let url = URL(string: urlString), !apiKey.isEmpty else {
2626
throw EtherscanTransactionCheckerError.invalidUrl(url: urlString)
2727
}
2828
let request = URLRequest(url: url)

Sources/Web3Core/Structure/Block/Block.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ extension Block: Decodable {
9898

9999
let unclesStrings = try container.decode([String].self, forKey: .uncles)
100100
self.uncles = try unclesStrings.map {
101-
guard let data = Data.fromHex($0) else { throw Web3Error.dataError }
101+
guard let data = Data.fromHex($0) else {
102+
throw Web3Error.dataError(desc: "Failed to parse uncle block from hex string to Data. Given string is not valid hex: \($0).")
103+
}
102104
return data
103105
}
104106
}

Sources/Web3Core/Structure/EventLog/EventLog.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ public struct EventLog: Decodable {
6363
let topicsStrings = try container.decode([String].self, forKey: .topics)
6464

6565
self.topics = try topicsStrings.map {
66-
guard let topic = Data.fromHex($0) else { throw Web3Error.dataError }
66+
guard let topic = Data.fromHex($0) else {
67+
throw Web3Error.dataError(desc: "Failed to parse event's topic from hex string to Data. Given string is not valid hex: \($0).")
68+
}
6769
return topic
6870
}
6971
}

Sources/Web3Core/Structure/Transaction/TransactionInBlock.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ public enum TransactionInBlock: Decodable {
1515
public init(from decoder: Decoder) throws {
1616
let value = try decoder.singleValueContainer()
1717
if let string = try? value.decode(String.self) {
18-
guard let d = Data.fromHex(string) else {throw Web3Error.dataError}
18+
guard let d = Data.fromHex(string) else {
19+
throw Web3Error.dataError(desc: "Failed to parse hex string to bytes. Given hex string: \(string)")
20+
}
1921
self = .hash(d)
2022
} else if let transaction = try? value.decode(CodableTransaction.self) {
2123
self = .transaction(transaction)

Sources/Web3Core/Structure/TxPool/TxPoolContent.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ public struct TxPoolContent: Decodable {
2323
for addressKey in raw.allKeys {
2424
let addressString = addressKey.stringValue
2525
guard let address = EthereumAddress(addressString, type: .normal, ignoreChecksum: true) else {
26-
throw Web3Error.dataError
26+
throw Web3Error.dataError(desc: "Failed to initialize EthereumAddress from value \(addressString). Is it 20 bytes hex string?")
2727
}
2828
let nestedContainer = try raw.nestedContainer(keyedBy: AdditionalDataCodingKeys.self, forKey: addressKey)
2929
var perNonceInformation = [TxPoolContentForNonce]()
3030
perNonceInformation.reserveCapacity(nestedContainer.allKeys.count)
3131
for nonceKey in nestedContainer.allKeys {
3232
guard let nonce = BigUInt(nonceKey.stringValue) else {
33-
throw Web3Error.dataError
33+
throw Web3Error.dataError(desc: "Failed to parse \(nonceKey.stringValue) as BigUInt nonce value for address \(addressString).")
3434
}
3535
let n = try? nestedContainer.nestedUnkeyedContainer(forKey: nonceKey)
3636
if n != nil {

Sources/Web3Core/Transaction/CodableTransaction.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ extension CodableTransaction: Codable {
210210
/// initializer required to support the Decodable protocol
211211
/// - Parameter decoder: the decoder stream for the input data
212212
public init(from decoder: Decoder) throws {
213-
guard let env = try EnvelopeFactory.createEnvelope(from: decoder) else { throw Web3Error.dataError }
213+
guard let env = try EnvelopeFactory.createEnvelope(from: decoder) else {
214+
throw Web3Error.dataError(desc: "EnvelopeFactory.createEnvelope failed. Failed to decode given data into CodableTransaction.")
215+
}
214216
self.envelope = env
215217

216218
// capture any metadata that might be present

Sources/Web3Core/Transaction/Envelope/EIP1559Envelope.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,16 @@ extension EIP1559Envelope {
102102
let list = try? container.decode([AccessListEntry].self, forKey: .accessList)
103103
self.accessList = list ?? []
104104

105-
let toString = try? container.decode(String.self, forKey: .to)
106-
switch toString {
105+
let stringValue = try? container.decode(String.self, forKey: .to)
106+
switch stringValue {
107107
case nil, "0x", "0x0":
108108
self.to = EthereumAddress.contractDeploymentAddress()
109109
default:
110110
// the forced unwrap here is safe as we trap nil in the previous case
111111
// swiftlint:disable force_unwrapping
112-
guard let ethAddr = EthereumAddress(toString!) else { throw Web3Error.dataError }
112+
guard let ethAddr = EthereumAddress(stringValue!) else {
113+
throw Web3Error.dataError(desc: "Failed to parse string as EthereumAddress. Given string: \(stringValue!). Is it a valid hex value 20 bytes in length?")
114+
}
113115
// swiftlint:enable force_unwrapping
114116
self.to = ethAddr
115117
}

Sources/Web3Core/Transaction/Envelope/EIP2930Envelope.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,16 @@ extension EIP2930Envelope {
8080
let list = try? container.decode([AccessListEntry].self, forKey: .accessList)
8181
self.accessList = list ?? []
8282

83-
let toString = try? container.decode(String.self, forKey: .to)
84-
switch toString {
83+
let stringValue = try? container.decode(String.self, forKey: .to)
84+
switch stringValue {
8585
case nil, "0x", "0x0":
8686
self.to = EthereumAddress.contractDeploymentAddress()
8787
default:
8888
// the forced unwrap here is safe as we trap nil in the previous case
8989
// swiftlint:disable force_unwrapping
90-
guard let ethAddr = EthereumAddress(toString!) else { throw Web3Error.dataError }
90+
guard let ethAddr = EthereumAddress(stringValue!) else {
91+
throw Web3Error.dataError(desc: "Failed to parse string as EthereumAddress. Given string: \(stringValue!). Is it a valid hex value 20 bytes in length?")
92+
}
9193
// swiftlint:enable force_unwrapping
9294
self.to = ethAddr
9395
}
@@ -241,21 +243,25 @@ public struct AccessListEntry: CustomStringConvertible, Codable {
241243
public init(from decoder: Decoder) throws {
242244
let container = try decoder.container(keyedBy: CodingKeys.self)
243245

244-
let addrString = try? container.decode(String.self, forKey: .address)
245-
switch addrString {
246+
let stringValue = try? container.decode(String.self, forKey: .address)
247+
switch stringValue {
246248
case nil, "0x", "0x0":
247249
self.address = EthereumAddress.contractDeploymentAddress()
248250
default:
249251
// the forced unwrap here is safe as we trap nil in the previous case
250252
// swiftlint:disable force_unwrapping
251-
guard let ethAddr = EthereumAddress(addrString!) else { throw Web3Error.dataError }
253+
guard let ethAddr = EthereumAddress(stringValue!) else {
254+
throw Web3Error.dataError(desc: "Failed to parse string as EthereumAddress. Given string: \(stringValue!). Is it a valid hex value 20 bytes in length?")
255+
}
252256
// swiftlint:enable force_unwrapping
253257
self.address = ethAddr
254258
}
255259
self.storageKeys = []
256260
if let keyStrings = try? container.decode([String].self, forKey: .storageKeys) {
257261
for keyString in keyStrings {
258-
guard let number = BigUInt(from: keyString) else { throw Web3Error.dataError }
262+
guard let number = BigUInt(from: keyString) else {
263+
throw Web3Error.dataError(desc: "Failed to patse storage key values string into BigUInt. String value: \(keyString). Check the formmating of your values.")
264+
}
259265
self.storageKeys.append(number)
260266
}
261267
}

Sources/Web3Core/Transaction/Envelope/EnvelopeFactory.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ public struct EnvelopeFactory {
5151
let envelopeType: TransactionType
5252
if container.contains(.type) {
5353
let typeUInt = try container.decodeHex(UInt.self, forKey: .type)
54-
if typeUInt < TransactionType.allCases.count {
55-
guard let type = TransactionType(rawValue: typeUInt) else { throw Web3Error.dataError } // conversion error
56-
envelopeType = type
57-
} else { throw Web3Error.dataError } // illegal value
54+
guard let type = TransactionType(rawValue: typeUInt) else {
55+
throw Web3Error.dataError(desc: "Given TransactionType raw value is not supported. Value given is \(typeUInt).")
56+
}
57+
envelopeType = type
5858
} else { envelopeType = .legacy } // legacy streams may not have type set
5959

6060
switch envelopeType {

Sources/Web3Core/Transaction/Envelope/LegacyEnvelope.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,16 @@ extension LegacyEnvelope {
8989
self.explicitChainID = try container.decodeHexIfPresent(BigUInt.self, forKey: .chainId)
9090
self.nonce = try container.decodeHex(BigUInt.self, forKey: .nonce)
9191

92-
let toString = try? container.decode(String.self, forKey: .to)
93-
switch toString {
92+
let stringValue = try? container.decode(String.self, forKey: .to)
93+
switch stringValue {
9494
case nil, "0x", "0x0":
9595
self.to = EthereumAddress.contractDeploymentAddress()
9696
default:
9797
// the forced unwrap here is safe as we trap nil in the previous case
9898
// swiftlint:disable force_unwrapping
99-
guard let ethAddr = EthereumAddress(toString!) else { throw Web3Error.dataError }
99+
guard let ethAddr = EthereumAddress(stringValue!) else {
100+
throw Web3Error.dataError(desc: "Failed to parse string as EthereumAddress. Given string: \(stringValue!). Is it a valid hex value 20 bytes in length?")
101+
}
100102
// swiftlint:enable force_unwrapping
101103
self.to = ethAddr
102104
}

Sources/Web3Core/Utility/Data+Extension.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ public extension Data {
1414
}
1515

1616
func toArray<T>(type: T.Type) throws -> [T] {
17-
return try self.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
17+
return try withUnsafeBytes { (body: UnsafeRawBufferPointer) in
1818
if let bodyAddress = body.baseAddress, body.count > 0 {
1919
let pointer = bodyAddress.assumingMemoryBound(to: T.self)
2020
return [T](UnsafeBufferPointer(start: pointer, count: self.count/MemoryLayout<T>.stride))
2121
} else {
22-
throw Web3Error.dataError
22+
throw Web3Error.dataError(desc: "`withUnsafeBytes` function call failed. We were unable to get a pointer to the first byte of the buffer or the buffer length was 0.")
2323
}
2424
}
2525
}
2626

2727
func constantTimeComparisonTo(_ other: Data?) -> Bool {
28-
guard let rhs = other else {return false}
29-
guard self.count == rhs.count else {return false}
28+
guard let rhs = other else { return false }
29+
guard self.count == rhs.count else { return false }
3030
var difference = UInt8(0x00)
3131
for i in 0..<self.count { // compare full length
3232
difference |= self[i] ^ rhs[i] // constant time

0 commit comments

Comments
 (0)