Skip to content

Commit 59bb431

Browse files
Merge pull request #455 from JeneaVranceanu/feat/solidity-error-type
2 parents e30d812 + 8ca9e92 commit 59bb431

File tree

6 files changed

+90
-37
lines changed

6 files changed

+90
-37
lines changed

Sources/web3swift/EthereumABI/ABIElements.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public extension ABI {
4444
case fallback(Fallback)
4545
case event(Event)
4646
case receive(Receive)
47+
case error(EthError)
4748

4849
public enum StateMutability {
4950
case payable
@@ -152,6 +153,21 @@ public extension ABI {
152153
self.payable = payable
153154
}
154155
}
156+
/// Custom structured error type available since solidity 0.8.4
157+
public struct EthError {
158+
public let name: String
159+
public let inputs: [Input]
160+
161+
public struct Input {
162+
public let name: String
163+
public let type: ParameterType
164+
165+
public init(name: String, type: ParameterType) {
166+
self.name = name
167+
self.type = type
168+
}
169+
}
170+
}
155171
}
156172
}
157173

@@ -173,6 +189,8 @@ extension ABI.Element {
173189
return signature + data
174190
case .receive(_):
175191
return nil
192+
case .error(_):
193+
return nil
176194
}
177195
}
178196
}
@@ -249,6 +267,8 @@ extension ABI.Element {
249267
return returnArray
250268
case .receive(_):
251269
return nil
270+
case .error(_):
271+
return nil
252272
}
253273
}
254274

@@ -324,6 +344,8 @@ extension ABI.Element {
324344
return returnArray
325345
case .receive(_):
326346
return nil
347+
case .error(_):
348+
return nil
327349
}
328350
}
329351
}

Sources/web3swift/EthereumABI/ABIParsing.swift

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ extension ABI {
3030
case fallback
3131
case event
3232
case receive
33+
case error
3334
}
3435

3536
}
3637

3738
extension ABI.Record {
3839
public func parse() throws -> ABI.Element {
39-
let typeString = self.type != nil ? self.type! : "function"
40+
let typeString = self.type ?? "function"
4041
guard let type = ABI.ElementType(rawValue: typeString) else {
4142
throw ABI.ParsingError.elementTypeInvalid
4243
}
@@ -61,6 +62,9 @@ fileprivate func parseToElement(from abiRecord: ABI.Record, type: ABI.ElementTyp
6162
case .receive:
6263
let receive = try parseReceive(abiRecord: abiRecord)
6364
return ABI.Element.receive(receive)
65+
case .error:
66+
let error = try parseError(abiRecord: abiRecord)
67+
return ABI.Element.error(error)
6468
}
6569

6670
}
@@ -70,26 +74,22 @@ fileprivate func parseFunction(abiRecord: ABI.Record) throws -> ABI.Element.Func
7074
let nativeInput = try input.parse()
7175
return nativeInput
7276
})
73-
let abiInputs = inputs != nil ? inputs! : [ABI.Element.InOut]()
74-
let outputs = try abiRecord.outputs?.map({ (output: ABI.Output) throws -> ABI.Element.InOut in
77+
let abiInputs = inputs ?? [ABI.Element.InOut]()
78+
let outputs = try abiRecord.outputs?.map({ (output:ABI.Output) throws -> ABI.Element.InOut in
7579
let nativeOutput = try output.parse()
7680
return nativeOutput
7781
})
78-
let abiOutputs = outputs != nil ? outputs! : [ABI.Element.InOut]()
79-
let name = abiRecord.name != nil ? abiRecord.name! : ""
80-
let payable = abiRecord.stateMutability != nil ?
81-
(abiRecord.stateMutability == "payable" || abiRecord.payable ?? false) : false
82-
let constant = (abiRecord.constant == true || abiRecord.stateMutability == "view" || abiRecord.stateMutability == "pure")
82+
let abiOutputs = outputs ?? [ABI.Element.InOut]()
83+
let name = abiRecord.name ?? ""
84+
let payable = abiRecord.stateMutability == "payable" || abiRecord.payable == true
85+
let constant = abiRecord.constant == true || abiRecord.stateMutability == "view" || abiRecord.stateMutability == "pure"
8386
let functionElement = ABI.Element.Function(name: name, inputs: abiInputs, outputs: abiOutputs, constant: constant, payable: payable)
8487
return functionElement
8588
}
8689

8790
fileprivate func parseFallback(abiRecord: ABI.Record) throws -> ABI.Element.Fallback {
8891
let payable = (abiRecord.stateMutability == "payable" || abiRecord.payable == true)
89-
var constant = abiRecord.constant == true
90-
if (abiRecord.stateMutability == "view" || abiRecord.stateMutability == "pure") {
91-
constant = true
92-
}
92+
let constant = abiRecord.constant == true || abiRecord.stateMutability == "view" || abiRecord.stateMutability == "pure"
9393
let functionElement = ABI.Element.Fallback(constant: constant, payable: payable)
9494
return functionElement
9595
}
@@ -99,16 +99,9 @@ fileprivate func parseConstructor(abiRecord: ABI.Record) throws -> ABI.Element.C
9999
let nativeInput = try input.parse()
100100
return nativeInput
101101
})
102-
let abiInputs = inputs != nil ? inputs! : [ABI.Element.InOut]()
103-
var payable = false
104-
if (abiRecord.payable != nil) {
105-
payable = abiRecord.payable!
106-
}
107-
if (abiRecord.stateMutability == "payable") {
108-
payable = true
109-
}
110-
let constant = false
111-
let functionElement = ABI.Element.Constructor(inputs: abiInputs, constant: constant, payable: payable)
102+
let abiInputs = inputs ?? [ABI.Element.InOut]()
103+
let payable = abiRecord.stateMutability == "payable" || abiRecord.payable == true
104+
let functionElement = ABI.Element.Constructor(inputs: abiInputs, constant: false, payable: payable)
112105
return functionElement
113106
}
114107

