feat: use ens API
This commit is contained in:
parent
1049b37c49
commit
b507813587
226
status/ens.nim
226
status/ens.nim
|
@ -15,6 +15,8 @@ import ./statusgo_backend/eth as eth
|
|||
import ./statusgo_backend/wallet
|
||||
import ./statusgo_backend/accounts as status_accounts
|
||||
import ./statusgo_backend/settings as status_settings
|
||||
import ./statusgo_backend_new/ens as status_ens
|
||||
|
||||
import ./types/[transaction, setting, rpc_response, network_type, network, profile]
|
||||
import ./utils
|
||||
import ./transactions
|
||||
|
@ -49,6 +51,45 @@ proc userNameOrAlias*(contact: Profile, removeSuffix: bool = false): string =
|
|||
else:
|
||||
result = contact.alias
|
||||
|
||||
proc resolver*(username: string): string =
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.resolver(chainId, username)
|
||||
return res.result.getStr
|
||||
|
||||
proc owner*(username: string): string =
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.ownerOf(chainId, username)
|
||||
let address = res.result.getStr
|
||||
if address == "0x0000000000000000000000000000000000000000":
|
||||
return ""
|
||||
|
||||
return address
|
||||
|
||||
proc pubkey*(username: string): string =
|
||||
try:
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.publicKeyOf(chainId, addDomain(username))
|
||||
var key = res.result.getStr
|
||||
key.removePrefix("0x")
|
||||
return "0x04" & key
|
||||
except:
|
||||
return ""
|
||||
|
||||
proc address*(username: string): string =
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.addressOf(chainId, username)
|
||||
return res.result.getStr
|
||||
|
||||
proc contenthash*(username: string): string =
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.contentHash(chainId, username)
|
||||
return res.result.getStr
|
||||
|
||||
proc getPrice*(): Stuint[256] =
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.price(chainId)
|
||||
return fromHex(Stuint[256], res.result.getStr)
|
||||
|
||||
proc label*(username:string): string =
|
||||
var node:array[32, byte] = keccak_256.digest(username.toLower()).data
|
||||
result = "0x" & node.toHex()
|
||||
|
@ -68,110 +109,6 @@ proc namehash*(ensName:string): string =
|
|||
|
||||
result = "0x" & node.toHex()
|
||||
|
||||
const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
||||
const resolver_signature = "0x0178b8bf"
|
||||
proc resolver*(usernameHash: string): string =
|
||||
let payload = %* [{
|
||||
"to": registry,
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"data": fmt"{resolver_signature}{userNameHash}"
|
||||
}, "latest"]
|
||||
let response = eth.call(payload)
|
||||
# TODO: error handling
|
||||
var resolverAddr = response.result
|
||||
resolverAddr.removePrefix("0x000000000000000000000000")
|
||||
result = "0x" & resolverAddr
|
||||
|
||||
const owner_signature = "0x02571be3" # owner(bytes32 node)
|
||||
proc owner*(username: string): string =
|
||||
var userNameHash = namehash(addDomain(username))
|
||||
userNameHash.removePrefix("0x")
|
||||
let payload = %* [{
|
||||
"to": registry,
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"data": fmt"{owner_signature}{userNameHash}"
|
||||
}, "latest"]
|
||||
let response = eth.call(payload)
|
||||
# TODO: error handling
|
||||
let ownerAddr = response.result
|
||||
if ownerAddr == "0x0000000000000000000000000000000000000000000000000000000000000000":
|
||||
return ""
|
||||
result = "0x" & ownerAddr.substr(26)
|
||||
|
||||
const pubkey_signature = "0xc8690233" # pubkey(bytes32 node)
|
||||
proc pubkey*(username: string): string =
|
||||
var userNameHash = namehash(addDomain(username))
|
||||
userNameHash.removePrefix("0x")
|
||||
let ensResolver = resolver(userNameHash)
|
||||
let payload = %* [{
|
||||
"to": ensResolver,
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"data": fmt"{pubkey_signature}{userNameHash}"
|
||||
}, "latest"]
|
||||
let response = eth.call(payload)
|
||||
# TODO: error handling
|
||||
var pubkey = response.result
|
||||
if pubkey == "0x" or pubkey == "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":
|
||||
result = ""
|
||||
else:
|
||||
pubkey.removePrefix("0x")
|
||||
result = "0x04" & pubkey
|
||||
|
||||
const address_signature = "0x3b3b57de" # addr(bytes32 node)
|
||||
proc address*(username: string): string =
|
||||
var userNameHash = namehash(addDomain(username))
|
||||
userNameHash.removePrefix("0x")
|
||||
let ensResolver = resolver(userNameHash)
|
||||
let payload = %* [{
|
||||
"to": ensResolver,
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"data": fmt"{address_signature}{userNameHash}"
|
||||
}, "latest"]
|
||||
let response = eth.call(payload)
|
||||
# TODO: error handling
|
||||
let address = response.result
|
||||
if address == "0x0000000000000000000000000000000000000000000000000000000000000000":
|
||||
return ""
|
||||
result = "0x" & address.substr(26)
|
||||
|
||||
const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32)
|
||||
proc contenthash*(ensAddr: string): string =
|
||||
var ensHash = namehash(ensAddr)
|
||||
ensHash.removePrefix("0x")
|
||||
let ensResolver = resolver(ensHash)
|
||||
let payload = %* [{
|
||||
"to": ensResolver,
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"data": fmt"{contenthash_signature}{ensHash}"
|
||||
}, "latest"]
|
||||
|
||||
let response = eth.call(payload)
|
||||
let bytesResponse = response.result
|
||||
if bytesResponse == "0x":
|
||||
return ""
|
||||
|
||||
let size = fromHex(Stuint[256], bytesResponse[66..129]).truncate(int)
|
||||
result = bytesResponse[130..129+size*2]
|
||||
result = bytesResponse[130..129+size*2]
|
||||
|
||||
|
||||
proc getPrice*(): Stuint[256] =
|
||||
let
|
||||
network = status_settings.getCurrentNetwork().toNetwork()
|
||||
contract = contracts.findContract(network.chainId, "ens-usernames")
|
||||
payload = %* [{
|
||||
"to": $contract.address,
|
||||
"data": contract.methods["getPrice"].encodeAbi()
|
||||
}, "latest"]
|
||||
|
||||
let response = eth.call(payload)
|
||||
if not response.error.isNil:
|
||||
raise newException(RpcException, "Error getting ens username price: " & response.error.message)
|
||||
if response.result == "0x":
|
||||
raise newException(RpcException, "Error getting ens username price: 0x")
|
||||
result = fromHex(Stuint[256], response.result)
|
||||
result = fromHex(Stuint[256], response.result)
|
||||
|
||||
proc releaseEstimateGas*(username: string, address: string, success: var bool): int =
|
||||
let
|
||||
label = fromHex(FixedBytes[32], label(username))
|
||||
|
@ -184,8 +121,7 @@ proc releaseEstimateGas*(username: string, address: string, success: var bool):
|
|||
let response = ensUsernamesContract.methods["release"].estimateGas(tx, release, success)
|
||||
if success:
|
||||
result = fromHex[int](response)
|
||||
except RpcException as e:
|
||||
error "Could not estimate gas for ens release", err=e.msg
|
||||
except rpc_response.RpcException as e:
|
||||
error "Could not estimate gas for ens release", err=e.msg
|
||||
|
||||
proc release*(username: string, address: string, gas, gasPrice, password: string, success: var bool): string =
|
||||
|
@ -200,30 +136,13 @@ proc release*(username: string, address: string, gas, gasPrice, password: strin
|
|||
result = ensUsernamesContract.methods["release"].send(tx, release, password, success)
|
||||
if success:
|
||||
trackPendingTransaction(result, address, $ensUsernamesContract.address, PendingTransactionType.ReleaseENS, username)
|
||||
except RpcException as e:
|
||||
except rpc_response.RpcException as e:
|
||||
error "Could not estimate gas for ens release", err=e.msg
|
||||
|
||||
|
||||
proc getExpirationTime*(username: string, success: var bool): int =
|
||||
let
|
||||
label = fromHex(FixedBytes[32], label(username))
|
||||
expTime = ExpirationTime(label: label)
|
||||
network = status_settings.getCurrentNetwork().toNetwork()
|
||||
ensUsernamesContract = contracts.findContract(network.chainId, "ens-usernames")
|
||||
|
||||
var tx = transactions.buildTransaction(parseAddress("0x0000000000000000000000000000000000000000"), 0.u256)
|
||||
tx.to = ensUsernamesContract.address.some
|
||||
tx.data = ensUsernamesContract.methods["getExpirationTime"].encodeAbi(expTime)
|
||||
var response = ""
|
||||
try:
|
||||
response = eth.call(tx).result
|
||||
success = true
|
||||
except RpcException as e:
|
||||
success = false
|
||||
error "Error obtaining expiration time", err=e.msg
|
||||
|
||||
if success:
|
||||
result = fromHex[int](response)
|
||||
let chainId = status_settings.getCurrentNetwork().toChainId()
|
||||
let res = status_ens.expireAt(chainId, username)
|
||||
return fromHex[int](res.result.getStr)
|
||||
|
||||
proc extractCoordinates*(pubkey: string):tuple[x: string, y:string] =
|
||||
result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131])
|
||||
|
@ -277,7 +196,6 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su
|
|||
var hash = namehash(username)
|
||||
hash.removePrefix("0x")
|
||||
|
||||
|
||||
let
|
||||
label = fromHex(FixedBytes[32], "0x" & hash)
|
||||
x = fromHex(FixedBytes[32], "0x" & pubkey[4..67])
|
||||
|
@ -293,7 +211,7 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su
|
|||
let response = resolverContract.methods["setPubkey"].estimateGas(tx, setPubkey, success)
|
||||
if success:
|
||||
result = fromHex[int](response)
|
||||
except RpcException as e:
|
||||
except rpc_response.RpcException as e:
|
||||
raise
|
||||
|
||||
proc setPubKey*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
|
||||
|
@ -315,7 +233,7 @@ proc setPubKey*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabl
|
|||
result = resolverContract.methods["setPubkey"].send(tx, setPubkey, password, success)
|
||||
if success:
|
||||
trackPendingTransaction(result, $address, resolverAddress, PendingTransactionType.SetPubKey, username)
|
||||
except RpcException as e:
|
||||
except rpc_response.RpcException as e:
|
||||
raise
|
||||
|
||||
proc statusRegistrarAddress*():string =
|
||||
|
@ -325,54 +243,6 @@ proc statusRegistrarAddress*():string =
|
|||
return $contract.address
|
||||
result = ""
|
||||
|
||||
type
|
||||
ENSType* {.pure.} = enum
|
||||
IPFS,
|
||||
SWARM,
|
||||
IPNS,
|
||||
UNKNOWN
|
||||
|
||||
proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: string] =
|
||||
if value == "":
|
||||
return (ENSType.UNKNOWN, "")
|
||||
|
||||
if value[0..5] == "e40101":
|
||||
return (ENSType.SWARM, value.split("1b20")[1])
|
||||
|
||||
if value[0..7] == "e3010170":
|
||||
try:
|
||||
let defaultCodec = parseHexInt("70") #dag-pb
|
||||
var codec = defaultCodec # no codec specified
|
||||
var codecStartIdx = 2 # idx of where codec would start if it was specified
|
||||
# handle the case when starts with 0xe30170 instead of 0xe3010170
|
||||
if value[2..5] == "0101":
|
||||
codecStartIdx = 6
|
||||
codec = parseHexInt(value[6..7])
|
||||
elif value[2..3] == "01" and value[4..5] != "12":
|
||||
codecStartIdx = 4
|
||||
codec = parseHexInt(value[4..5])
|
||||
|
||||
# strip the info we no longer need
|
||||
var multiHashStr = value[codecStartIdx + 2..<value.len]
|
||||
|
||||
# The rest of the hash identifies the multihash algo, length, and digest
|
||||
# More info: https://multiformats.io/multihash/
|
||||
# 12 = identifies sha2-256 hash
|
||||
# 20 = multihash length = 32
|
||||
# ...rest = multihash digest
|
||||
let
|
||||
multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
|
||||
decoded = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get()
|
||||
return (ENSType.IPFS, $decoded)
|
||||
except Exception as e:
|
||||
error "Error decoding ENS contenthash", hash=value, exception=e.msg
|
||||
raise
|
||||
|
||||
if value[0..8] == "e50101700":
|
||||
return (ENSType.IPNS, parseHexStr(value[12..value.len-1]))
|
||||
|
||||
return (ENSType.UNKNOWN, "")
|
||||
|
||||
proc validateEnsName*(ens: string, isStatus: bool, usernames: seq[string]): string =
|
||||
var username = ens & (if(isStatus): domain else: "")
|
||||
result = ""
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import ens, wallet, permissions, utils
|
||||
import ../eventemitter
|
||||
import ./types/[setting, permission]
|
||||
import utils
|
||||
import statusgo_backend/accounts
|
||||
import statusgo_backend/core
|
||||
import statusgo_backend/settings as status_settings
|
||||
import json, json_serialization, sets, strutils
|
||||
import chronicles
|
||||
import stew/byteutils
|
||||
from stew/base32 import nil
|
||||
from stew/base58 import nil
|
||||
|
||||
const HTTPS_SCHEME* = "https"
|
||||
const IPFS_GATEWAY* = ".infura.status.im"
|
||||
const SWARM_GATEWAY* = "swarm-gateways.net"
|
||||
|
||||
logScope:
|
||||
topics = "provider-model"
|
||||
|
||||
type ProviderModel* = ref object
|
||||
events*: EventEmitter
|
||||
permissions*: PermissionsModel
|
||||
wallet*: WalletModel
|
||||
|
||||
proc newProviderModel*(events: EventEmitter, permissions: PermissionsModel, wallet: WalletModel): ProviderModel =
|
||||
result = ProviderModel()
|
||||
result.events = events
|
||||
result.permissions = permissions
|
||||
result.wallet = wallet
|
||||
|
||||
proc ensResourceURL*(self: ProviderModel, ens: string, url: string):
|
||||
(string, string, string, string, bool) =
|
||||
|
||||
let contentHash = contenthash(ens)
|
||||
if contentHash == "": # ENS does not have a content hash
|
||||
return (url, url, HTTPS_SCHEME, "", false)
|
||||
|
||||
let decodedHash = contentHash.decodeENSContentHash()
|
||||
|
||||
case decodedHash[0]:
|
||||
of ENSType.IPFS:
|
||||
let
|
||||
base58bytes = base58.decode(base58.BTCBase58, decodedHash[1])
|
||||
base32Hash = base32.encode(base32.Base32Lower, base58bytes)
|
||||
|
||||
result = (url, base32Hash & IPFS_GATEWAY, HTTPS_SCHEME, "", true)
|
||||
|
||||
of ENSType.SWARM:
|
||||
result = (url, SWARM_GATEWAY, HTTPS_SCHEME,
|
||||
"/bzz:/" & decodedHash[1] & "/", true)
|
||||
|
||||
of ENSType.IPNS:
|
||||
result = (url, decodedHash[1], HTTPS_SCHEME, "", true)
|
||||
|
||||
else:
|
||||
warn "Unknown content for", ens, contentHash
|
|
@ -24,7 +24,6 @@ proc callPrivateRPC*(methodName: string, payload = %* []): RpcResponse[JsonNode]
|
|||
debug "NewBE_callPrivateRPC", rpc_method=methodName
|
||||
let rpcResponseRaw = status_go.callPrivateRPC($inputJSON)
|
||||
result = Json.decode(rpcResponseRaw, RpcResponse[JsonNode])
|
||||
|
||||
if(not result.error.isNil):
|
||||
var err = "\nstatus-go error ["
|
||||
err &= fmt"methodName:{methodName}, "
|
||||
|
|
|
@ -3,20 +3,40 @@ import ./core, ./response_type
|
|||
|
||||
export response_type
|
||||
|
||||
proc resolver*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
# TODO: Use a real chain id
|
||||
let payload = %* [1, username]
|
||||
proc resolver*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_resolver", payload)
|
||||
|
||||
proc contentHash*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
# TODO: Use a real chain id
|
||||
let payload = %* [1, username]
|
||||
proc ownerOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_ownerOf", payload)
|
||||
|
||||
proc contentHash*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_contentHash", payload)
|
||||
|
||||
proc resourceURL*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
# TODO: Use a real chain id
|
||||
let payload = %* [1, username]
|
||||
return core.callPrivateRPC("ens_resourceURL", payload)
|
||||
proc publicKeyOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_publicKeyOf", payload)
|
||||
|
||||
proc addressOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_addressOf", payload)
|
||||
|
||||
proc expireAt*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
|
||||
return core.callPrivateRPC("ens_expireAt", payload)
|
||||
|
||||
proc price*(chainId: int): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId]
|
||||
return core.callPrivateRPC("ens_price", payload)
|
||||
|
||||
proc resourceURL*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [chainId, username]
|
||||
return core.callPrivateRPC("ens_resourceURL", payload)
|
|
@ -1,9 +1,10 @@
|
|||
import ens, provider
|
||||
import stew/byteutils
|
||||
from stew/base32 import nil
|
||||
from stew/base58 import nil
|
||||
import ./statusgo_backend_new/ens as status_ens
|
||||
import chronicles, httpclient, net
|
||||
import strutils
|
||||
import json
|
||||
import semver
|
||||
import constants
|
||||
|
||||
|
@ -14,31 +15,12 @@ type
|
|||
url*: string
|
||||
|
||||
proc getLatestVersion*(): VersionInfo =
|
||||
let contentHash = contenthash(APP_UPDATES_ENS)
|
||||
if contentHash == "":
|
||||
let response = status_ens.resourceUrl(chainId=1, username=APP_UPDATES_ENS)
|
||||
let host = response.result{"Host"}.getStr
|
||||
if host == "":
|
||||
raise newException(ValueError, "ENS does not have a content hash")
|
||||
|
||||
var url: string = ""
|
||||
|
||||
let decodedHash = contentHash.decodeENSContentHash()
|
||||
|
||||
case decodedHash[0]:
|
||||
of ENSType.IPFS:
|
||||
let
|
||||
base58bytes = base58.decode(base58.BTCBase58, decodedHash[1])
|
||||
base32Hash = base32.encode(base32.Base32Lower, base58bytes)
|
||||
|
||||
url = "https://" & base32Hash & IPFS_GATEWAY
|
||||
|
||||
of ENSType.SWARM:
|
||||
url = "https://" & SWARM_GATEWAY & "/bzz:/" & decodedHash[1]
|
||||
|
||||
of ENSType.IPNS:
|
||||
url = "https://" & decodedHash[1]
|
||||
|
||||
else:
|
||||
warn "Unknown content for", contentHash
|
||||
raise newException(ValueError, "Unknown content for " & contentHash)
|
||||
let url = "https://" & host & response.result{"Path"}.getStr
|
||||
|
||||
# Read version from folder
|
||||
let secureSSLContext = newContext()
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c6549360a2eeb1e6232591d9d13fbff99ca6b49
|
||||
Subproject commit 5512e19d83c9693bbb043a8aa6b3386164213c46
|
Loading…
Reference in New Issue