Skip to content

Commit

Permalink
[ADD] Offchain Read support (EIP-3668) for eth_call and ENS resolution (
Browse files Browse the repository at this point in the history
  • Loading branch information
DarthMike authored May 18, 2022
1 parent 4f1f893 commit f1e2f10
Show file tree
Hide file tree
Showing 22 changed files with 1,453 additions and 445 deletions.
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import PackageDescription
let package = Package(
name: "web3.swift",
platforms: [
.iOS(SupportedPlatform.IOSVersion.v11),
.macOS(SupportedPlatform.MacOSVersion.v10_12)
.iOS(SupportedPlatform.IOSVersion.v13),
.macOS(SupportedPlatform.MacOSVersion.v11)
],
products: [
.library(name: "web3.swift", targets: ["web3"]),
Expand Down
2 changes: 1 addition & 1 deletion web3sTests/Client/EthereumClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ extension EthereumClientTests {
XCTAssertEqual(
error as? EthereumClientError,
.executionError(
.init(code: -32000, message: "execution reverted")
.init(code: -32000, message: "execution reverted", data: nil)
)
)
}
Expand Down
72 changes: 72 additions & 0 deletions web3sTests/ENS/ENSOffchainTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// ENSOffchainTests.swift
// web3sTests
//
// Created by Miguel on 17/05/2022.
// Copyright © 2022 Argent Labs Limited. All rights reserved.
//

import XCTest
@testable import web3

class ENSOffchainTests: XCTestCase {
var account: EthereumAccount?
var client: EthereumClient!

override func setUp() {
super.setUp()
self.client = EthereumClient(url: URL(string: TestConfig.clientUrl)!)
}

func testDNSEncode() {
XCTAssertEqual(
EthereumNameService.dnsEncode(name: "offchainexample.eth").web3.hexString,
"0x0f6f6666636861696e6578616d706c650365746800"
)
XCTAssertEqual(
EthereumNameService.dnsEncode(name: "1.offchainexample.eth").web3.hexString,
"0x01310f6f6666636861696e6578616d706c650365746800"
)

}

func testGivenRopstenRegistry_WhenResolvingOffchainENS_ResolvesCorrectly() async {
do {
let nameService = EthereumNameService(client: client!)
let ens = try await nameService.resolve(
ens: "offchainexample.eth",
mode: .allowOffchainLookup
)
XCTAssertEqual(EthereumAddress("0xd8da6bf26964af9d7eed9e03e53415d37aa96045"), ens)
} catch {
XCTFail("Expected ens but failed \(error).")
}
}

func testGivenRopstenRegistry_WhenResolvingOffchainENSAndDisabled_ThenFails() async {
do {
let nameService = EthereumNameService(client: client!)
let _ = try await nameService.resolve(
ens: "offchainexample.eth",
mode: .onchain
)
XCTFail("Expecting error")
} catch let error {
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
}
}

func testGivenRopstenRegistry_WhenResolvingNonOffchainENS_ThenResolves() async {
do {
let nameService = EthereumNameService(client: client!)
let ens = try await nameService.resolve(
ens: "resolver.eth",
mode: .allowOffchainLookup
)
XCTAssertEqual(EthereumAddress("0x42d63ae25990889e35f215bc95884039ba354115"), ens)
} catch {
XCTFail("Expected ens but failed \(error).")
}
}
}

226 changes: 33 additions & 193 deletions web3sTests/ENS/ENSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,184 +24,7 @@ class ENSTests: XCTestCase {
XCTAssertEqual(nameHash, "0x3e58ef7a2e196baf0b9d36a65cc590ac9edafb3395b7cdeb8f39206049b4534c")
}

func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly() {
let expect = expectation(description: "Get the ENS owner")

do {
let function = ENSContracts.ENSRegistryFunctions.owner(contract: ENSContracts.RopstenAddress, _node: EthereumNameService.nameHash(name: "test").web3.hexData ?? Data())

let tx = try function.transaction()

client?.eth_call(tx, block: .Latest, completion: { (error, dataStr) in
guard let dataStr = dataStr else {
XCTFail()
expect.fulfill()
return
}
let owner = String(dataStr[dataStr.index(dataStr.endIndex, offsetBy: -40)...])
XCTAssertEqual(owner.web3.noHexPrefix,"09b5bd82f3351a4c8437fc6d7772a9e6cd5d25a1")
expect.fulfill()
})

} catch {
XCTFail()
expect.fulfill()
}

waitForExpectations(timeout: 20)
}

func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly() {
let expect = expectation(description: "Get the ENS address")

let nameService = EthereumNameService(client: client!)
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), completion: { (error, ens) in
XCTAssertEqual("julien.argent.test", ens)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly() {
let expect = expectation(description: "Get the ENS address")

let nameService = EthereumNameService(client: client!)
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"), completion: { (error, ens) in
XCTAssertNil(ens)
XCTAssertEqual(error, .ensUnknown)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly() {
let expect = expectation(description: "Get the ENS address")

let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"), completion: { (error, ens) in
XCTAssertNil(ens)
XCTAssertEqual(error, .ensUnknown)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly() {
let expect = expectation(description: "Get the ENS reverse lookup address")

let nameService = EthereumNameService(client: client!)
nameService.resolve(ens: "julien.argent.test", completion: { (error, ens) in
XCTAssertEqual(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), ens)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest() {
let expect = expectation(description: "Get the ENS reverse lookup address")

let nameService = EthereumNameService(client: client!)
nameService.resolve(ens: "**somegarbage)_!!", completion: { (error, ens) in
XCTAssertNil(ens)
XCTAssertEqual(error, .ensUnknown)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest() {
let expect = expectation(description: "Get the ENS reverse lookup address")

let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
nameService.resolve(ens: "**somegarbage)_!!", completion: { (error, ens) in
XCTAssertNil(ens)
XCTAssertEqual(error, .ensUnknown)
expect.fulfill()
})

waitForExpectations(timeout: 20)
}

func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall() {
let expect = expectation(description: "Get the ENS reverse lookup address")

let nameService = EthereumNameService(client: client!)

var results: [EthereumNameService.ResolveOutput<String>]?

nameService.resolve(addresses: [
EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"),
EthereumAddress("0x09b5bd82f3351a4c8437fc6d7772a9e6cd5d25a1"),
EthereumAddress("0x7e691d7ffb007abe91d8a24d7f22fc74307dab06")

]) { result in
switch result {
case .success(let resolutions):
results = resolutions.map { $0.output }
case .failure:
break
}
expect.fulfill()
}

waitForExpectations(timeout: 5)

XCTAssertEqual(
results,
[
.resolved("julien.argent.test"),
.couldNotBeResolved(.ensUnknown),
.resolved("davidtests.argent.xyz")
]
)
}

func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall() {
let expect = expectation(description: "Get the ENS reverse lookup address")

let nameService = EthereumNameService(client: client!)

var results: [EthereumNameService.ResolveOutput<EthereumAddress>]?

nameService.resolve(names: [
"julien.argent.test",
"davidtests.argent.xyz",
"somefakeens.argent.xyz"

]) { result in
switch result {
case .success(let resolutions):
results = resolutions.map { $0.output }
case .failure:
break
}
expect.fulfill()
}

waitForExpectations(timeout: 5)

XCTAssertEqual(
results,
[
.resolved(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8")),
.resolved(EthereumAddress("0x7e691d7ffb007abe91d8a24d7f22fc74307dab06")),
.couldNotBeResolved(.ensUnknown)
]
)
}
}


#if compiler(>=5.5) && canImport(_Concurrency)

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
extension ENSTests {
func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly_Async() async {
func testGivenRopstenRegistry_WhenExistingDomainName_ResolvesOwnerAddressCorrectly() async {
do {
let function = ENSContracts.ENSRegistryFunctions.owner(contract: ENSContracts.RopstenAddress, _node: EthereumNameService.nameHash(name: "test").web3.hexData ?? Data())

Expand All @@ -220,67 +43,85 @@ extension ENSTests {
}
}

func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly_Async() async {
func testGivenRopstenRegistry_WhenExistingAddress_ThenResolvesCorrectly() async {
do {
let nameService = EthereumNameService(client: client!)
let ens = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"))
let ens = try await nameService.resolve(
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"),
mode: .onchain
)
XCTAssertEqual("julien.argent.test", ens)
} catch {
XCTFail("Expected ens but failed \(error).")
}
}

func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly_Async() async {
func testGivenRopstenRegistry_WhenNotExistingAddress_ThenFailsCorrectly() async {
do {
let nameService = EthereumNameService(client: client!)
_ = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"))
_ = try await nameService.resolve(
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"),
mode: .onchain
)
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
}
}

func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly_Async() async {
func testGivenCustomRegistry_WhenNotExistingAddress_ThenResolvesFailsCorrectly() async {
do {
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
_ = try await nameService.resolve(address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"))
_ = try await nameService.resolve(
address: EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c9"),
mode: .onchain
)
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
}
}

func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly_Async() async {
func testGivenRopstenRegistry_WhenExistingENS_ThenResolvesAddressCorrectly() async {
do {
let nameService = EthereumNameService(client: client!)
let ens = try await nameService.resolve(ens: "julien.argent.test")
let ens = try await nameService.resolve(
ens: "julien.argent.test",
mode: .onchain
)
XCTAssertEqual(EthereumAddress("0xb0b874220ff95d62a676f58d186c832b3e6529c8"), ens)
} catch {
XCTFail("Expected ens but failed \(error).")
}
}

func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest_Async() async {
func testGivenRopstenRegistry_WhenInvalidENS_ThenErrorsRequest() async {
do {
let nameService = EthereumNameService(client: client!)
_ = try await nameService.resolve(ens: "**somegarbage)_!!")
_ = try await nameService.resolve(
ens: "**somegarbage)_!!",
mode: .onchain
)
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
}
}

func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest_Async() async {
func testGivenCustomRegistry_WhenInvalidENS_ThenErrorsRequest() async {
do {
let nameService = EthereumNameService(client: client!, registryAddress: EthereumAddress("0x7D7C04B7A05539a92541105806e0971E45969F85"))
_ = try await nameService.resolve(ens: "**somegarbage)_!!")
_ = try await nameService.resolve(
ens: "**somegarbage)_!!",
mode: .onchain
)
XCTFail("Expected to throw while awaiting, but succeeded")
} catch {
XCTAssertEqual(error as? EthereumNameServiceError, .ensUnknown)
}
}

func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall_Async() async {
func testGivenRopstenRegistry_ThenResolvesMultipleAddressesInOneCall() async {
let nameService = EthereumNameService(client: client!)

var results: [EthereumNameService.ResolveOutput<String>]?
Expand Down Expand Up @@ -308,7 +149,7 @@ extension ENSTests {
)
}

func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall_Async() async {
func testGivenRopstenRegistry_ThenResolvesMultipleNamesInOneCall() async {
let nameService = EthereumNameService(client: client!)

var results: [EthereumNameService.ResolveOutput<EthereumAddress>]?
Expand Down Expand Up @@ -337,4 +178,3 @@ extension ENSTests {
}
}

#endif
Loading

0 comments on commit f1e2f10

Please sign in to comment.