| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 | 
							- /**
 
-  *  ENS is a service which allows easy-to-remember names to map to
 
-  *  network addresses.
 
-  *
 
-  *  @_section: api/providers/ens-resolver:ENS Resolver  [about-ens-rsolver]
 
-  */
 
- import { getAddress } from "../address/index.js";
 
- import { ZeroAddress } from "../constants/index.js";
 
- import { Contract } from "../contract/index.js";
 
- import { dnsEncode, namehash } from "../hash/index.js";
 
- import { hexlify, isHexString, toBeHex, defineProperties, encodeBase58, assert, assertArgument, isError, FetchRequest } from "../utils/index.js";
 
- // @TODO: This should use the fetch-data:ipfs gateway
 
- // Trim off the ipfs:// prefix and return the default gateway URL
 
- function getIpfsLink(link) {
 
-     if (link.match(/^ipfs:\/\/ipfs\//i)) {
 
-         link = link.substring(12);
 
-     }
 
-     else if (link.match(/^ipfs:\/\//i)) {
 
-         link = link.substring(7);
 
-     }
 
-     else {
 
-         assertArgument(false, "unsupported IPFS format", "link", link);
 
-     }
 
-     return `https:/\/gateway.ipfs.io/ipfs/${link}`;
 
- }
 
- ;
 
- ;
 
- /**
 
-  *  A provider plugin super-class for processing multicoin address types.
 
-  */
 
- export class MulticoinProviderPlugin {
 
-     /**
 
-      *  The name.
 
-      */
 
-     name;
 
-     /**
 
-      *  Creates a new **MulticoinProviderPluing** for %%name%%.
 
-      */
 
-     constructor(name) {
 
-         defineProperties(this, { name });
 
-     }
 
-     connect(proivder) {
 
-         return this;
 
-     }
 
-     /**
 
-      *  Returns ``true`` if %%coinType%% is supported by this plugin.
 
-      */
 
-     supportsCoinType(coinType) {
 
-         return false;
 
-     }
 
-     /**
 
-      *  Resolves to the encoded %%address%% for %%coinType%%.
 
-      */
 
-     async encodeAddress(coinType, address) {
 
-         throw new Error("unsupported coin");
 
-     }
 
-     /**
 
-      *  Resolves to the decoded %%data%% for %%coinType%%.
 
-      */
 
-     async decodeAddress(coinType, data) {
 
-         throw new Error("unsupported coin");
 
-     }
 
- }
 
- const BasicMulticoinPluginId = "org.ethers.plugins.provider.BasicMulticoin";
 
- /**
 
-  *  A **BasicMulticoinProviderPlugin** provides service for common
 
-  *  coin types, which do not require additional libraries to encode or
 
-  *  decode.
 
-  */
 
- export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin {
 
-     /**
 
-      *  Creates a new **BasicMulticoinProviderPlugin**.
 
-      */
 
-     constructor() {
 
-         super(BasicMulticoinPluginId);
 
-     }
 
- }
 
- const matcherIpfs = new RegExp("^(ipfs):/\/(.*)$", "i");
 
- const matchers = [
 
-     new RegExp("^(https):/\/(.*)$", "i"),
 
-     new RegExp("^(data):(.*)$", "i"),
 
-     matcherIpfs,
 
-     new RegExp("^eip155:[0-9]+/(erc[0-9]+):(.*)$", "i"),
 
- ];
 
- /**
 
-  *  A connected object to a resolved ENS name resolver, which can be
 
-  *  used to query additional details.
 
-  */
 
- export class EnsResolver {
 
-     /**
 
-      *  The connected provider.
 
-      */
 
-     provider;
 
-     /**
 
-      *  The address of the resolver.
 
-      */
 
-     address;
 
-     /**
 
-      *  The name this resolver was resolved against.
 
-      */
 
-     name;
 
-     // For EIP-2544 names, the ancestor that provided the resolver
 
-     #supports2544;
 
-     #resolver;
 
-     constructor(provider, address, name) {
 
-         defineProperties(this, { provider, address, name });
 
-         this.#supports2544 = null;
 
-         this.#resolver = new Contract(address, [
 
-             "function supportsInterface(bytes4) view returns (bool)",
 
-             "function resolve(bytes, bytes) view returns (bytes)",
 
-             "function addr(bytes32) view returns (address)",
 
-             "function addr(bytes32, uint) view returns (bytes)",
 
-             "function text(bytes32, string) view returns (string)",
 
-             "function contenthash(bytes32) view returns (bytes)",
 
-         ], provider);
 
-     }
 
-     /**
 
-      *  Resolves to true if the resolver supports wildcard resolution.
 
-      */
 
-     async supportsWildcard() {
 
-         if (this.#supports2544 == null) {
 
-             this.#supports2544 = (async () => {
 
-                 try {
 
-                     return await this.#resolver.supportsInterface("0x9061b923");
 
-                 }
 
-                 catch (error) {
 
-                     // Wildcard resolvers must understand supportsInterface
 
-                     // and return true.
 
-                     if (isError(error, "CALL_EXCEPTION")) {
 
-                         return false;
 
-                     }
 
-                     // Let future attempts try again...
 
-                     this.#supports2544 = null;
 
-                     throw error;
 
-                 }
 
-             })();
 
-         }
 
