Skip to content

Commit f1e2f10

Browse files
authored
[ADD] Offchain Read support (EIP-3668) for eth_call and ENS resolution (#208)
1 parent 4f1f893 commit f1e2f10

22 files changed

+1453
-445
lines changed

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import PackageDescription
44
let package = Package(
55
name: "web3.swift",
66
platforms: [
7-
.iOS(SupportedPlatform.IOSVersion.v11),
8-
.macOS(SupportedPlatform.MacOSVersion.v10_12)
7+
.iOS(SupportedPlatform.IOSVersion.v13),
8+
.macOS(SupportedPlatform.MacOSVersion.v11)
99
],
1010
products: [
1111
.library(name: "web3.swift", targets: ["web3"]),

web3sTests/Client/EthereumClientTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ extension EthereumClientTests {
334334
XCTAssertEqual(
335335
error as? EthereumClientError,
336336
.executionError(
337-
.init(code: -32000, message: "execution reverted")
337+
.init(code: -32000, message: "execution reverted", data: nil)
338338
)
339339
)
340340
}

web3sTests/ENS/ENSOffchainTests.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// ENSOffchainTests.swift
3+
// web3sTests
4+
//
5+
// Created by Miguel on 17/05/2022.
6+
// Copyright © 2022 Argent Labs Limited. All rights reserved.
7+
//
8+
9+
import XCTest
10+
@testable import web3
11+
12+
class ENSOffchainTests: XCTestCase {
13+
var account: EthereumAccount?
14+
var client: EthereumClient!
15+
16+
override func setUp() {
17+
super.setUp()
18+
self.client = EthereumClient(url: URL(string: TestConfig.clientUrl)!)
19+
}
20+
21+
func testDNSEncode() {
22+
XCTAssertEqual(
23+
EthereumNameService.dnsEncode(name: "offchainexample.eth").web3.hexString,
24+
"0x0f6f6666636861696e6578616d706c650365746800"
25+
)
26+
XCTAssertEqual(
27+
EthereumNameService.dnsEncode(name: "1.offchainexample.eth").web3.hexString,
28+
"0x01310f6f6666636861696e6578616d706c650365746800"
29+
)
30+
31+
}
32+
33+
func testGivenRopstenRegistry_WhenResolvingOffchainENS_ResolvesCorrectly() async {
34+
do {
35+
let nameService = EthereumNameService(client: client!)
36+
let ens = try await nameService.resolve(
37+
ens: "offchainexample.eth",
38+
mode: .allowOffchainLookup
39+
)
40+
XCTAssertEqual(EthereumAddress("0xd8da6bf26964af9d7eed9e03e53415d37aa96045"), ens)
41+
} catch {
42+
XCTFail("Expected ens but failed \(error).")
43+
}
44+
}
45+
46+
func testGivenRopstenRegistry_WhenResolvingOffchainENSAndDisabled_ThenFails() async {
47+
do {
48+
let nameService = EthereumNameService(client: client!)
49+
let _ = try await nameService.resolve(
50+
ens: "offchainexample.eth",
51+
mode: .onchain
52+
)
53+
XCTFail("Expecting error")
54+
} catch let error {
55+
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
56+
}
57+
}
58+
59+
func testGivenRopstenRegistry_WhenResolvingNonOffchainENS_ThenResolves() async {
60+
do {
61+
let nameService = EthereumNameService(client: client!)
62+
let ens = try await nameService.resolve(
63+
ens: "resolver.eth",
64+
mode: .allowOffchainLookup
65+
)
66+
XCTAssertEqual(EthereumAddress("0x42d63ae25990889e35f215bc95884039ba354115"), ens)
67+
} catch {
68+
XCTFail("Expected ens but failed \(error).")
69+
}
70+
}
71+
}
72+

web3sTests/ENS/ENSTests.swift

Lines changed: 33 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -24,184 +24,7 @@ class ENSTests: XCTestCase {
2424
XCTAssertEqual(nameHash, "0x3e58ef7a2e196baf0b9d36a65cc590ac9edafb3395b7cdeb8f39206049b4534c")
2525
}
2626

27-
func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly() {
28-
let expect = expectation(description: "Get the ENS owner")
29-
30-
do {
31-
let function = ENSContracts.ENSRegistryFunctions.owner(contract: ENSContracts.RopstenAddress, _node: EthereumNameService.nameHash(name: "test").web3.hexData ?? Data())
32-
33-
let tx = try function.transaction()
34-
35-
client?.eth_call(tx, block: .Latest, completion: { (error, dataStr) in
36-
guard let dataStr = dataStr else {
37-
XCTFail()
38-
expect.fulfill()
39-
return
40-
}
41-
let owner = String(dataStr[dataStr.index(dataStr.endIndex, offsetBy: -40)...])
42-
XCTAssertEqual(owner.web3.noHexPrefix,"09b5bd82f3351a4c8437fc6d7772a9e6cd5d25a1")
43-
expect.fulfill()
44-
})
45-
46-
} catch {
47-
XCTFail()
48-
expect.fulfill()
49-
}
50-
51-
waitForExpectations(timeout: 20)
52-
}
53-
54-
func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly() {
55-
let expect = expectation(description: "Get the ENS address")
56-
57-
let nameService = EthereumNameService(client: client!)
58-
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), completion: { (error, ens) in
59-
XCTAssertEqual("julien.argent.test", ens)
60-
expect.fulfill()
61-
})
62-
63-
waitForExpectations(timeout: 20)
64-
}
65-
66-
func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly() {
67-
let expect = expectation(description: "Get the ENS address")
68-
69-
let nameService = EthereumNameService(client: client!)
70-
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"), completion: { (error, ens) in
71-
XCTAssertNil(ens)
72-
XCTAssertEqual(error, .ensUnknown)
73-
expect.fulfill()
74-
})
75-
76-
waitForExpectations(timeout: 20)
77-
}
78-
79-
func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly() {
80-
let expect = expectation(description: "Get the ENS address")
81-
82-
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
83-
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"), completion: { (error, ens) in
84-
XCTAssertNil(ens)
85-
XCTAssertEqual(error, .ensUnknown)
86-
expect.fulfill()
87-
})
88-
89-
waitForExpectations(timeout: 20)
90-
}
91-
92-
func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly() {
93-
let expect = expectation(description: "Get the ENS reverse lookup address")
94-
95-
let nameService = EthereumNameService(client: client!)
96-
nameService.resolve(ens: "julien.argent.test", completion: { (error, ens) in
97-
XCTAssertEqual(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), ens)
98-
expect.fulfill()
99-
})
100-
101-
waitForExpectations(timeout: 20)
102-
}
103-
104-
func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest() {
105-
let expect = expectation(description: "Get the ENS reverse lookup address")
106-
107-
let nameService = EthereumNameService(client: client!)
108-
nameService.resolve(ens: "**somegarbage)_!!", completion: { (error, ens) in
109-
XCTAssertNil(ens)
110-
XCTAssertEqual(error, .ensUnknown)
111-
expect.fulfill()
112-
})
113-
114-
waitForExpectations(timeout: 20)
115-
}
116-
117-
func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest() {
118-
let expect = expectation(description: "Get the ENS reverse lookup address")
119-
120-
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
121-
nameService.resolve(ens: "**somegarbage)_!!", completion: { (error, ens) in
122-
XCTAssertNil(ens)
123-
XCTAssertEqual(error, .ensUnknown)
124-
expect.fulfill()
125-
})
126-
127-
waitForExpectations(timeout: 20)
128-
}
129-
130-
func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall() {
131-
let expect = expectation(description: "Get the ENS reverse lookup address")
132-
133-
let nameService = EthereumNameService(client: client!)
134-
135-
var results: [EthereumNameService.ResolveOutput<String>]?
136-
137-
nameService.resolve(addresses: [
138-
EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"),
139-
EthereumAddress("0x09b5bd82f3351a4c8437fc6d7772a9e6cd5d25a1"),
140-
EthereumAddress("0x7e691d7ffb007abe91d8a24d7f22fc74307dab06")
141-
142-
]) { result in
143-
switch result {
144-
case .success(let resolutions):
145-
results = resolutions.map { $0.output }
146-
case .failure:
147-
break
148-
}
149-
expect.fulfill()
150-
}
151-
152-
waitForExpectations(timeout: 5)
153-
154-
XCTAssertEqual(
155-
results,
156-
[
157-
.resolved("julien.argent.test"),
158-
.couldNotBeResolved(.ensUnknown),
159-
.resolved("davidtests.argent.xyz")
160-
]
161-
)
162-
}
163-
164-
func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall() {
165-
let expect = expectation(description: "Get the ENS reverse lookup address")
166-
167-
let nameService = EthereumNameService(client: client!)
168-
169-
var results: [EthereumNameService.ResolveOutput<EthereumAddress>]?
170-
171-
nameService.resolve(names: [
172-
"julien.argent.test",
173-
"davidtests.argent.xyz",
174-
"somefakeens.argent.xyz"
175-
176-
]) { result in
177-
switch result {
178-
case .success(let resolutions):
179-
results = resolutions.map { $0.output }
180-
case .failure:
181-
break
182-
}
183-
expect.fulfill()
184-
}
185-
186-
waitForExpectations(timeout: 5)
187-
188-
XCTAssertEqual(
189-
results,
190-
[
191-
.resolved(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8")),
192-
.resolved(EthereumAddress("0x7e691d7ffb007abe91d8a24d7f22fc74307dab06")),
193-
.couldNotBeResolved(.ensUnknown)
194-
]
195-
)
196-
}
197-
}
198-
199-
200-
#if compiler(>=5.5) && canImport(_Concurrency)
201-
202-
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
203-
extension ENSTests {
204-
func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly_Async() async {
27+
func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly() async {
20528
do {
20629
let function = ENSContracts.ENSRegistryFunctions.owner(contract: ENSContracts.RopstenAddress, _node: EthereumNameService.nameHash(name: "test").web3.hexData ?? Data())
20730

@@ -220,67 +43,85 @@ extension ENSTests {
22043
}
22144
}
22245

223-
func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly_Async() async {
46+
func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly() async {
22447
do {
22548
let nameService = EthereumNameService(client: client!)
226-
let ens = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"))
49+
let ens = try await nameService.resolve(
50+
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"),
51+
mode: .onchain
52+
)
22753
XCTAssertEqual("julien.argent.test", ens)
22854
} catch {
22955
XCTFail("Expected ens but failed \(error).")
23056
}
23157
}
23258

233-
func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly_Async() async {
59+
func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly() async {
23460
do {
23561
let nameService = EthereumNameService(client: client!)
236-
_ = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"))
62+
_ = try await nameService.resolve(
63+
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"),
64+
mode: .onchain
65+
)
23766
XCTFail("Expected to throw while awaiting, but succeeded")
23867
} catch {
23968
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
24069
}
24170
}
24271

243-
func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly_Async() async {
72+
func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly() async {
24473
do {
24574
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
246-
_ = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"))
75+
_ = try await nameService.resolve(
76+
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"),
77+
mode: .onchain
78+
)
24779
XCTFail("Expected to throw while awaiting, but succeeded")
24880
} catch {
24981
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
25082
}
25183
}
25284

253-
func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly_Async() async {
85+
func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly() async {
25486
do {
25587
let nameService = EthereumNameService(client: client!)
256-
let ens = try await nameService.resolve(ens: "julien.argent.test")
88+
let ens = try await nameService.resolve(
89+
ens: "julien.argent.test",
90+
mode: .onchain
91+
)
25792
XCTAssertEqual(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), ens)
25893
} catch {
25994
XCTFail("Expected ens but failed \(error).")
26095
}
26196
}
26297

263-
func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest_Async() async {
98+
func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest() async {
26499
do {
265100
let nameService = EthereumNameService(client: client!)
266-
_ = try await nameService.resolve(ens: "**somegarbage)_!!")
101+
_ = try await nameService.resolve(
102+
ens: "**somegarbage)_!!",
103+
mode: .onchain
104+
)
267105
XCTFail("Expected to throw while awaiting, but succeeded")
268106
} catch {
269107
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
270108
}
271109
}
272110

273-
func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest_Async() async {
111+
func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest() async {
274112
do {
275113
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
276-
_ = try await nameService.resolve(ens: "**somegarbage)_!!")
114+
_ = try await nameService.resolve(
115+
ens: "**somegarbage)_!!",
116+
mode: .onchain
117+
)
277118
XCTFail("Expected to throw while awaiting, but succeeded")
278119
} catch {
279120
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
280121
}
281122
}
282123

283-
func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall_Async() async {
124+
func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall() async {
284125
let nameService = EthereumNameService(client: client!)
285126

286127
var results: [EthereumNameService.ResolveOutput<String>]?
@@ -308,7 +149,7 @@ extension ENSTests {
308149
)
309150
}
310151

311-
func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall_Async() async {
152+
func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall() async {
312153
let nameService = EthereumNameService(client: client!)
313154

314155
var results: [EthereumNameService.ResolveOutput<EthereumAddress>]?
@@ -337,4 +178,3 @@ extension ENSTests {
337178
}
338179
}
339180

340-
#endif

0 commit comments

Comments
 (0)