@@ -117,9 +110,9 @@ fileprivate func parseEvent(abiRecord: ABI.Record) throws -> ABI.Element.Event {
117110
let nativeInput = try input.parseForEvent()
118111
return nativeInput
119112
})
120-
let abiInputs = inputs != nil ? inputs! : [ABI.Element.Event.Input]()
121-
let name = abiRecord.name != nil ? abiRecord.name! : ""
122-
let anonymous = abiRecord.anonymous != nil ? abiRecord.anonymous! : false
113+
let abiInputs = inputs ?? [ABI.Element.Event.Input]()
114+
let name = abiRecord.name ?? ""
115+
let anonymous = abiRecord.anonymous ?? false
123116
let functionElement = ABI.Element.Event(name: name, inputs: abiInputs, anonymous: anonymous)
124117
return functionElement
125118
}
@@ -129,21 +122,25 @@ fileprivate func parseReceive(abiRecord: ABI.Record) throws -> ABI.Element.Recei
129122
let nativeInput = try input.parse()
130123
return nativeInput
131124
})
132-
let abiInputs = inputs != nil ? inputs! : [ABI.Element.InOut]()
133-
var payable = false
134-
if (abiRecord.payable != nil) {
135-
payable = abiRecord.payable!
136-
}
137-
if (abiRecord.stateMutability == "payable") {
138-
payable = true
139-
}
125+
let abiInputs = inputs ?? [ABI.Element.InOut]()
126+
let payable = abiRecord.stateMutability == "payable" || abiRecord.payable == true
140127
let functionElement = ABI.Element.Receive(inputs: abiInputs, payable: payable)
141128
return functionElement
142129
}
143130

