Skip to content

Commit f2cb4b9

Browse files
fix: EthError uses InOut as type for inputs; produces error declaration using errorDeclaration computed variable; tests added
1 parent 3198752 commit f2cb4b9

File tree

3 files changed

+65
-19
lines changed

3 files changed

+65
-19
lines changed

Sources/Core/EthereumABI/ABIElements.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,16 @@ public extension ABI {
156156
/// Custom structured error type available since solidity 0.8.4
157157
public struct EthError {
158158
public let name: String
159-
public let inputs: [Input]
159+
public let inputs: [InOut]
160160

161-
public struct Input {
162-
public let name: String
163-
public let type: ParameterType
161+
/// e.g. `CustomError(uint32, address sender)`
162+
public var errorDeclaration: String {
163+
"\(name)(\(inputs.map { "\($0.type.abiRepresentation) \($0.name)".trim() }.joined(separator: ",")))"
164+
}
164165

165-
public init(name: String, type: ParameterType) {
166-
self.name = name
167-
self.type = type
168-
}
166+
public init(name: String, inputs: [InOut]) {
167+
self.name = name.trim()
168+
self.inputs = inputs
169169
}
170170
}
171171
}

Sources/Core/EthereumABI/ABIParsing.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,9 @@ private func parseReceive(abiRecord: ABI.Record) throws -> ABI.Element.Receive {
129129
}
130130

131131
private func parseError(abiRecord: ABI.Record) throws -> ABI.Element.EthError {
132-
let inputs = try abiRecord.inputs?.map({ (input: ABI.Input) throws -> ABI.Element.EthError.Input in
133-
let nativeInput = try input.parseForError()
134-
return nativeInput
135-
})
136-
let abiInputs = inputs ?? []
132+
let abiInputs = try abiRecord.inputs?.map({ input throws -> ABI.Element.InOut in
133+
try input.parse()
134+
}) ?? []
137135
let name = abiRecord.name ?? ""
138136
return ABI.Element.EthError(name: name, inputs: abiInputs)
139137
}
@@ -172,12 +170,6 @@ extension ABI.Input {
172170
let indexed = self.indexed == true
173171
return ABI.Element.Event.Input(name: name, type: parameterType, indexed: indexed)
174172
}
175-
176-
func parseForError() throws -> ABI.Element.EthError.Input {
177-
let name = self.name ?? ""
178-
let parameterType = try ABITypeParser.parseTypeString(self.type)
179-
return ABI.Element.EthError.Input(name: name, type: parameterType)
180-
}
181173
}
182174

183175
extension ABI.Output {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// ABIElementsTest.swift
3+
//
4+
// Created by JeneaVranceanu on 28.11.2022.
5+
//
6+
7+
import Foundation
8+
import XCTest
9+
import Core
10+
11+
class ABIElementsTest: XCTestCase {
12+
typealias EthError = ABI.Element.EthError
13+
14+
/// Function with any parameters should be able to decode `require` and `revert` calls in soliditiy.
15+
/// Note: `require(expression)` and `revert()` without a message return 0 bytes thus we cannot guarantee
16+
/// that 0 bytes response will be interpreted correctly.
17+
private let emptyFunction = ABI.Element.Function(name: "any",
18+
inputs: [],
19+
outputs: [],
20+
constant: false,
21+
payable: false)
22+
23+
func testErrorRepresentation() {
24+
XCTAssertEqual(EthError(name: "Error", inputs: []).errorDeclaration, "Error()")
25+
XCTAssertEqual(EthError(name: "Error", inputs: [.init(name: "", type: .address)]).errorDeclaration, "Error(address)")
26+
XCTAssertEqual(EthError(name: "Error", inputs: [.init(name: " ", type: .address)]).errorDeclaration, "Error(address)")
27+
XCTAssertEqual(EthError(name: "Error", inputs: [.init(name: " ", type: .address), .init(name: "", type: .uint(bits: 256))]).errorDeclaration, "Error(address,uint256)")
28+
XCTAssertEqual(EthError(name: "Error", inputs: [.init(name: "sender", type: .address), .init(name: " ", type: .uint(bits: 256))]).errorDeclaration, "Error(address sender,uint256)")
29+
let allTypesNamedAndNot: [ABI.Element.InOut] = [
30+
.init(name: "sender", type: .address),
31+
.init(name: "", type: .address),
32+
.init(name: "", type: .uint(bits: 8)),
33+
.init(name: "", type: .uint(bits: 16)),
34+
.init(name: "", type: .uint(bits: 32)),
35+
.init(name: "", type: .uint(bits: 64)),
36+
.init(name: "", type: .uint(bits: 128)),
37+
.init(name: "", type: .uint(bits: 256)),
38+
.init(name: "my_int_8", type: .int(bits: 8)),
39+
.init(name: "my_int_16", type: .int(bits: 16)),
40+
.init(name: "my_int_32", type: .int(bits: 32)),
41+
.init(name: "my_int_64", type: .int(bits: 64)),
42+
.init(name: "my_int_128", type: .int(bits: 128)),
43+
.init(name: "my_int_256", type: .int(bits: 256)),
44+
.init(name: "someFlag", type: .bool),
45+
.init(name: "rand_bytes", type: .bytes(length: 123)),
46+
.init(name: "", type: .dynamicBytes),
47+
.init(name: "error_message_maybe", type: .string),
48+
]
49+
XCTAssertEqual(EthError(name: "VeryCustomErrorName",
50+
inputs: allTypesNamedAndNot).errorDeclaration,
51+
"VeryCustomErrorName(address sender,address,uint8,uint16,uint32,uint64,uint128,uint256,int8 my_int_8,int16 my_int_16,int32 my_int_32,int64 my_int_64,int128 my_int_128,int256 my_int_256,bool someFlag,bytes123 rand_bytes,bytes,string error_message_maybe)")
52+
}
53+
54+
}

0 commit comments

Comments
 (0)