@@ -202,7 +202,7 @@ extension ABI.Element.Constructor {
202
202
extension ABI . Element . Function {
203
203
204
204
/// Encode parameters of a given contract method
205
- /// - Parameter parameters : Parameters to pass to Ethereum contract
205
+ /// - Parameters : Parameters to pass to Ethereum contract
206
206
/// - Returns: Encoded data
207
207
public func encodeParameters( _ parameters: [ Any ] ) -> Data ? {
208
208
guard parameters. count == inputs. count,
@@ -211,13 +211,123 @@ extension ABI.Element.Function {
211
211
}
212
212
}
213
213
214
- // MARK: - Event logs decoding
214
+ // MARK: - Event logs decoding & encoding
215
215
216
216
extension ABI . Element . Event {
217
217
public func decodeReturnedLogs( eventLogTopics: [ Data ] , eventLogData: Data ) -> [ String : Any ] ? {
218
218
guard let eventContent = ABIDecoder . decodeLog ( event: self , eventLogTopics: eventLogTopics, eventLogData: eventLogData) else { return nil }
219
219
return eventContent
220
220
}
221
+
222
+ public static func encodeTopic( input: ABI . Element . Event . Input , value: Any ) -> EventFilterParameters . Topic ? {
223
+ switch input. type {
224
+ case . string:
225
+ guard let string = value as? String else {
226
+ return nil
227
+ }
228
+ return . string( string. sha3 ( . keccak256) . addHexPrefix ( ) )
229
+ case . dynamicBytes:
230
+ guard let data = ABIEncoder . convertToData ( value) else {
231
+ return nil
232
+ }
233
+ return . string( data. sha3 ( . keccak256) . toHexString ( ) . addHexPrefix ( ) )
234
+ case . bytes( length: _) :
235
+ guard let data = ABIEncoder . convertToData ( value) , let data = data. setLengthLeft ( 32 ) else {
236
+ return nil
237
+ }
238
+ return . string( data. toHexString ( ) . addHexPrefix ( ) )
239
+ case . address, . uint( bits: _) , . int( bits: _) , . bool:
240
+ guard let encoded = ABIEncoder . encodeSingleType ( type: input. type, value: value) else {
241
+ return nil
242
+ }
243
+ return . string( encoded. toHexString ( ) . addHexPrefix ( ) )
244
+ default :
245
+ guard let data = try ? ABIEncoder . abiEncode ( value) . setLengthLeft ( 32 ) else {
246
+ return nil
247
+ }
248
+ return . string( data. toHexString ( ) . addHexPrefix ( ) )
249
+ }
250
+ }
251
+
252
+ public func encodeParameters( _ parameters: [ Any ? ] ) -> [ EventFilterParameters . Topic ? ] {
253
+ guard parameters. count <= inputs. count else {
254
+ // too many arguments for fragment
255
+ return [ ]
256
+ }
257
+ var topics : [ EventFilterParameters . Topic ? ] = [ ]
258
+
259
+ if !anonymous {
260
+ topics. append ( . string( topic. toHexString ( ) . addHexPrefix ( ) ) )
261
+ }
262
+
263
+ for (i, p) in parameters. enumerated ( ) {
264
+ let input = inputs [ i]
265
+ if !input. indexed {
266
+ // cannot filter non-indexed parameters; must be null
267
+ return [ ]
268
+ }
269
+ if p == nil {
270
+ topics. append ( nil )
271
+ } else if input. type. isArray || input. type. isTuple {
272
+ // filtering with tuples or arrays not supported
273
+ return [ ]
274
+ } else if let p = p as? Array < Any > {
275
+ topics. append ( . strings( p. map { Self . encodeTopic ( input: input, value: $0) } ) )
276
+ } else {
277
+ topics. append ( Self . encodeTopic ( input: input, value: p!) )
278
+ }
279
+ }
280
+
281
+ // Trim off trailing nulls
282
+ while let last = topics. last {
283
+ if last == nil {
284
+ topics. removeLast ( )
285
+ } else if case . string( let string) = last, string == nil {
286
+ topics. removeLast ( )
287
+ } else {
288
+ break
289
+ }
290
+ }
291
+ return topics
292
+ }
293
+ }
294
+
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
+ }
221
331
}
222
332
223
333
// MARK: - Function input/output decoding
@@ -232,7 +342,7 @@ extension ABI.Element {
232
342
case . fallback:
233
343
return nil
234
344
case . function( let function) :
235
- return function. decodeReturnData ( data)
345
+ return try ? function. decodeReturnData ( data)
236
346
case . receive:
237
347
return nil
238
348
case . error:
@@ -265,74 +375,38 @@ extension ABI.Element.Function {
265
375
return ABIDecoder . decodeInputData ( rawData, methodEncoding: methodEncoding, inputs: inputs)
266
376
}
267
377
268
- /// 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.
269
379
/// - Parameters:
270
380
/// - data: bytes returned by a function call;
271
- /// - errors: optional dictionary of known errors that could be returned by the function you called. Used to decode the error information.
272
381
/// - Returns: a dictionary containing decoded data mappend to indices and names of returned values if these are not `nil`.
273
- /// 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.
274
384
///
275
385
/// Return cases:
276
- /// - when no `outputs` declared and `data` is not an error response :
386
+ /// - when no `outputs` declared:
277
387
/// ```swift
278
- /// ["_success": true ]
388
+ /// [: ]
279
389
/// ```
280
390
/// - when `outputs` declared and decoding completed successfully:
281
391
/// ```swift
282
- /// ["_success": true, " 0": value_1, "1": value_2, ...]
392
+ /// ["0": value_1, "1": value_2, ...]
283
393
/// ```
284
394
/// Additionally this dictionary will have mappings to output names if these names are specified in the ABI;
285
- /// - function call was aborted using `revert(message)` or `require(expression, message)`:
286
- /// ```swift
287
- /// ["_success": false, "_abortedByRevertOrRequire": true, "_errorMessage": message]`
288
- /// ```
289
- /// - function call was aborted using `revert CustomMessage()` and `errors` argument contains the ABI of that custom error type:
290
- /// ```swift
291
- /// ["_success": false,
292
- /// "_abortedByRevertOrRequire": true,
293
- /// "_error": error_name_and_types, // e.g. `MyCustomError(uint256, address senderAddress)`
294
- /// "0": error_arg1,
295
- /// "1": error_arg2,
296
- /// ...,
297
- /// "error_arg1_name": error_arg1, // Only named arguments will be mapped to their names, e.g. `"senderAddress": EthereumAddress`
298
- /// "error_arg2_name": error_arg2, // Otherwise, you can query them by position index.
299
- /// ...]
300
- /// ```
301
- /// - in case of any error:
302
- /// ```swift
303
- /// ["_success": false, "_failureReason": String]
304
- /// ```
305
- /// Error reasons include:
306
- /// - `outputs` declared but at least one value failed to be decoded;
307
- /// - `data.count` is less than `outputs.count * 32`;
308
- /// - `outputs` defined and `data` is empty;
309
- /// - `data` represent reverted transaction
310
- ///
311
- /// How `revert(string)` and `require(expression, string)` return value is decomposed:
312
- /// - `08C379A0` function selector for `Error(string)`;
313
- /// - next 32 bytes are the data offset;
314
- /// - next 32 bytes are the error message length;
315
- /// - the next N bytes, where N >= 32, are the message bytes
316
- /// - the rest are 0 bytes padding.
317
- public func decodeReturnData( _ data: Data , errors: [ String : ABI . Element . EthError ] ? = nil ) -> [ String : Any ] {
318
- if let decodedError = decodeErrorResponse ( data, errors: errors) {
319
- return decodedError
320
- }
321
-
395
+ public func decodeReturnData( _ data: Data ) throws -> [ String : Any ] {
322
396
guard !outputs. isEmpty else {
323
397
NSLog ( " Function doesn't have any output types to decode given data. " )
324
- return [ " _success " : true ]
398
+ return [ : ]
325
399
}
326
400
327
401
guard outputs. count * 32 <= data. count else {
328
- 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. " )
329
403
}
330
404
331
405
// TODO: need improvement - we should be able to tell which value failed to be decoded
332
406
guard let values = ABIDecoder . decode ( types: outputs, data: data) else {
333
- return [ " _success " : false , " _failureReason " : " Failed to decode at least one value. " ]
407
+ throw Web3Error . processingError ( desc : " Failed to decode at least one value. " )
334
408
}
335
- var returnArray : [ String : Any ] = [ " _success " : true ]
409
+ var returnArray : [ String : Any ] = [ : ]
336
410
for i in outputs. indices {
337
411
returnArray [ " \( i) " ] = values [ i]
338
412
if !outputs[ i] . name. isEmpty {
@@ -381,6 +455,7 @@ extension ABI.Element.Function {
381
455
/// // "_parsingError" is optional and is present only if decoding of custom error arguments failed
382
456
/// "_parsingError": "Data matches MyCustomError(uint256, address senderAddress) but failed to be decoded."]
383
457
/// ```
458
+ @available ( * , deprecated, message: " Use decode function from `ABI.Element.EthError` instead " )
384
459
public func decodeErrorResponse( _ data: Data , errors: [ String : ABI . Element . EthError ] ? = nil ) -> [ String : Any ] ? {
385
460
/// If data is empty and outputs are expected it is treated as a `require(expression)` or `revert()` call with no message.
386
461
/// In solidity `require(false)` and `revert()` calls return empty error response.
0 commit comments