resolve ens back and forth

This commit is contained in:
ivan grachev 2023-11-14 19:26:21 +03:00
parent f46514617e
commit 2fffa50d17

View File

@ -24,36 +24,40 @@ class EthereumRPC {
private let queue = DispatchQueue(label: "EthereumRPC")
private let urlSession = URLSession(configuration: .default)
private let ensResolver = "0x231b0ee14048e9dccd1d247744d114a4eb5e8e63"
func fetchGasPrice(rpcUrl: String, completion: @escaping (Result<String, Error>) -> Void) {
request(method: "eth_gasPrice", params: [], rpcUrl: rpcUrl, completion: completion)
}
func resolveENS(rpcUrl: String, address: String, completion: @escaping (Result<String, Error>) -> Void) {
let reverseRecord = "\(address.lowercased().cleanHex).addr.reverse"
var node = Data(repeating: 0, count: 32)
if !reverseRecord.isEmpty {
node = reverseRecord.split(separator: ".").reversed().reduce(node) { (currentNode, label) in
var node = currentNode
guard let data = label.data(using: .utf8) else { return Data() }
node.append(Hash.keccak256(data: data))
return Hash.keccak256(data: node)
unsafeResolveENS(rpcUrl: rpcUrl, address: address) { [weak self] result in
switch result {
case .success(let success):
self?.resolveAddress(rpcUrl: rpcUrl, ens: success) { forwardResult in
if case let .success(forward) = forwardResult, forward == address.cleanHex.lowercased() {
completion(result)
} else {
completion(.failure(EthereumRPCError.unknown))
}
}
case .failure:
completion(result)
}
}
let nameHash = node.hexString
let data = "0x691f3431" + String(repeating: "0", count: 64 - nameHash.count) + nameHash
let method = "eth_call"
}
private func unsafeResolveENS(rpcUrl: String, address: String, completion: @escaping (Result<String, Error>) -> Void) {
let reverseRecord = "\(address.lowercased().cleanHex).addr.reverse"
let nameHash = namehash(reverseRecord).hexString
let params: [Any] = [
[
"to": "0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63",
"data": data
"to": ensResolver,
"data": "0x691f3431" + String(repeating: "0", count: 64 - nameHash.count) + nameHash
],
"latest"
]
request(method: method, params: params, rpcUrl: rpcUrl) { result in
request(method: "eth_call", params: params, rpcUrl: rpcUrl) { result in
switch result {
case .success(let success):
if let data = Data(hexString: String(success.cleanHex.dropFirst(64))),
@ -68,6 +72,29 @@ class EthereumRPC {
}
}
func resolveAddress(rpcUrl: String, ens: String, completion: @escaping (Result<String, Error>) -> Void) {
let params: [Any] = [
[
"to": ensResolver,
"data": "0x3b3b57de" + namehash(ens).hexString
],
"latest"
]
request(method: "eth_call", params: params, rpcUrl: rpcUrl) { result in
switch result {
case .success(let response):
let address = String(response.suffix(40))
if address.contains(where: { $0 != "0" }) {
completion(.success(address))
} else {
completion(.failure(EthereumRPCError.unknown))
}
case .failure(let error):
completion(.failure(error))
}
}
}
func getBalance(rpcUrl: String, for address: String, completion: @escaping (Result<String, Error>) -> Void) {
request(method: "eth_getBalance", params: [address, "pending"], rpcUrl: rpcUrl, completion: completion)
}
@ -116,8 +143,20 @@ class EthereumRPC {
completion(.failure(EthereumRPCError.unknown))
}
}
task.resume()
}
private func namehash(_ name: String) -> Data {
var node = Data(repeating: 0, count: 32)
if !name.isEmpty {
name.split(separator: ".").reversed().forEach { label in
guard let data = label.data(using: .utf8) else { return }
node.append(Hash.keccak256(data: data))
node = Hash.keccak256(data: node)
}
}
return node
}
}