@@ -202,7 +202,7 @@ extension ABI.Element.Constructor {
202202extension ABI . Element . Function {
203203
204204 /// Encode parameters of a given contract method
205- /// - Parameter parameters : Parameters to pass to Ethereum contract
205+ /// - Parameters : Parameters to pass to Ethereum contract
206206 /// - Returns: Encoded data
207207 public func encodeParameters( _ parameters: [ Any ] ) -> Data ? {
208208 guard parameters. count == inputs. count,
@@ -292,6 +292,44 @@ extension ABI.Element.Event {
292292 }
293293}
294294
295+ // MARK: - Decode custom error
296+
297+ extension ABI . Element . EthError {
298+ /// Decodes `revert CustomError(_)` calls.
299+ /// - Parameters:
300+ /// - data: bytes returned by a function call that stripped error signature hash.
301+ /// - Returns: a dictionary containing decoded data mappend to indices and names of returned values or nil if decoding failed.
302+ public func decodeEthError( _ data: Data ) -> [ String : Any ] ? {
303+ guard inputs. count * 32 <= data. count,
304+ let decoded = ABIDecoder . decode ( types: inputs, data: data) else {
305+ return nil
306+ }
307+
308+ var result = [ String: Any] ( )
309+ for (index, out) in inputs. enumerated ( ) {
310+ result [ " \( index) " ] = decoded [ index]
311+ if !out. name. isEmpty {
312+ result [ out. name] = decoded [ index]
313+ }
314+ }
315+ return result
316+ }
317+
318+ /// Decodes `revert(string)` or `require(expression, string)` calls.
319+ /// These calls are decomposed as `Error(string)` error.
320+ public static func decodeStringError( _ data: Data ) -> String ? {
321+ let decoded = ABIDecoder . decode ( types: [ . init( name: " " , type: . string) ] , data: data)
322+ return decoded? . first as? String
323+ }
324+
325+ /// Decodes `Panic(uint256)` errors.
326+ /// See more about panic code explain at: https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require
327+ public static func decodePanicError( _ data: Data ) -> BigUInt ? {
328+ let decoded = ABIDecoder . decode ( types: [ . init( name: " " , type: . uint( bits: 256 ) ) ] , data: data)
329+ return decoded? . first as? BigUInt
330+ }
331+ }
332+
295333// MARK: - Function input/output decoding
296334
297335extension ABI . Element {
@@ -304,7 +342,7 @@ extension ABI.Element {
304342 case . fallback:
305343 return nil
306344 case . function( let function) :
307- return function. decodeReturnData ( data)
345+ return try ? function. decodeReturnData ( data)
308346 case . receive:
309347 return nil
310348 case . error:
@@ -337,74 +375,38 @@ extension ABI.Element.Function {
337375 return ABIDecoder . decodeInputData ( rawData, methodEncoding: methodEncoding, inputs: inputs)
338376 }
339377
340- /// Decodes data returned by a function call. Able to decode `revert(string)`, `revert CustomError(...)` and `require(expression, string)` calls.
378+ /// Decodes data returned by a function call.
341379 /// - Parameters:
342380 /// - data: bytes returned by a function call;
343- /// - errors: optional dictionary of known errors that could be returned by the function you called. Used to decode the error information.
344381 /// - Returns: a dictionary containing decoded data mappend to indices and names of returned values if these are not `nil`.
345- /// If `data` is an error response returns dictionary containing all available information about that specific error. Read more for details.
382+ /// - Throws:
383+ /// - `Web3Error.processingError(desc: String)` when decode process failed.
346384 ///
347385 /// Return cases:
348- /// - when no `outputs` declared and `data` is not an error response :
386+ /// - when no `outputs` declared:
349387 /// ```swift
350- /// ["_success": true ]
388+ /// [: ]
351389 /// ```
352390 /// - when `outputs` declared and decoding completed successfully:
353391 /// ```swift
354- /// ["_success": true, " 0": value_1, "1": value_2, ...]
392+ /// ["0": value_1, "1": value_2, ...]
355393 /// ```
356394 /// Additionally this dictionary will have mappings to output names if these names are specified in the ABI;
357- /// - function call was aborted using `revert(message)` or `require(expression, message)`:
358- /// ```swift
359- /// ["_success": false, "_abortedByRevertOrRequire": true, "_errorMessage": message]`
360- /// ```
361- /// - function call was aborted using `revert CustomMessage()` and `errors` argument contains the ABI of that custom error type:
362- /// ```swift
363- /// ["_success": false,
364- /// "_abortedByRevertOrRequire": true,
365- /// "_error": error_name_and_types, // e.g. `MyCustomError(uint256, address senderAddress)`
366- /// "0": error_arg1,
367- /// "1": error_arg2,
368- /// ...,
369- /// "error_arg1_name": error_arg1, // Only named arguments will be mapped to their names, e.g. `"senderAddress": EthereumAddress`
370- /// "error_arg2_name": error_arg2, // Otherwise, you can query them by position index.
371- /// ...]
372- /// ```
373- /// - in case of any error:
374- /// ```swift
375- /// ["_success": false, "_failureReason": String]
376- /// ```
377- /// Error reasons include:
378- /// - `outputs` declared but at least one value failed to be decoded;
379- /// - `data.count` is less than `outputs.count * 32`;
380- /// - `outputs` defined and `data` is empty;
381- /// - `data` represent reverted transaction
382- ///
383- /// How `revert(string)` and `require(expression, string)` return value is decomposed:
384- /// - `08C379A0` function selector for `Error(string)`;
385- /// - next 32 bytes are the data offset;
386- /// - next 32 bytes are the error message length;
387- /// - the next N bytes, where N >= 32, are the message bytes
388- /// - the rest are 0 bytes padding.
389- public func decodeReturnData( _ data: Data , errors: [ String : ABI . Element . EthError ] ? = nil ) -> [ String : Any ] {
390- if let decodedError = decodeErrorResponse ( data, errors: errors) {
391- return decodedError
392- }
393-
395+ public func decodeReturnData( _ data: Data ) throws -> [ String : Any ] {
394396 guard !outputs. isEmpty else {
395397 NSLog ( " Function doesn't have any output types to decode given data. " )
396- return [ " _success " : true ]
398+ return [ : ]
397399 }
398400
399401 guard outputs. count * 32 <= data. count else {
400- return [ " _success " : false , " _failureReason " : " Bytes count must be at least \( outputs. count * 32 ) . Given \( data. count) . Decoding will fail. " ]
402+ throw Web3Error . processingError ( desc : " Bytes count must be at least \( outputs. count * 32 ) . Given \( data. count) . Decoding will fail. " )
401403 }
402404
403405 // TODO: need improvement - we should be able to tell which value failed to be decoded
404406 guard let values = ABIDecoder . decode ( types: outputs, data: data) else {
405- return [ " _success " : false , " _failureReason " : " Failed to decode at least one value. " ]
407+ throw Web3Error . processingError ( desc : " Failed to decode at least one value. " )
406408 }
407- var returnArray : [ String : Any ] = [ " _success " : true ]
409+ var returnArray : [ String : Any ] = [ : ]
408410 for i in outputs. indices {
409411 returnArray [ " \( i) " ] = values [ i]
410412 if !outputs[ i] . name. isEmpty {
@@ -453,6 +455,7 @@ extension ABI.Element.Function {
453455 /// // "_parsingError" is optional and is present only if decoding of custom error arguments failed
454456 /// "_parsingError": "Data matches MyCustomError(uint256, address senderAddress) but failed to be decoded."]
455457 /// ```
458+ @available ( * , deprecated, message: " Use decode function from `ABI.Element.EthError` instead " )
456459 public func decodeErrorResponse( _ data: Data , errors: [ String : ABI . Element . EthError ] ? = nil ) -> [ String : Any ] ? {
457460 /// If data is empty and outputs are expected it is treated as a `require(expression)` or `revert()` call with no message.
458461 /// In solidity `require(false)` and `revert()` calls return empty error response.
0 commit comments