'use strict'; require('reflect-metadata'); var tatum = require('@tatumio/tatum'); var LoadBalancer = require('@tatumio/tatum/dist/src/service/rpc/generic/LoadBalancer'); var network_utils = require('@tatumio/tatum/dist/src/util/network.utils'); var viem = require('viem'); var ens = require('viem/ens'); var chains = require('viem/chains'); var ensNormalize = require('@adraffy/ens-normalize'); var ensValidation = require('@ensdomains/ens-validation'); var web3_js = require('@solana/web3.js'); var splNameService = require('@bonfida/spl-name-service'); var ethers = require('ethers'); var SID = require('@siddomains/sidjs'); var interfaces = require('@siddomains/sidjs/dist/constants/interfaces'); var contract = require('@siddomains/sidjs/dist/utils/contract'); var registrarController = require('@siddomains/sid-contracts/build/contracts/IRegistrarController.json'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var SID__default = /*#__PURE__*/_interopDefault(SID); var registrarController__default = /*#__PURE__*/_interopDefault(registrarController); var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); function createCustomClient(tldInfo, rpcUrl) { const client = viem.createPublicClient({ chain: { id: Number(tldInfo.chainId), rpcUrls: { default: { http: [ rpcUrl || tldInfo.defaultRpc ] }, public: { http: [ rpcUrl || tldInfo.defaultRpc ] } }, name: "", network: "", nativeCurrency: { decimals: 18, name: "", symbol: "" } }, transport: viem.http() }); return client; } __name(createCustomClient, "createCustomClient"); var v2Tlds = /* @__PURE__ */ new Set([ "bnb", "arb", "eth" ]); function isV2Tld(tld) { return v2Tlds.has(tld); } __name(isV2Tld, "isV2Tld"); function isEthChain(chainId) { const ethChains = /* @__PURE__ */ new Set([ chains.mainnet.id, chains.goerli.id, chains.sepolia.id ]); return ethChains.has(chainId); } __name(isEthChain, "isEthChain"); function getChainFromId(chainId) { switch (chainId) { case 1: return chains.mainnet; case 5: return chains.goerli; case 11155111: return chains.sepolia; default: return chains.mainnet; } } __name(getChainFromId, "getChainFromId"); function getBaseContractFromChainId(chainId) { switch (chainId) { case 1: case 11155111: return "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85"; case 56: return "0xE3b1D32e43Ce8d658368e2CBFF95D57Ef39Be8a6"; case 97: return "0x888A2BA9787381000Cd93CA4bd23bB113f03C5Af"; case 42161: return "0x5d482d501b369f5ba034dec5c5fb7a50d2d6ca20"; default: return "0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85"; } } __name(getBaseContractFromChainId, "getBaseContractFromChainId"); var ResolverAbi = [ { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" } ], name: "name", outputs: [ { internalType: "string", name: "", type: "string" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" }, { internalType: "uint256", name: "identifier", type: "uint256" } ], name: "tldName", outputs: [ { internalType: "string", name: "", type: "string" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" }, { internalType: "string", name: "key", type: "string" } ], name: "text", outputs: [ { internalType: "string", name: "", type: "string" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" }, { internalType: "uint256", name: "contentTypes", type: "uint256" } ], name: "ABI", outputs: [ { internalType: "uint256", name: "", type: "uint256" }, { internalType: "bytes", name: "", type: "bytes" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" } ], name: "addr", outputs: [ { internalType: "address payable", name: "", type: "address" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" }, { internalType: "uint256", name: "coinType", type: "uint256" } ], name: "addr", outputs: [ { internalType: "bytes", name: "", type: "bytes" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" } ], name: "contenthash", outputs: [ { internalType: "bytes", name: "", type: "bytes" } ], stateMutability: "view", type: "function" } ]; var ReverseResolverAbi = [ { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" } ], name: "name", outputs: [ { internalType: "string", name: "", type: "string" } ], stateMutability: "view", type: "function" } ]; var SANNContractAbi = [ { inputs: [ { internalType: "uint256", name: "identifier", type: "uint256" } ], name: "tldBase", outputs: [ { internalType: "address", name: "", type: "address" } ], stateMutability: "view", type: "function" } ]; var SIDRegistryAbi = [ { inputs: [ { internalType: "bytes32", name: "node", type: "bytes32" } ], name: "resolver", outputs: [ { internalType: "address", name: "", type: "address" } ], stateMutability: "view", type: "function" } ]; var TldBaseContractAbi = [ { inputs: [ { internalType: "uint256", name: "tokenId", type: "uint256" } ], name: "tokenURI", outputs: [ { internalType: "string", name: "", type: "string" } ], stateMutability: "view", type: "function" } ]; var VerifiedTldHubAbi = [ { inputs: [ { internalType: "uint256", name: "chainId", type: "uint256" } ], name: "getChainInfo", outputs: [ { components: [ { internalType: "uint256", name: "chainId", type: "uint256" }, { internalType: "string", name: "defaultRpc", type: "string" }, { internalType: "address", name: "registry", type: "address" }, { internalType: "address", name: "sann", type: "address" } ], internalType: "struct VerifiedTldHub.chainInfo", name: "", type: "tuple" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "uint256", name: "chainId", type: "uint256" } ], name: "getChainTlds", outputs: [ { internalType: "string[]", name: "", type: "string[]" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "string[]", name: "tlds", type: "string[]" } ], name: "getTldInfo", outputs: [ { components: [ { internalType: "string", name: "tld", type: "string" }, { internalType: "uint256", name: "identifier", type: "uint256" }, { internalType: "uint256", name: "chainId", type: "uint256" }, { internalType: "string", name: "defaultRpc", type: "string" }, { internalType: "address", name: "registry", type: "address" }, { internalType: "address", name: "sann", type: "address" } ], internalType: "struct VerifiedTldHub.completeTldInfo[]", name: "", type: "tuple[]" } ], stateMutability: "view", type: "function" }, { inputs: [], name: "getTlds", outputs: [ { internalType: "string[]", name: "", type: "string[]" } ], stateMutability: "view", type: "function" } ]; var CONTRACTS = { verifiedTldHub: "0x754D6827A57334143eD5fB58C5b1A4aAe4396ba5", verifiedTldHubTest: "0x123c784946a9b649FB1268D589E41bc7BF658725" }; var _a; var ContractReader = (_a = class { constructor(isDev, rpcUrl) { this.isDev = isDev; this.rpcUrl = rpcUrl ?? "https://rpc.ankr.com/eth"; } /** Get verified TLD hub contract */ getVerifiedTldHubContract() { const ethClient = viem.createPublicClient({ chain: this.isDev ? chains.bscTestnet : chains.mainnet, transport: viem.http(this.isDev ? void 0 : this.rpcUrl) }); const hubContract = viem.getContract({ address: this.isDev ? CONTRACTS.verifiedTldHubTest : CONTRACTS.verifiedTldHub, abi: VerifiedTldHubAbi, client: { public: ethClient } }); return hubContract; } async getTldInfo(tldList) { const hubContract = this.getVerifiedTldHubContract(); const tldInfoList = await hubContract.read.getTldInfo([ tldList ]); return tldInfoList.filter((e) => !!e.tld); } /** * Get resolver contract by TLD * * @export * @param {string} domain * @param {TldInfo} tldInfo * @param {string} [rpcUrl] * @return {*} */ async getResolverContractByTld(namehash52, tldInfo, rpcUrl) { const client = createCustomClient(tldInfo, rpcUrl); const registryContract = viem.getContract({ address: tldInfo.registry, abi: SIDRegistryAbi, client: { public: client } }); const resolverAddr = await registryContract.read.resolver([ namehash52 ]); if (!viem.hexToNumber(resolverAddr)) { throw "resolver address is null"; } const resolverContract = viem.getContract({ address: resolverAddr, abi: ResolverAbi, client: { public: client } }); return resolverContract; } /** Get reverse resolver contract (V2 only) */ async getReverseResolverContract(reverseNamehash, tldInfo, rpcUrl) { if (!tldInfo.defaultRpc) return void 0; const client = createCustomClient(tldInfo, rpcUrl); const registryContract = viem.getContract({ address: tldInfo.registry, abi: SIDRegistryAbi, client: { public: client } }); const resolverAddr = await registryContract.read.resolver([ reverseNamehash ]); const resolverContract = viem.getContract({ address: resolverAddr ?? "", abi: ReverseResolverAbi, client: { public: client } }); return resolverContract; } async getTldMetadata(domain, tldInfo, rpcUrl) { const tokenId = viem.hexToBigInt(viem.keccak256(Buffer.from(domain.split(".")[0]))); const client = createCustomClient(tldInfo, rpcUrl); const sannContract = viem.getContract({ address: tldInfo.sann, abi: SANNContractAbi, client: { public: client } }); const tldBaseContractAddr = tldInfo.identifier === BigInt(0) ? getBaseContractFromChainId(Number(tldInfo.chainId)) : await sannContract.read.tldBase([ BigInt(`${tldInfo.identifier}`) ]); if (tldInfo.chainId === BigInt(chains.mainnet.id)) { return `https://metadata.ens.domains/mainnet/${tldBaseContractAddr}/${tokenId}`; } const tldBaseContract = viem.getContract({ address: tldBaseContractAddr, abi: TldBaseContractAbi, client: { public: client } }); const metadata = await tldBaseContract.read.tokenURI([ tokenId ]); return metadata; } // Read content hash from resolver contract async getContenthash(namehash52, tldInfo, rpcUrl) { const resolver = await this.getResolverContractByTld(namehash52, tldInfo, rpcUrl); const functionExists = await this.resolverFunctionExists(resolver.address, "contenthash(bytes32)", tldInfo, rpcUrl); if (!functionExists) return void 0; const contentHash = await resolver.read.contenthash([ namehash52 ]); return contentHash; } // Read content hash from resolver contract async getABI(namehash52, tldInfo, rpcUrl) { const resolver = await this.getResolverContractByTld(namehash52, tldInfo, rpcUrl); const functionExists = await this.resolverFunctionExists(resolver.address, "ABI(bytes32, uint256)", tldInfo, rpcUrl); if (!functionExists) return void 0; const contentHash = await resolver.read.ABI([ namehash52, BigInt(1) ]); return contentHash; } async containsTldNameFunction(resolverAddr, tldInfo, rpcUrl) { const client = createCustomClient(tldInfo, rpcUrl); const bytecode = await client.getBytecode({ address: resolverAddr }); const selector = viem.toFunctionSelector("tldName(bytes32, uint256)"); return bytecode?.includes(selector.slice(2)) ?? false; } async resolverFunctionExists(resolverAddr, functionName, tldInfo, rpcUrl) { const client = createCustomClient(tldInfo, rpcUrl); const bytecode = await client.getBytecode({ address: resolverAddr }); const selector = viem.toFunctionSelector(functionName); return bytecode?.includes(selector.slice(2)) ?? false; } }, __name(_a, "ContractReader"), _a); function isEncodedLabelhash(hash) { return hash.startsWith("[") && hash.endsWith("]") && hash.length === 66; } __name(isEncodedLabelhash, "isEncodedLabelhash"); var normalize = /* @__PURE__ */ __name((name) => name ? ensNormalize.ens_normalize(name) : name, "normalize"); function tldNamehash(inputName, identifier) { if (!identifier) return viem.namehash(inputName); const fullNameNode = `${inputName}.[${viem.toHex(identifier, { size: 32 }).slice(2)}]`; return viem.namehash(fullNameNode); } __name(tldNamehash, "tldNamehash"); var whitelist_default = [ "cz.bnb", "id.bnb", "sm.bnb", "yg.bnb", "cz.eth.bnb", "id.eth.bnb", "sm.eth.bnb", "yg.eth.bnb", "go.arb" ]; function validateName(name) { if (!name) { throw new Error("Invalid name"); } const labelArr = name.split("."); let domain = name; let suffix = ""; if (labelArr.length > 1) { domain = labelArr.slice(0, labelArr.length - 1).join("."); suffix = labelArr[labelArr.length - 1]; } if (labelArr.length === 3 && suffix.toLowerCase() === "bnb" && labelArr[1].toLowerCase() === "eth") { domain = labelArr[0]; } const hasEmptyLabels = labelArr.filter((e) => e.length < 1).length > 0; if (hasEmptyLabels) throw new Error("Domain cannot have empty labels"); if (!validateLabelLength(domain, !isV2Tld(suffix)) && !whitelist_default.includes(name.toLowerCase())) { throw new Error("Invalid name"); } if (!validateDomains(domain)) throw new Error("Invalid name"); const normalizedArray = labelArr.map((label) => { return isEncodedLabelhash(label) ? label : normalize(label); }); try { return normalizedArray.join("."); } catch (e) { throw e; } } __name(validateName, "validateName"); function validateLabelLength(name, allowShortLabel = false) { if (!name) { return false; } const len = countCharacters(name); if (len > 512 || !allowShortLabel && len < 3) { return false; } let normalizedValue; try { normalizedValue = normalize(name); } catch (e) { normalizedValue = name; } if (normalizedValue.length > 512 || !allowShortLabel && len < 3) { return false; } return true; } __name(validateLabelLength, "validateLabelLength"); function validateDomains(value) { const nospecial = /^[^*|\\":<>[\]{}`\\\\()';@&$]+$/u; const blackList = /[\u0000-\u002c\u002e-\u002f\u003a-\u005e\u0060\u007b-\u007f\u200b\u200c\u200d\ufeff]/g; return nospecial.test(value) && !blackList.test(value) && ensValidation.validate(value); } __name(validateDomains, "validateDomains"); function countCharacters(str) { const normalizedStr = ensNormalize.ens_normalize(str); const regex = /[\u0000-\uffff]|\p{L}|\p{Emoji}(?!\p{M})/gu; const matches = normalizedStr.match(regex); return matches ? matches.length : 0; } __name(countCharacters, "countCharacters"); var proxyReaderAbi = [ { inputs: [ { internalType: "string[]", name: "keys", type: "string[]" }, { internalType: "uint256", name: "tokenId", type: "uint256" } ], name: "getMany", outputs: [ { internalType: "string[]", name: "values", type: "string[]" } ], stateMutability: "view", type: "function" } ]; var unsRegistryAbi = [ { inputs: [ { internalType: "address", name: "addr", type: "address" } ], name: "reverseNameOf", outputs: [ { internalType: "string", name: "reverseUri", type: "string" } ], stateMutability: "view", type: "function" } ]; var _a2; var UDResolver = (_a2 = class { constructor() { this.proxyReaderAddress = "0x423F2531bd5d3C3D4EF7C318c2D1d9BEDE67c680"; this.unsRegistryAddress = "0xa9a6A3626993D487d2Dbda3173cf58cA1a9D9e9f"; } /** * Resolve address from name * @param domain * @returns */ async getAddress(domain) { const client = viem.createPublicClient({ chain: chains.polygon, transport: viem.http() }); const proxyReaderContract = viem.getContract({ address: this.proxyReaderAddress, abi: proxyReaderAbi, publicClient: client }); const keys = [ "crypto.ETH.address" ]; const res = await proxyReaderContract.read.getMany([ keys, BigInt(viem.namehash(domain)) ]); return res.at(0) || null; } /** * Resolve name from address * @param address * @returns */ async getName(address) { const client = viem.createPublicClient({ chain: chains.polygon, transport: viem.http() }); const registryContract = viem.getContract({ address: this.unsRegistryAddress, abi: unsRegistryAbi, publicClient: client }); const res = await registryContract.read.reverseNameOf([ address ]); if (!res.endsWith(".crypto")) { return null; } return res; } }, __name(_a2, "UDResolver"), _a2); var baseUrl = "https://api.lens.dev/"; var _a3; var LensProtocol = (_a3 = class { static async getDomainName(address) { const gql = ` query Profile($ethereumAddress: EthereumAddress!) { defaultProfile(request: { ethereumAddress: $ethereumAddress }) { id handle } } `; const res = await fetch(baseUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: gql, variables: { ethereumAddress: address } }) }).then((res2) => res2.json()); return res.data.defaultProfile.handle; } static async getAddress(domain) { const gql = ` query Profile ($handle: Handle!) { profile(request: { handle: $handle }) { id ownedBy } } `; const res = await fetch(baseUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: gql, variables: { handle: domain } }) }).then((res2) => res2.json()); return res.data.profile.ownedBy; } }, __name(_a3, "LensProtocol"), _a3); var _a4; var Web3Name = (_a4 = class { constructor({ isDev = false, rpcUrl } = {}) { this.contractReader = new ContractReader(isDev, rpcUrl); } async getTldInfoList({ queryTldList, queryChainIdList }) { const hubContract = this.contractReader.getVerifiedTldHubContract(); const chainTlds = []; for await (const chainId of queryChainIdList ?? []) { const tlds2 = await hubContract.read.getChainTlds([ BigInt(chainId) ]); if (isEthChain(chainId)) { const ethTld = tlds2.filter( (e) => e !== "eth" /* ENS */ ).at(0); if (ethTld) chainTlds.push(ethTld); chainTlds.push( "eth" /* ENS */ ); } else { const tldName = tlds2.at(0); if (tldName) chainTlds.push(tldName); } } const tlds = queryTldList ?? []; if (tlds.length === 0) { const allTlds = await hubContract.read.getTlds(); tlds.push(...allTlds); } const reqTlds = queryChainIdList?.length ? chainTlds : tlds; return await this.contractReader.getTldInfo(reqTlds); } async getDomainNameByTld(address, reverseNamehash, tld, isTldName, rpcUrl) { let name = null; try { if (tld.tld === "eth") { const contract = await this.contractReader.getReverseResolverContract(reverseNamehash, tld, rpcUrl); name = await contract?.read.name([ reverseNamehash ]) ?? ""; } else { const contract = await this.contractReader.getResolverContractByTld(reverseNamehash, tld, rpcUrl); if (isTldName) { if (isV2Tld(tld.tld)) { const containsTldNameFunction = await this.contractReader.containsTldNameFunction(contract.address, tld, rpcUrl); if (containsTldNameFunction) { name = await contract.read.tldName([ reverseNamehash, tld.identifier ]); } else { name = await contract.read.name([ reverseNamehash ]); } } else { name = await contract.read.tldName([ reverseNamehash, tld.identifier ]); } } else { name = await contract.read.name([ reverseNamehash ]); } } } catch (error) { } if (name) { const reverseAddress = await this.getAddress(name, { rpcUrl }); if (reverseAddress?.toLowerCase() === address.toLowerCase()) { return name; } else { return null; } } return name; } /** * Get domain name from address. * If queryChainIdList is specified, it will return domain name from that chain. * If queryTldList is specified, it will return domain name from that TLDs with the order. * If neither is specified, it will return domain name from all TLDs. * It's not recommended to use queryChainIdList and queryTldList together. * * @param {GetDomainNameProps} { address, queryChainIdList, queryTldList } * @return {*} {(Promise)} * @memberof Web3Name */ async getDomainName({ address, queryChainIdList, queryTldList, rpcUrl }) { if (queryChainIdList?.length && queryTldList?.length) { console.warn("queryChainIdList and queryTldList cannot be used together, queryTldList will be ignored"); } try { const reverseNode = `${ens.normalize(address).slice(2)}.addr.reverse`; const reverseNamehash = viem.namehash(reverseNode); const tldInfoList = await this.getTldInfoList({ queryChainIdList, queryTldList, rpcUrl }); const resList = []; for await (const tld of tldInfoList) { if (!tld.tld) continue; const isTldName = !!queryTldList?.length; let name = await this.getDomainNameByTld(address, reverseNamehash, tld, isTldName, rpcUrl); if (name) { resList.push(name); break; } } if (queryTldList?.includes( "lens" /* LENS */ )) { const lensName = await LensProtocol.getDomainName(address); if (lensName) resList.push(lensName); } else if (queryTldList?.includes( "crypto" /* CRYPTO */ )) { const UD = new UDResolver(); const udName = await UD.getName(address); if (udName) resList.push(udName); } return resList.at(0) ?? null; } catch (e) { console.log(`Error getting name for reverse record of ${address}`, e); return null; } } async batchGetDomainName({ addressList, queryChainIdList, queryTldList, rpcUrl }) { if (queryChainIdList?.length && queryTldList?.length) { console.warn("queryChainIdList and queryTldList cannot be used together, queryTldList will be ignored"); } if (!addressList.length) return []; let curAddr = addressList[0]; try { const tldInfoList = await this.getTldInfoList({ queryChainIdList, queryTldList, rpcUrl }); const resList = []; const isIncludeLens = queryTldList?.includes( "lens" /* LENS */ ); const isIncludeCrypto = queryTldList?.includes( "crypto" /* CRYPTO */ ); for await (const address of addressList) { curAddr = address; const reverseNode = `${ens.normalize(address).slice(2)}.addr.reverse`; const reverseNamehash = viem.namehash(reverseNode); let nameRes = null; for await (const tld of tldInfoList) { if (!tld.tld) continue; const isTldName = !!queryTldList?.length; nameRes = await this.getDomainNameByTld(address, reverseNamehash, tld, isTldName, rpcUrl); if (nameRes) { break; } } if (!nameRes && isIncludeLens) { nameRes = await LensProtocol.getDomainName(address); } if (!nameRes && isIncludeCrypto) { const UD = new UDResolver(); nameRes = await UD.getName(address); } resList.push({ address, domain: nameRes }); } return resList; } catch (e) { console.log(`Error getting name for reverse record of ${curAddr}`, e); return null; } } /** * Get address from name. If coinType is specified, it will return ENSIP-9 address for that coinType. * * @param {string} name * @param {{ coinType?: number; rpcUrl?: string }} { coinType, rpcUrl } * @return {*} {(Promise)} * @memberof Web3Name */ async getAddress(name, { coinType, rpcUrl } = {}) { const tld = name.split(".").pop()?.toLowerCase(); if (!tld) { return null; } const normalizedDomain = "lens" === tld ? name : ens.normalize(name); if (tld !== "eth" && tld !== "lens" && tld !== "crypto") { validateName(normalizedDomain); } try { if (tld === "eth") { const tldInfoList2 = await this.contractReader.getTldInfo([ tld ]); const publicClient = viem.createPublicClient({ chain: getChainFromId(Number(tldInfoList2[0].chainId)), transport: viem.http() }); return await publicClient.getEnsAddress({ name: normalizedDomain }); } if (tld === "lens") { return await LensProtocol.getAddress(name); } if (tld === "crypto") { const UD = new UDResolver(); return await UD.getAddress(name); } const tldInfoList = await this.contractReader.getTldInfo([ tld ]); const tldInfo = tldInfoList.at(0); if (!tldInfo) { throw "TLD not found"; } const namehash52 = tldNamehash(normalizedDomain, isV2Tld(tld) ? void 0 : tldInfo.identifier); const resolverContract = await this.contractReader.getResolverContractByTld(namehash52, tldInfo, rpcUrl); const res = coinType !== void 0 ? await resolverContract.read.addr([ namehash52, BigInt(coinType) ]) : await resolverContract.read.addr([ namehash52 ]); return res; } catch (error) { console.error(`Error getting address for ${name}`, error); return null; } } /** * Get available domain list from address. * * @param {GetDomainNameProps} { address, queryChainIdList, queryTldList } * @return {*} {Promise} * @memberof Web3Name */ async getDomainNames({ address, queryChainIdList, queryTldList, rpcUrl }) { if (queryChainIdList?.length && queryTldList?.length) { console.warn("queryChainIdList and queryTldList cannot be used together, queryTldList will be ignored"); } const resList = /* @__PURE__ */ new Set([]); try { const reverseNode = `${address.toLowerCase().slice(2)}.addr.reverse`; const reverseNamehash = viem.namehash(reverseNode); const hubContract = this.contractReader.getVerifiedTldHubContract(); const chainTlds = []; for (const chainId of queryChainIdList ?? []) { const tlds2 = await hubContract.read.getChainTlds([ BigInt(chainId) ]); if (isEthChain(chainId)) { const ethTld = tlds2.filter( (e) => e !== "eth" /* ENS */ ).at(0); if (ethTld) chainTlds.push(ethTld); chainTlds.push( "eth" /* ENS */ ); } else { const tldName = tlds2.at(0); if (tldName) chainTlds.push(tldName); } } const tlds = queryTldList ?? []; if (tlds.length === 0) { const allTlds = await hubContract.read.getTlds(); tlds.push(...allTlds); } const reqTlds = queryChainIdList?.length ? chainTlds : tlds; const tldInfoList = await this.contractReader.getTldInfo(reqTlds); for (const tld of tldInfoList) { if (!tld.tld) continue; let name = ""; try { if (tld.tld === "eth") { const contract = await this.contractReader.getReverseResolverContract(reverseNamehash, tld, rpcUrl); name = await contract?.read.name([ reverseNamehash ]) ?? ""; } else { const contract = await this.contractReader.getResolverContractByTld(reverseNamehash, tld); if (queryTldList?.length) { if (isV2Tld(tld.tld)) { const containsTldNameFunction = await this.contractReader.containsTldNameFunction(contract.address, tld); if (!containsTldNameFunction) throw "TLD name is not supported for this TLD"; } name = await contract.read.tldName([ reverseNamehash, tld.identifier ]); } else { name = await contract.read.name([ reverseNamehash ]); } } } catch (error) { continue; } if (name) { const reverseAddress = await this.getAddress(name, { rpcUrl }); if (reverseAddress === address) { resList.add(name); } } } if (queryTldList?.includes( "lens" /* LENS */ )) { const lensName = await LensProtocol.getDomainName(address); if (lensName) resList.add(lensName); } else if (queryTldList?.includes( "crypto" /* CRYPTO */ )) { const UD = new UDResolver(); const name = await UD.getName(address); name && resList.add(name); } return Array.from(resList); } catch (e) { console.log(`Error getting name for reverse record of ${address}`, e); return []; } } /** * Get domain record from name and key. * * @param {{ name: string; key: string; rpcUrl?: string }} { name, key, rpcUrl } * @return {*} * @memberof Web3Name */ async getDomainRecord({ name, key, rpcUrl }) { const tld = name.split(".").pop()?.toLowerCase(); if (!tld) { return null; } try { const normalizedDomain = "lens" === tld ? name : ens.normalize(name); const tldInfoList = await this.contractReader.getTldInfo([ tld ]); const tldInfo = tldInfoList[0]; if (!tldInfo) { throw "TLD not found"; } const namehash52 = tldNamehash(normalizedDomain, isV2Tld(tld) ? void 0 : tldInfo.identifier); const resolverContract = await this.contractReader.getResolverContractByTld(namehash52, tldInfo, rpcUrl); const record = await resolverContract.read.text([ namehash52, key ]); return record; } catch (error) { console.error(`Error getting address for ${name}`, error); return null; } } /** * Get domain metadata from name. * * @param {{ name: string; rpcUrl?: string }} { name, rpcUrl } * @return {*} * @memberof Web3Name */ async getMetadata({ name, rpcUrl }) { const tld = name.split(".").pop()?.toLowerCase(); if (!tld) { return null; } try { const tldInfo = await this.contractReader.getTldInfo([ tld ]); if (!tldInfo || !tldInfo.at(0)?.sann) { return null; } const metadata = await this.contractReader.getTldMetadata(name, tldInfo[0], rpcUrl); const res = await fetch(metadata).then((res2) => res2.json()); return res; } catch (error) { console.error(`Error getting metadata for ${name}`, error); } } /** * Get domain avatar from name. * * @param {{ name: string; key: string; rpcUrl?: string }} { name, rpcUrl } * @return {*} * @memberof Web3Name */ async getDomainAvatar({ name, rpcUrl }) { const metadata = await this.getMetadata({ name, rpcUrl }); return metadata?.image; } /** * Get domain content hash from name. * * @param {{ name: string; rpcUrl?: string }} { name, rpcUrl } * @return {*} {(Promise)} * @memberof Web3Name */ async getContentHash({ name, rpcUrl }) { const tld = name.split(".").pop()?.toLowerCase(); if (!tld) { return void 0; } try { const tldInfo = (await this.contractReader.getTldInfo([ tld ])).at(0); if (!tldInfo) throw "TLD not found"; const namehash52 = tldNamehash(ens.normalize(name), isV2Tld(tld) ? void 0 : tldInfo.identifier); const contenthash = await this.contractReader.getContenthash(namehash52, tldInfo, rpcUrl); if (!contenthash || contenthash === "0x") return void 0; return contenthash; } catch (error) { console.error(`Error getting content hash for ${name}`, error); } } }, __name(_a4, "Web3Name"), _a4); function createWeb3Name({ isDev = false, rpcUrl } = {}) { return new Web3Name({ isDev, rpcUrl }); } __name(createWeb3Name, "createWeb3Name"); var _a5; var SolName = (_a5 = class { constructor() { this.connection = new web3_js.Connection(web3_js.clusterApiUrl("mainnet-beta")); } async getDomainName({ address }) { const name = await splNameService.reverseLookup(this.connection, new web3_js.PublicKey(address)); return name; } async getAddress({ name }) { const owner = await splNameService.resolve(this.connection, name); return owner; } }, __name(_a5, "SolName"), _a5); function createSolName() { return new SolName(); } __name(createSolName, "createSolName"); function getRegistrarControllerContract({ address, signer }) { return new ethers.Contract(address, registrarController__default.default, signer); } __name(getRegistrarControllerContract, "getRegistrarControllerContract"); var YEAR_IN_SECONDS = 31556952; function calculateDuration(years) { return ethers.BigNumber.from(parseInt((years * YEAR_IN_SECONDS).toFixed())); } __name(calculateDuration, "calculateDuration"); function genCommitSecret() { return ethers.utils.hexlify(ethers.utils.randomBytes(32)); } __name(genCommitSecret, "genCommitSecret"); function getBufferedPrice(price) { return price.mul(110).div(100); } __name(getBufferedPrice, "getBufferedPrice"); var emptySignature = [ "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", 0, 0, "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ]; function getApiUrl() { return "https://api.prd.space.id/v1/sign-referral"; } __name(getApiUrl, "getApiUrl"); async function getReferralSignature(domain, chainId) { if (!domain || !chainId) return emptySignature; try { const res = await fetch(getApiUrl(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domain, chainId }) }); const signReferral = await res.json(); if (signReferral) { return [ signReferral.referrerAddress, SID.namehash(domain), Number(signReferral.referralCount), Number(signReferral.signedAt), signReferral.signature ]; } else { throw new Error("sign referral fail"); } } catch (e) { console.error(e); throw new Error("sign referral fail"); } } __name(getReferralSignature, "getReferralSignature"); var ENS_COMMIT_WAIT_TIEM = 60; var ensRegisterAbi = [ { constant: true, inputs: [ { internalType: "string", name: "name", type: "string" } ], name: "available", outputs: [ { internalType: "bool", name: "", type: "bool" } ], payable: false, stateMutability: "view", type: "function" }, { constant: false, inputs: [ { internalType: "bytes32", name: "commitment", type: "bytes32" } ], name: "commit", outputs: [], payable: false, stateMutability: "nonpayable", type: "function" }, { constant: true, inputs: [ { internalType: "bytes32", name: "", type: "bytes32" } ], name: "commitments", outputs: [ { internalType: "uint256", name: "", type: "uint256" } ], payable: false, stateMutability: "view", type: "function" }, { constant: true, inputs: [ { internalType: "string", name: "name", type: "string" }, { internalType: "address", name: "owner", type: "address" }, { internalType: "bytes32", name: "secret", type: "bytes32" } ], name: "makeCommitment", outputs: [ { internalType: "bytes32", name: "", type: "bytes32" } ], payable: false, stateMutability: "pure", type: "function" }, { constant: true, inputs: [ { internalType: "string", name: "name", type: "string" }, { internalType: "address", name: "owner", type: "address" }, { internalType: "bytes32", name: "secret", type: "bytes32" }, { internalType: "address", name: "resolver", type: "address" }, { internalType: "address", name: "addr", type: "address" } ], name: "makeCommitmentWithConfig", outputs: [ { internalType: "bytes32", name: "", type: "bytes32" } ], payable: false, stateMutability: "pure", type: "function" }, { constant: false, inputs: [ { internalType: "string", name: "name", type: "string" }, { internalType: "address", name: "owner", type: "address" }, { internalType: "uint256", name: "duration", type: "uint256" }, { internalType: "bytes32", name: "secret", type: "bytes32" } ], name: "register", outputs: [], payable: true, stateMutability: "payable", type: "function" }, { constant: false, inputs: [ { internalType: "string", name: "name", type: "string" }, { internalType: "address", name: "owner", type: "address" }, { internalType: "uint256", name: "duration", type: "uint256" }, { internalType: "bytes32", name: "secret", type: "bytes32" }, { internalType: "address", name: "resolver", type: "address" }, { internalType: "address", name: "addr", type: "address" } ], name: "registerWithConfig", outputs: [], payable: true, stateMutability: "payable", type: "function" }, { constant: true, inputs: [ { internalType: "string", name: "name", type: "string" }, { internalType: "uint256", name: "duration", type: "uint256" } ], name: "rentPrice", outputs: [ { internalType: "uint256", name: "", type: "uint256" } ], payable: false, stateMutability: "view", type: "function" } ]; var ens_default = ensRegisterAbi; var wait = /* @__PURE__ */ __name((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)), "wait"); var getTldByChainId = /* @__PURE__ */ __name((chainId) => { switch (chainId) { case 1: return "eth"; case 97: case 56: return "bnb"; case 42161: case 421613: return "arb"; default: return "bnb"; } }, "getTldByChainId"); var _a6; var SIDRegister = (_a6 = class { constructor(options) { const { signer, sidAddress, chainId } = options; if (!ethers.Signer.isSigner(signer)) throw new Error("signer is required"); if (!chainId) throw new Error("chainId is required"); this.sidAddress = sidAddress ?? SID.getSidAddress(chainId); this.signer = signer; this.chainId = chainId; } async getRegistrarController() { if (!this.registrarController) { const sidContract = contract.getSIDContract({ address: this.sidAddress, provider: this.signer.provider }); const hash = SID.namehash(getTldByChainId(this.chainId)); const resolverAddr = await sidContract.resolver(hash); const resolverContract = contract.getResolverContract({ address: resolverAddr, provider: this.signer.provider }); const registrarControllerAddr = await resolverContract.interfaceImplementer(hash, interfaces.interfaces.permanentRegistrar); if (this.chainId === 1) { this.registrarController = new ethers.Contract(registrarControllerAddr, ens_default, this.signer); } else { this.registrarController = getRegistrarControllerContract({ address: registrarControllerAddr, signer: this.signer }); } return this.registrarController; } if (!this.registrarController) throw new Error("Registrar Controller is not initialized"); return this.registrarController; } async getPublicResolver() { const sid = new SID__default.default({ provider: this.signer.provider, sidAddress: this.sidAddress }); if (this.chainId === 1) { return sid.name("resolver.eth").getAddress(); } return sid.name(`sid-resolver.${getTldByChainId(this.chainId)}`).getAddress(); } /** * Get the rent price for a name. * @param label * @param year number of registration years */ async getRentPrice(label, year) { const normalizedName = SID.validateName(label); if (normalizedName !== label) throw new Error("unnormailzed name"); const registrarController2 = await this.getRegistrarController(); const res = await registrarController2.rentPrice(normalizedName, calculateDuration(year)); if (this.chainId === 1) { return res; } return res[0].add(res[1]); } /** * check if the domain is available for registration * @param label */ async getAvailable(label) { const normalizedName = SID.validateName(label); if (normalizedName !== label) throw new Error("unnormailzed name"); const registrarController2 = await this.getRegistrarController(); return registrarController2.available(normalizedName); } /** * register a domain * @param label * @param address the address to register * @param year * @param options.referrer optional parameter. the referrer domain. only work for .bnb and .arb domain * @param options.setPrimaryName optional parameter. register and set the domain as primary name. only work for .bnb and .arb domain * @param options.onCommitSuccess optional parameter. callback function when the commitment is successful. only required for .eth domain */ async register(label, address, year, options) { const referrer = options?.referrer; const setPrimaryName = options?.setPrimaryName; const normalizedName = SID.validateName(label); if (normalizedName !== label) throw new Error("unnormailzed name"); if (year < 1) throw new Error("minimum registration for one year"); const duration = calculateDuration(year); const publicResolver = await this.getPublicResolver(); const registrarController2 = await this.getRegistrarController(); const secret = genCommitSecret(); if (this.chainId === 1) { const commitment = await registrarController2.makeCommitmentWithConfig(normalizedName, address, secret, publicResolver, address); const tx2 = await registrarController2?.commit(commitment); await tx2?.wait(); const checkRes = await registrarController2?.commitments(commitment); const createTime = checkRes?.toNumber() ?? 0; if (createTime > 0) { if (options?.onCommitSuccess) { await options.onCommitSuccess(ENS_COMMIT_WAIT_TIEM); } else { await wait(ENS_COMMIT_WAIT_TIEM * 1e3); } } else { throw new Error("commitment error"); } } const priceRes = await this.getRentPrice(normalizedName, year); const bufferedPrice = getBufferedPrice(priceRes); let tx; if (this.chainId === 1) { const gas = await registrarController2.estimateGas?.registerWithConfig(normalizedName, address, duration, secret, publicResolver, address, { value: bufferedPrice }); const gasLimit = (gas ?? ethers.BigNumber.from(0)).add(21e3); tx = await registrarController2?.registerWithConfig(normalizedName, address, duration, secret, publicResolver, address, { value: bufferedPrice, gasLimit: gasLimit ? ethers.BigNumber.from(gasLimit) : void 0 }); } else { const referralSign = await getReferralSignature(referrer ?? "", this.chainId); const gas = await registrarController2.estimateGas?.registerWithConfigAndPoint(normalizedName, address, duration, publicResolver, false, setPrimaryName, referralSign, { value: bufferedPrice }); const gasLimit = (gas ?? ethers.BigNumber.from(0)).add(21e3); tx = await registrarController2.registerWithConfigAndPoint(normalizedName, address, duration, publicResolver, false, setPrimaryName, referralSign, { value: bufferedPrice, gasLimit }); } await tx.wait(); return normalizedName; } }, __name(_a6, "SIDRegister"), _a6); var _SpaceIdCore = class _SpaceIdCore extends tatum.TatumSdkExtension { constructor(tatumSdkContainer) { super(tatumSdkContainer); __publicField(this, "loadBalancer"); __publicField(this, "sdkConfig"); __publicField(this, "supportedNetworks", [ tatum.Network.ETHEREUM, tatum.Network.ARBITRUM_ONE, tatum.Network.BINANCE_SMART_CHAIN, tatum.Network.GNOSIS, tatum.Network.SOLANA ]); this.loadBalancer = this.tatumSdkContainer.get(LoadBalancer.LoadBalancer); this.sdkConfig = this.tatumSdkContainer.getConfig(); } async getAddress(name, optional) { if (tatum.isSolanaNetwork(this.sdkConfig.network)) { return createSolName().getAddress({ name }); } return createWeb3Name().getAddress(name, optional); } async getDomainName(address, optional) { if (tatum.isSolanaNetwork(this.sdkConfig.network)) { return createSolName().getDomainName({ address }); } return createWeb3Name().getDomainName({ address, ...optional }); } async getDomainNames(address, optional) { this.validateIfNotSolana(); return createWeb3Name().getDomainNames({ address, ...optional }); } async getDomainNameBatch(addresses, optional) { this.validateIfNotSolana(); return createWeb3Name().batchGetDomainName({ addressList: addresses, ...optional }); } async getDomainRecord(name, key, optional) { this.validateIfNotSolana(); return createWeb3Name().getDomainRecord({ name, key, ...optional }); } async getMetadata(name, optional) { this.validateIfNotSolana(); return createWeb3Name().getMetadata({ name, ...optional }); } async getContentHash(name, optional) { this.validateIfNotSolana(); return createWeb3Name().getContentHash({ name, ...optional }); } async isDomainAvailable(name, privateKey) { const { client } = await this.getRegisterClient(privateKey); return client.getAvailable(name); } async getRegistrationFee(name, years, privateKey) { const { client } = await this.getRegisterClient(privateKey); return client.getRentPrice(name, years); } async registerDomain(name, years, privateKey) { const { client, address } = await this.getRegisterClient(privateKey); if (await client.getAvailable(name)) { const price = await client.getRentPrice(name, years); await client.register(name, address, years, this.sdkConfig.network === tatum.Network.ETHEREUM ? { onCommitSuccess: /* @__PURE__ */ __name((waitTime) => { return new Promise((resolve2) => { setTimeout(resolve2, waitTime * 1e3); }); }, "onCommitSuccess") } : void 0); console.log(`[SpaceIdCore] Registered domain ${name} for ${price}`); return true; } console.error(`[SpaceIdCore] Domain ${name} is unavailable`); return false; } validateIfNotSolana() { if (tatum.isSolanaNetwork(this.sdkConfig.network)) { throw new Error(`[SpaceIdCore] Method not supported for selected chain`); } } getApiKey() { if (this.sdkConfig.apiKey && typeof this.sdkConfig.apiKey !== "string") { return this.sdkConfig.apiKey.v4 || this.sdkConfig.apiKey.v3 || ""; } return this.sdkConfig.apiKey || ""; } async getRegisterClient(privateKey) { const supportedChainIds = [ 1, 56, 97 ]; const chainId = network_utils.NetworkUtils.getChainId(this.sdkConfig.network); if (!supportedChainIds.includes(chainId)) { throw new Error(`[SpaceIdCore] Domain registration not supported for selected chain`); } const provider = new ethers.ethers.providers.JsonRpcProvider({ url: this.loadBalancer.getRpcNodeUrl(), headers: { "x-api-key": this.getApiKey() } }, chainId); const signer = new ethers.ethers.Wallet(privateKey, provider); return { client: new SIDRegister({ signer, chainId }), address: await signer.getAddress() }; } }; __name(_SpaceIdCore, "SpaceIdCore"); var SpaceIdCore = _SpaceIdCore; exports.SpaceIdCore = SpaceIdCore;