131+
fileprivate 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 ?? []
137+
let name = abiRecord.name ?? ""
138+
return ABI.Element.EthError(name: name, inputs: abiInputs)
139+
}
140+
144141
extension ABI.Input {
145142
func parse() throws -> ABI.Element.InOut {
146-
let name = self.name != nil ? self.name! : ""
143+
let name = self.name ?? ""
147144
let parameterType = try ABITypeParser.parseTypeString(self.type)
148145
if case .tuple(types: _) = parameterType {
149146
let components = try self.components?.compactMap({ (inp: ABI.Input) throws -> ABI.Element.ParameterType in
@@ -171,12 +168,18 @@ extension ABI.Input {
171168
}
172169
}
173170

174-
func parseForEvent() throws -> ABI.Element.Event.Input{
175-
let name = self.name != nil ? self.name! : ""
171+
func parseForEvent() throws -> ABI.Element.Event.Input {
172+
let name = self.name ?? ""
176173
let parameterType = try ABITypeParser.parseTypeString(self.type)
177174
let indexed = self.indexed == true
178175
return ABI.Element.Event.Input(name: name, type: parameterType, indexed: indexed)
179176
}
177+
178+
func parseForError() throws -> ABI.Element.EthError.Input {
179+
let name = self.name ?? ""
180+
let parameterType = try ABITypeParser.parseTypeString(self.type)
181+
return ABI.Element.EthError.Input(name:name, type: parameterType)
182+
}
180183
}
181184

182185
extension ABI.Output {

Sources/web3swift/Web3/Web3+InfuraProviders.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public enum BlockNumber {
3131
public final class InfuraProvider: Web3HttpProvider {
3232
public init?(_ net: Networks, accessToken token: String? = nil, keystoreManager manager: KeystoreManager? = nil) {
3333
var requestURLstring = "https://" + net.name + Constants.infuraHttpScheme
34-
requestURLstring += token != nil ? token! : Constants.infuraToken
34+
requestURLstring += token ?? Constants.infuraToken
3535
let providerURL = URL(string: requestURLstring)
3636
super.init(providerURL!, network: net, keystoreManager: manager)
3737
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// web3swiftDecodeSolidityErrorType.swift
3+
// Tests
4+
//
5+
// Created by JeneaVranceanu on 25/01/2022.
6+
// Copyright © 2022 web3swift. All rights reserved.
7+
//
8+
9+
import XCTest
10+
import web3swift
11+
12+
/// Since solidity 0.8.4 a new type was introduced called `error`.
13+
/// Contracts' ABI with this type were not decodable.
14+
class web3swiftDecodeSolidityErrorType: XCTestCase {
15+
16+
func testStructuredErrorTypeDecoding() throws {
17+
let contractAbiWithErrorTypes = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"disallowedAddress\",\"type\":\"address\"}],\"name\":\"NotAllowedAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"disallowedFunction\",\"type\":\"bytes4\"}],\"name\":\"NotAllowedFunction\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"permission\",\"type\":\"string\"}],\"name\":\"NotAuthorised\",\"type\":\"error\"}]"
18+
let web3Instance = try Web3.new(URL.init(string: "http://127.0.0.1:8545")!)
19+
let contract = web3.web3contract(web3: web3Instance, abiString: contractAbiWithErrorTypes)
20+
assert(contract != nil)
21+
}
22+
}

Tests/web3swiftTests/remoteTests/RemoteTests.xctestplan

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"SoliditySha3Test",
2222
"web3swiftAdvancedABIv2Tests",
2323
"web3swiftBasicLocalNodeTests",
24+
"web3swiftDecodeSolidityErrorType",
2425
"web3swiftEIP67Tests",
2526
"web3swiftEIP681Tests",
2627
"web3swiftERC20ClassTests",

web3swift.xcodeproj/project.pbxproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
5CF7E8BC276B79380009900F /* web3swiftWebsocketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF7E8B7276B79380009900F /* web3swiftWebsocketTests.swift */; };
194194
604FA4FF27ECBDC80021108F /* DataConversionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604FA4FE27ECBDC80021108F /* DataConversionTests.swift */; };
195195
CB50A52827060BD600D7E39B /* EIP712Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB50A52727060BD600D7E39B /* EIP712Tests.swift */; };
196+
D606A56B279F5D59003643ED /* web3swiftDecodeSolidityErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D606A56A279F5D59003643ED /* web3swiftDecodeSolidityErrorType.swift */; };
196197
D6A3D9B827F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */; };
197198
E22A911F241ED71A00EC1021 /* browser.min.js in Resources */ = {isa = PBXBuildFile; fileRef = E22A911E241ED71A00EC1021 /* browser.min.js */; };
198199
E2B76710241ED479007EBFE3 /* browser.js in Resources */ = {isa = PBXBuildFile; fileRef = E2B7670F241ED479007EBFE3 /* browser.js */; };
@@ -414,6 +415,7 @@
414415
604FA4FE27ECBDC80021108F /* DataConversionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataConversionTests.swift; sourceTree = "<group>"; };
415416
CB50A52727060BD600D7E39B /* EIP712Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Tests.swift; sourceTree = "<group>"; };
416417
CB50A52927060C5300D7E39B /* EIP712.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712.swift; sourceTree = "<group>"; };
418+
D606A56A279F5D59003643ED /* web3swiftDecodeSolidityErrorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = web3swiftDecodeSolidityErrorType.swift; sourceTree = "<group>"; };
417419
D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABIEncoderSoliditySha3Test.swift; sourceTree = "<group>"; };
418420
E22A911E241ED71A00EC1021 /* browser.min.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = browser.min.js; sourceTree = "<group>"; };
419421
E2B7670F241ED479007EBFE3 /* browser.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = browser.js; sourceTree = "<group>"; };
@@ -1011,6 +1013,7 @@
10111013
CB50A52727060BD600D7E39B /* EIP712Tests.swift */,
10121014
D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */,
10131015
5CDEF973275A74670004A2F2 /* LocalTests.xctestplan */,
1016+
D606A56A279F5D59003643ED /* web3swiftDecodeSolidityErrorType.swift */,
10141017
);
10151018
path = localTests;
10161019
sourceTree = "<group>";
@@ -1382,6 +1385,8 @@
13821385
isa = PBXSourcesBuildPhase;
13831386
buildActionMask = 2147483647;
13841387
files = (
1388+
CB50A52827060BD600D7E39B /* EIP712Tests.swift in Sources */,
1389+
D606A56B279F5D59003643ED /* web3swiftDecodeSolidityErrorType.swift in Sources */,
13851390
5CF7E8AB276B792A0009900F /* web3swiftTests.swift in Sources */,
13861391
5CF7E8A4276B792A0009900F /* web3swiftTransactionsTests.swift in Sources */,
13871392
5C26D8A027F3725500431EB0 /* EIP1559BlockTests.swift in Sources */,

0 commit comments

Comments
 (0)