-         return await this.#supports2544;
 
-     }
 
-     async #fetch(funcName, params) {
 
-         params = (params || []).slice();
 
-         const iface = this.#resolver.interface;
 
-         // The first parameters is always the nodehash
 
-         params.unshift(namehash(this.name));
 
-         let fragment = null;
 
-         if (await this.supportsWildcard()) {
 
-             fragment = iface.getFunction(funcName);
 
-             assert(fragment, "missing fragment", "UNKNOWN_ERROR", {
 
-                 info: { funcName }
 
-             });
 
-             params = [
 
-                 dnsEncode(this.name, 255),
 
-                 iface.encodeFunctionData(fragment, params)
 
-             ];
 
-             funcName = "resolve(bytes,bytes)";
 
-         }
 
-         params.push({
 
-             enableCcipRead: true
 
-         });
 
-         try {
 
-             const result = await this.#resolver[funcName](...params);
 
-             if (fragment) {
 
-                 return iface.decodeFunctionResult(fragment, result)[0];
 
-             }
 
-             return result;
 
-         }
 
-         catch (error) {
 
-             if (!isError(error, "CALL_EXCEPTION")) {
 
-                 throw error;
 
-             }
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      *  Resolves to the address for %%coinType%% or null if the
 
-      *  provided %%coinType%% has not been configured.
 
-      */
 
-     async getAddress(coinType) {
 
-         if (coinType == null) {
 
-             coinType = 60;
 
-         }
 
-         if (coinType === 60) {
 
-             try {
 
-                 const result = await this.#fetch("addr(bytes32)");
 
-                 // No address
 
-                 if (result == null || result === ZeroAddress) {
 
-                     return null;
 
-                 }
 
-                 return result;
 
-             }
 
-             catch (error) {
 
-                 if (isError(error, "CALL_EXCEPTION")) {
 
-                     return null;
 
-                 }
 
-                 throw error;
 
-             }
 
-         }
 
-         // Try decoding its EVM canonical chain as an EVM chain address first
 
-         if (coinType >= 0 && coinType < 0x80000000) {
 
-             let ethCoinType = coinType + 0x80000000;
 
-             const data = await this.#fetch("addr(bytes32,uint)", [ethCoinType]);
 
-             if (isHexString(data, 20)) {
 
-                 return getAddress(data);
 
-             }
 
-         }
 
-         let coinPlugin = null;
 
-         for (const plugin of this.provider.plugins) {
 
-             if (!(plugin instanceof MulticoinProviderPlugin)) {
 
-                 continue;
 
-             }
 
-             if (plugin.supportsCoinType(coinType)) {
 
-                 coinPlugin = plugin;
 
-                 break;
 
-             }
 
-         }
 
-         if (coinPlugin == null) {
 
-             return null;
 
-         }
 
-         // keccak256("addr(bytes32,uint256")
 
-         const data = await this.#fetch("addr(bytes32,uint)", [coinType]);
 
-         // No address
 
-         if (data == null || data === "0x") {
 
-             return null;
 
-         }
 
-         // Compute the address
 
-         const address = await coinPlugin.decodeAddress(coinType, data);
 
-         if (address != null) {
 
-             return address;
 
-         }
 
-         assert(false, `invalid coin data`, "UNSUPPORTED_OPERATION", {
 
-             operation: `getAddress(${coinType})`,
 
-             info: { coinType, data }
 
-         });
 
-     }
 
-     /**
 
-      *  Resolves to the EIP-634 text record for %%key%%, or ``null``
 
-      *  if unconfigured.
 
-      */
 
-     async getText(key) {
 
-         const data = await this.#fetch("text(bytes32,string)", [key]);
 
-         if (data == null || data === "0x") {
 
-             return null;
 
-         }
 
-         return data;
 
-     }
 
-     /**
 
-      *  Rsolves to the content-hash or ``null`` if unconfigured.
 
-      */
 
-     async getContentHash() {
 
-         // keccak256("contenthash()")
 
-         const data = await this.#fetch("contenthash(bytes32)");
 
-         // No contenthash
 
-         if (data == null || data === "0x") {
 
-             return null;
 
-         }
 
-         // IPFS (CID: 1, Type: 70=DAG-PB, 72=libp2p-key)
 
-         const ipfs = data.match(/^0x(e3010170|e5010172)(([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f]*))$/);
 
-         if (ipfs) {
 
-             const scheme = (ipfs[1] === "e3010170") ? "ipfs" : "ipns";
 
-             const length = parseInt(ipfs[4], 16);
 
-             if (ipfs[5].length === length * 2) {
 
-                 return `${scheme}:/\/${encodeBase58("0x" + ipfs[2])}`;
 
-             }
 
-         }
 
-         // Swarm (CID: 1, Type: swarm-manifest; hash/length hard-coded to keccak256/32)
 
-         const swarm = data.match(/^0xe40101fa011b20([0-9a-f]*)$/);
 
-         if (swarm && swarm[1].length === 64) {
 
-             return `bzz:/\/${swarm[1]}`;
 
-         }
 
-         assert(false, `invalid or unsupported content hash data`, "UNSUPPORTED_OPERATION", {
 
-             operation: "getContentHash()",
 
-             info: { data }
 
-         });
 
-     }
 
-     /**
 
-      *  Resolves to the avatar url or ``null`` if the avatar is either
 
-      *  unconfigured or incorrectly configured (e.g. references an NFT
 
-      *  not owned by the address).
 
-      *
 
-      *  If diagnosing issues with configurations, the [[_getAvatar]]
 
-      *  method may be useful.
 
-      */
 
-     async getAvatar() {
 
-         const avatar = await this._getAvatar();
 
-         return avatar.url;
 
-     }
 
-     /**
 
-      *  When resolving an avatar, there are many steps involved, such
 
-      *  fetching metadata and possibly validating ownership of an
 
-      *  NFT.
 
-      *
 
-      *  This method can be used to examine each step and the value it
 
-      *  was working from.
 
-      */
 
-     async _getAvatar() {
 
-         const linkage = [{ type: "name", value: this.name }];
 
-         try {
 
-             // test data for ricmoo.eth
 
-             //const avatar = "eip155:1/erc721:0x265385c7f4132228A0d54EB1A9e7460b91c0cC68/29233";
 
-             const avatar = await this.getText("avatar");
 
-             if (avatar == null) {
 
-                 linkage.push({ type: "!avatar", value: "" });
 
-                 return { url: null, linkage };
 
-             }
 
-             linkage.push({ type: "avatar", value: avatar });
 
-             for (let i = 0; i < matchers.length; i++) {
 
-                 const match = avatar.match(matchers[i]);
 
-                 if (match == null) {
 
-                     continue;
 
-                 }
 
-                 const scheme = match[1].toLowerCase();
 
-                 switch (scheme) {
 
-                     case "https":
 
-                     case "data":
 
-                         linkage.push({ type: "url", value: avatar });
 
-                         return { linkage, url: avatar };
 
-                     case "ipfs": {
 
-                         const url = getIpfsLink(avatar);
 
-                         linkage.push({ type: "ipfs", value: avatar });
 
-                         linkage.push({ type: "url", value: url });
 
-                         return { linkage, url };
 
-                     }
 
-                     case "erc721":
 
-                     case "erc1155": {
 
-                         // Depending on the ERC type, use tokenURI(uint256) or url(uint256)
 
-                         const selector = (scheme === "erc721") ? "tokenURI(uint256)" : "uri(uint256)";
 
-                         linkage.push({ type: scheme, value: avatar });
 
-                         // The owner of this name
 
-                         const owner = await this.getAddress();
 
-                         if (owner == null) {
 
-                             linkage.push({ type: "!owner", value: "" });
 
-                             return { url: null, linkage };
 
-                         }
 
-                         const comps = (match[2] || "").split("/");
 
-                         if (comps.length !== 2) {
 
-                             linkage.push({ type: `!${scheme}caip`, value: (match[2] || "") });
 
-                             return { url: null, linkage };
 
-                         }
 
-                         const tokenId = comps[1];
 
-                         const contract = new Contract(comps[0], [
 
-                             // ERC-721
 
-                             "function tokenURI(uint) view returns (string)",
 
-                             "function ownerOf(uint) view returns (address)",
 
-                             // ERC-1155
 
-                             "function uri(uint) view returns (string)",
 
-                             "function balanceOf(address, uint256) view returns (uint)"
 
-                         ], this.provider);
 
-                         // Check that this account owns the token
 
-                         if (scheme === "erc721") {
 
-                             const tokenOwner = await contract.ownerOf(tokenId);
 
-                             if (owner !== tokenOwner) {
 
-                                 linkage.push({ type: "!owner", value: tokenOwner });
 
-                                 return { url: null, linkage };
 
-                             }
 
-                             linkage.push({ type: "owner", value: tokenOwner });
 
-                         }
 
-                         else if (scheme === "erc1155") {
 
-                             const balance = await contract.balanceOf(owner, tokenId);
 
-                             if (!balance) {
 
-                                 linkage.push({ type: "!balance", value: "0" });
 
-                                 return { url: null, linkage };
 
-                             }
 
-                             linkage.push({ type: "balance", value: balance.toString() });
 
-                         }
 
-                         // Call the token contract for the metadata URL
 
-                         let metadataUrl = await contract[selector](tokenId);
 
-                         if (metadataUrl == null || metadataUrl === "0x") {
 
-                             linkage.push({ type: "!metadata-url", value: "" });
 
-                             return { url: null, linkage };
 
-                         }
 
-                         linkage.push({ type: "metadata-url-base", value: metadataUrl });
 
-                         // ERC-1155 allows a generic {id} in the URL
 
-                         if (scheme === "erc1155") {
 
-                             metadataUrl = metadataUrl.replace("{id}", toBeHex(tokenId, 32).substring(2));
 
-                             linkage.push({ type: "metadata-url-expanded", value: metadataUrl });
 
-                         }
 
-                         // Transform IPFS metadata links
 
-                         if (metadataUrl.match(/^ipfs:/i)) {
 
-                             metadataUrl = getIpfsLink(metadataUrl);
 
-                         }
 
-                         linkage.push({ type: "metadata-url", value: metadataUrl });
 
-                         // Get the token metadata
 
-                         let metadata = {};
 
-                         const response = await (new FetchRequest(metadataUrl)).send();
 
-                         response.assertOk();
 
-                         try {
 
-                             metadata = response.bodyJson;
 
-                         }
 
-                         catch (error) {
 
-                             try {
 
-                                 linkage.push({ type: "!metadata", value: response.bodyText });
 
-                             }
 
-                             catch (error) {
 
-                                 const bytes = response.body;
 
-                                 if (bytes) {
 
-                                     linkage.push({ type: "!metadata", value: hexlify(bytes) });
 
-                                 }
 
-                                 return { url: null, linkage };
 
-                             }
 
-                             return { url: null, linkage };
 
-                         }
 
-                         if (!metadata) {
 
-                             linkage.push({ type: "!metadata", value: "" });
 
-                             return { url: null, linkage };
 
-                         }
 
-                         linkage.push({ type: "metadata", value: JSON.stringify(metadata) });
 
-                         // Pull the image URL out
 
-                         let imageUrl = metadata.image;
 
-                         if (typeof (imageUrl) !== "string") {
 
-                             linkage.push({ type: "!imageUrl", value: "" });
 
-                             return { url: null, linkage };
 
-                         }
 
-                         if (imageUrl.match(/^(https:\/\/|data:)/i)) {
 
-                             // Allow
 
-                         }
 
-                         else {
 
-                             // Transform IPFS link to gateway
 
-                             const ipfs = imageUrl.match(matcherIpfs);
 
-                             if (ipfs == null) {
 
-                                 linkage.push({ type: "!imageUrl-ipfs", value: imageUrl });
 
-                                 return { url: null, linkage };
 
-                             }
 
-                             linkage.push({ type: "imageUrl-ipfs", value: imageUrl });
 
-                             imageUrl = getIpfsLink(imageUrl);
 
-                         }
 
-                         linkage.push({ type: "url", value: imageUrl });
 
-                         return { linkage, url: imageUrl };
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         catch (error) { }
 
-         return { linkage, url: null };
 
-     }
 
-     static async getEnsAddress(provider) {
 
-         const network = await provider.getNetwork();
 
-         const ensPlugin = network.getPlugin("org.ethers.plugins.network.Ens");
 
-         // No ENS...
 
-         assert(ensPlugin, "network does not support ENS", "UNSUPPORTED_OPERATION", {
 
-             operation: "getEnsAddress", info: { network }
 
-         });
 
-         return ensPlugin.address;
 
-     }
 
-     static async #getResolver(provider, name) {
 
-         const ensAddr = await EnsResolver.getEnsAddress(provider);
 
-         try {
 
-             const contract = new Contract(ensAddr, [
 
-                 "function resolver(bytes32) view returns (address)"
 
-             ], provider);
 
-             const addr = await contract.resolver(namehash(name), {
 
-                 enableCcipRead: true
 
-             });
 
-             if (addr === ZeroAddress) {
 
-                 return null;
 
-             }
 
-             return addr;
 
-         }
 
-         catch (error) {
 
-             // ENS registry cannot throw errors on resolver(bytes32),
 
-             // so probably a link error
 
-             throw error;
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      *  Resolve to the ENS resolver for %%name%% using %%provider%% or
 
-      *  ``null`` if unconfigured.
 
-      */
 
-     static async fromName(provider, name) {
 
-         let currentName = name;
 
-         while (true) {
 
-             if (currentName === "" || currentName === ".") {
 
-                 return null;
 
-             }
 
-             // Optimization since the eth node cannot change and does
 
-             // not have a wildcard resolver
 
-             if (name !== "eth" && currentName === "eth") {
 
-                 return null;
 
-             }
 
-             // Check the current node for a resolver
 
-             const addr = await EnsResolver.#getResolver(provider, currentName);
 
-             // Found a resolver!
 
-             if (addr != null) {
 
-                 const resolver = new EnsResolver(provider, addr, name);
 
-                 // Legacy resolver found, using EIP-2544 so it isn't safe to use
 
-                 if (currentName !== name && !(await resolver.supportsWildcard())) {
 
-                     return null;
 
-                 }
 
-                 return resolver;
 
-             }
 
-             // Get the parent node
 
-             currentName = currentName.split(".").slice(1).join(".");
 
-         }
 
-     }
 
- }
 
- //# sourceMappingURL=ens-resolver.js.map
 
 
  |