UNPKG

arnacon-sdk

Version:

A comprehensive SDK for deploying and managing Arnacon smart contracts across multiple networks

309 lines (266 loc) 11.6 kB
const { ethers } = require("ethers"); const axios = require("axios"); const namehash = require("../utils/namehash"); class BlockchainExplorer { constructor(deploymentManager) { this.deploymentManager = deploymentManager; this.signer = deploymentManager.signer; this.factoryLoader = deploymentManager.factoryLoader; this.provider = deploymentManager.signer.provider; } /** * Get contract instance by name */ async getContract(name) { const address = this.deploymentManager.getContractAddress(name); if (!address) { throw new Error(`Contract ${name} not found in deployed addresses`); } const abi = this.factoryLoader.getContractABI(name); return new ethers.Contract(address, abi, this.provider); } /** * Get contract instance by address */ async getDeployedContract(name, address) { const abi = this.factoryLoader.getContractABI(name); return new ethers.Contract(address, abi, this.provider); } /** * Get comprehensive data for an ENS name */ async getData(ens) { console.log("Getting data for: ", ens); const NameWrapper = await this.getContract("NameWrapper"); const node = namehash(ens); const data = await NameWrapper.getData(node); const expiry = data.expiry; let owner = data.owner; if(owner === "0x0000000000000000000000000000000000000000"){ return {expiry, owner}; } const {arnaconResolver, provisionRegistry, resolverAddress} = await this.findFirstRecords(ens); let tokenURI = {}; if(resolverAddress){ tokenURI = { resolverAddress: resolverAddress }; } if(provisionRegistry){ try{ tokenURI = {...tokenURI, provisionRegistry: provisionRegistry.address}; } catch (error) { console.log("Error getting provision, resolver records probably not set:", error); } } if(arnaconResolver){ try{ console.log("Getting NFT contract address...", arnaconResolver.address); const NFTContractAddress = await arnaconResolver.getNFTContract(node); console.log("NFT Contract Address:", NFTContractAddress); if(NFTContractAddress === "0x0000000000000000000000000000000000000000"){ return {expiry, owner, metadata: tokenURI}; } const abi = [ "function ownerOf(uint256 tokenId) public view returns (address)", "function tokenURI(uint256 tokenId) public view returns (string memory)" ]; const NFTContract = new ethers.Contract(NFTContractAddress, abi, this.provider); const currentTokenId = await arnaconResolver.getCurrentTokenId(node); const ipfsUrl = await NFTContract.tokenURI(currentTokenId); const ipfsHash = ipfsUrl.replace('ipfs://', ''); const response = await axios.get(`https://plum-geographical-catfish-11.mypinata.cloud/ipfs/${ipfsHash}`); tokenURI = { ...tokenURI, productInfo: response.data, arnaconResolver: arnaconResolver.address }; } catch (error) { console.log("Error getting NFT contract address:", error); } } return {expiry, owner, metadata: tokenURI}; } /** * Find first records for an ENS name by checking parent domains */ async findFirstRecords(ens) { const splittedEns = ens.split("."); let foundArnaconResolver; let foundProvisionRegistry; let foundResolverAddress; // Check the loop up until the TLD (e.g. global) for(let i = 0; i <= splittedEns.length - 2; i++){ const currentEns = splittedEns.slice(i).join("."); const {arnaconResolver, provisionRegistry, resolverAddress} = await this.getNameRecords(currentEns); if(arnaconResolver || provisionRegistry){ if(!foundArnaconResolver && arnaconResolver){ foundArnaconResolver = arnaconResolver; } if(!foundProvisionRegistry && provisionRegistry){ foundProvisionRegistry = provisionRegistry; } if(!foundResolverAddress && resolverAddress){ foundResolverAddress = resolverAddress; } } } return { arnaconResolver: foundArnaconResolver, provisionRegistry: foundProvisionRegistry, resolverAddress: foundResolverAddress }; } /** * Get name records for a specific ENS name */ async getNameRecords(ens) { const resolverAddress = await this.getResolver(ens); const node = namehash(ens); const result = {arnaconResolver: null, provisionRegistry: null, resolverAddress}; try { const publicResolver = await this.getDeployedContract("PublicResolver", resolverAddress); const arnaconResolverAddress = await publicResolver.text(node, "arnaconResolver"); const provisionRegistryAddress = await publicResolver.text(node, "provisionRegistry"); if(arnaconResolverAddress){ const arnaconResolver = await this.getDeployedContract("ArnaconResolver", arnaconResolverAddress); result.arnaconResolver = arnaconResolver; } if(provisionRegistryAddress){ const provisionRegistry = await this.getDeployedContract("ProvisionRegistry", provisionRegistryAddress); result.provisionRegistry = provisionRegistry; } return result; } catch (error) { console.log("Error getting Arnacon Resolver Address:", error); return result; } } /** * Get provision data from a name by traversing up the hierarchy */ async getProvision(name, identifier) { const splittedName = name.split("."); const ENS = await this.getContract("ENSRegistry"); for(let i = 0; i <= splittedName.length - 2; i++){ const currentName = splittedName.slice(i).join("."); console.log("Checking: ", currentName); try{ const resolverAddress = await ENS.resolver(namehash(currentName)); const publicResolver = await this.getDeployedContract("PublicResolver", resolverAddress); let provisionRegistryAddress = await publicResolver.text(namehash(currentName), "provisionRegistry"); if(!provisionRegistryAddress){ console.log("Provision Registry Address not found, using default address"); provisionRegistryAddress = (await this.getContract("ProvisionRegistry")).address; } if(provisionRegistryAddress){ try{ console.log("provisionRegistryAddress:", provisionRegistryAddress); const key = await publicResolver.text(namehash(currentName), identifier); console.log("Key:", key); if(!key){ return null; } const provisionRegistry = await this.getDeployedContract("ProvisionRegistry", provisionRegistryAddress); const provisionIpfs = await provisionRegistry.getProvision(key); if(provisionIpfs.startsWith("ipfs://")){ const provision = await axios.get(`https://plum-geographical-catfish-11.mypinata.cloud/ipfs/${provisionIpfs.replace("ipfs://", "")}`); return provision.data; } return provisionIpfs; } catch (error) { console.log("Error getting key:", error); } } } catch (error) { console.log("Error getting provision:", error); } } return null; } /** * Get expiry for a name from Name Wrapper */ async getExpiry(name) { const NameWrapper = await this.getContract("NameWrapper"); // Compute the tokenId correctly const tokenId = namehash(name); // Fetch expiry from Name Wrapper const data = await NameWrapper.getData(tokenId); const expiry = data.expiry; return expiry; } /** * Get owner of an ENS name */ async getEnsOwner(name) { const node = namehash(name); const EnsRegistry = await this.getContract("ENSRegistry"); const owner = await EnsRegistry.owner(node); return owner; } /** * Get wrapped owner of an ENS name */ async getWrapperOwner(name) { const node = namehash(name); const NameWrapper = await this.getContract("NameWrapper"); const owner = await NameWrapper.ownerOf(node); return owner; } async getOwner(name) { const owner = await this.getEnsOwner(name); const NameWrapper = await this.getContract("NameWrapper"); if(owner === NameWrapper.address){ const node = namehash(name); return await NameWrapper.ownerOf(node); } return owner; } /** * Get names for a specific address */ async getRegisteredNames(address) { const GlobalRegistrarController = await this.getContract("GlobalRegistrarController"); console.log("Global Registrar Controller: ", GlobalRegistrarController.address); const SecondLevelControllerAddress = await GlobalRegistrarController.get2LDControllerFor(address); console.log("Second Level Controller Address: ", SecondLevelControllerAddress); const SecondLevelController = await this.getDeployedContract("SecondLevelController", SecondLevelControllerAddress); const names = await SecondLevelController.getRegisteredNames(); console.log("got names: ", names); return names; } /** * Get resolver for an ENS name */ async getResolver(ens) { const ENS = await this.getContract("ENSRegistry"); const resolverAddress = await ENS.resolver(namehash(ens)); return resolverAddress; } /** * Get TTL for an ENS name */ async getTTL(ens) { const ENS = await this.getContract("ENSRegistry"); const ttl = await ENS.ttl(namehash(ens)); return ttl; } /** * Decode ENS name from hex */ decodeENSName(hex) { let bytes = ethers.utils.arrayify(hex); let result = []; let i = 0; while (i < bytes.length) { let length = bytes[i]; if (length === 0) break; // Null terminator found i++; // Move to actual characters result.push(ethers.utils.toUtf8String(bytes.slice(i, i + length))); i += length; } return result.join("."); } } module.exports = BlockchainExplorer;