arnacon-sdk
Version:
A comprehensive SDK for deploying and managing Arnacon smart contracts across multiple networks
309 lines (266 loc) • 11.6 kB
JavaScript
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;