@authereum/resolution
Version:
Domain Resolution for blockchain domains
681 lines (680 loc) • 30.1 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var bn_js_1 = __importDefault(require("bn.js"));
var Ens_1 = __importDefault(require("./Ens"));
var Zns_1 = __importDefault(require("./Zns"));
var Cns_1 = __importDefault(require("./Cns"));
var UdApi_1 = __importDefault(require("./UdApi"));
var publicTypes_1 = require("./publicTypes");
var resolutionError_1 = __importStar(require("./errors/resolutionError"));
var NamingService_1 = __importDefault(require("./NamingService"));
var utils_1 = require("./utils");
var Eip1993Factories_1 = require("./utils/Eip1993Factories");
var DnsUtils_1 = __importDefault(require("./DnsUtils"));
/**
* Blockchain domain Resolution library - Resolution.
* @example
* ```
* import Resolution from '@unstoppabledomains/resolution';
*
* let resolution = new Resolution({ blockchain: {
* ens: {
* url: "https://mainnet.infura.io/v3/12351245223",
* network: "mainnet"
* }
* }
* });
*
* let domain = "brad.zil";
* resolution.addr(domain, "eth").then(addr => console.log(addr));;
* ```
*/
var Resolution = /** @class */ (function () {
function Resolution(_a) {
var _b = _a === void 0 ? {} : _a, _c = _b.blockchain, blockchain = _c === void 0 ? true : _c, _d = _b.api, api = _d === void 0 ? publicTypes_1.DefaultAPI : _d;
this.blockchain = !!blockchain;
if (blockchain) {
if (blockchain === true) {
blockchain = {};
}
var web3provider = blockchain.web3Provider;
if (web3provider) {
console.warn('Usage of `web3Provider` option is deprecated. Use `provider` option instead for each individual blockchain');
}
var ens = this.normalizeSource(blockchain.ens, web3provider);
var zns = this.normalizeSource(blockchain.zns);
var cns = this.normalizeSource(blockchain.cns, web3provider);
if (ens) {
this.ens = new Ens_1.default(ens);
}
if (zns) {
this.zns = new Zns_1.default(zns);
}
if (cns) {
this.cns = new Cns_1.default(cns);
}
}
else {
this.api = new UdApi_1.default(api);
}
}
/**
* Creates a resolution with configured infura id for ens and cns
* @param infura infura project id
* @param network ethereum network name
*/
Resolution.infura = function (infura, network) {
if (network === void 0) { network = 'mainnet'; }
return new this({
blockchain: {
ens: { url: utils_1.signedInfuraLink(infura, network), network: network },
cns: { url: utils_1.signedInfuraLink(infura, network), network: network },
},
});
};
/**
* Creates a resolution instance with configured provider
* @param provider - any provider compatible with EIP-1193
* @see https://eips.ethereum.org/EIPS/eip-1193
*/
Resolution.fromEip1193Provider = function (provider) {
return new this({
blockchain: { zns: true, ens: { provider: provider }, cns: { provider: provider } },
});
};
/**
* Create a resolution instance from web3 0.x version provider
* @param provider - an 0.x version provider from web3 ( must implement sendAsync(payload, callback) )
* @see https://github.com/ethereum/web3.js/blob/0.20.7/lib/web3/httpprovider.js#L116
*/
Resolution.fromWeb3Version0Provider = function (provider) {
return this.fromEip1193Provider(Eip1993Factories_1.Eip1993Factories.fromWeb3Version0Provider(provider));
};
/**
* Create a resolution instance from web3 1.x version provider
* @param provider - an 1.x version provider from web3 ( must implement send(payload, callback) )
* @see https://github.com/ethereum/web3.js/blob/1.x/packages/web3-core-helpers/types/index.d.ts#L165
* @see https://github.com/ethereum/web3.js/blob/1.x/packages/web3-providers-http/src/index.js#L95
*/
Resolution.fromWeb3Version1Provider = function (provider) {
return this.fromEip1193Provider(Eip1993Factories_1.Eip1993Factories.fromWeb3Version1Provider(provider));
};
/**
* Creates instance of resolution from provider that implements Ethers Provider#call interface.
* This wrapper support only `eth_call` method for now, which is enough for all the current Resolution functionality
* @param provider - provider object
* @see https://github.com/ethers-io/ethers.js/blob/v4-legacy/providers/abstract-provider.d.ts#L91
* @see https://github.com/ethers-io/ethers.js/blob/v5.0.4/packages/abstract-provider/src.ts/index.ts#L224
* @see https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#jsonrpcprovider-inherits-from-provider
* @see https://github.com/ethers-io/ethers.js/blob/master/packages/providers/src.ts/json-rpc-provider.ts
*/
Resolution.fromEthersProvider = function (provider) {
return this.fromEip1193Provider(Eip1993Factories_1.Eip1993Factories.fromEthersProvider(provider));
};
/**
* Resolves the given domain
* @async
* @param domain - domain name to be resolved
* @returns A promise that resolves in an object
*/
Resolution.prototype.resolve = function (domain) {
return __awaiter(this, void 0, void 0, function () {
var method, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
return [4 /*yield*/, method.resolve(domain)];
case 1:
result = _a.sent();
return [2 /*return*/, result || publicTypes_1.UnclaimedDomainResponse];
}
});
});
};
/**
* Resolves given domain name to a specific currency address if exists
* @async
* @param domain - domain name to be resolved
* @param currencyTicker - currency ticker like BTC, ETH, ZIL
* @deprecated since Resolution v1.7.0
* @returns A promise that resolves in an address or null
*/
Resolution.prototype.address = function (domain, currencyTicker) {
return __awaiter(this, void 0, void 0, function () {
var error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.warn('Resolution#address is deprecated since v1.7.0, use Resolution#addr instead');
domain = this.prepareDomain(domain);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.addressOrThrow(domain, currencyTicker)];
case 2: return [2 /*return*/, _a.sent()];
case 3:
error_1 = _a.sent();
if (error_1 instanceof resolutionError_1.default) {
return [2 /*return*/, null];
}
else {
throw error_1;
}
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
};
/**
* Resolves given domain name to a specific currency address if exists
* @async
* @param domain - domain name to be resolved
* @param currencyTicker - currency ticker like BTC, ETH, ZIL
* @throws [[ResolutionError]] if address is not found
* @returns A promise that resolves in an address
*/
Resolution.prototype.addr = function (domain, currrencyTicker) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.record(domain, "crypto." + currrencyTicker.toUpperCase() + ".address")];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolves given domain name to a verified twitter handle
* @async
* @param domain - domain name to be resolved
* @throws [[ResolutionError]] if twitter is not found
* @returns A promise that resolves in a verified twitter handle
*/
Resolution.prototype.twitter = function (domain) {
return __awaiter(this, void 0, void 0, function () {
var namingService, method;
return __generator(this, function (_a) {
domain = this.prepareDomain(domain);
namingService = this.serviceName(domain);
if (namingService !== 'CNS') {
throw new resolutionError_1.default(resolutionError_1.ResolutionErrorCode.UnsupportedMethod, {
domain: domain,
methodName: 'twitter',
});
}
method = this.getNamingMethodOrThrow(domain);
return [2 /*return*/, method.twitter(domain)];
});
});
};
/**
* Resolve a chat id from the domain record
* @param domain - domain name to be resolved
* @throws [[ResolutionError]]
* @returns A promise that resolves in chatId
*/
Resolution.prototype.chatId = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.record(domain, 'gundb.username.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolve a gundb public key from the domain record
* @param domain - domain name to be resolved
* @throws [[ResolutionError]]
* @returns a promise that resolves in gundb public key
*/
Resolution.prototype.chatPk = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.record(domain, 'gundb.public_key.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolves the IPFS hash configured for domain records on ZNS
* @param domain - domain name
* @throws [[ResolutionError]]
*/
Resolution.prototype.ipfsHash = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
return [4 /*yield*/, this.getPreferableNewRecord(domain, 'dweb.ipfs.hash', 'ipfs.html.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolves the httpUrl attached to domain
* @param domain - domain name
*/
Resolution.prototype.httpUrl = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
return [4 /*yield*/, this.getPreferableNewRecord(domain, 'browser.redirect_url', 'ipfs.redirect_domain.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolves the ipfs redirect url for a supported domain records
* @deprecated since v1.0.15 use Resolution#httpUrl instead
* @param domain - domain name
* @throws [[ResolutionError]]
* @returns A Promise that resolves in redirect url
*/
Resolution.prototype.ipfsRedirect = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.warn('Resolution#ipfsRedirect is deprecated since v1.0.15, use Resolution#httpUrl instead');
return [4 /*yield*/, this.record(domain, 'ipfs.redirect_domain.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Resolves the ipfs email field from whois configurations
* @param domain - domain name
* @throws [[ResolutionError]]
* @returns A Promise that resolves in an email address configured for this domain whois
*/
Resolution.prototype.email = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.record(domain, 'whois.email.value')];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* @returns A specific currency address or throws an error
* @param domain domain name
* @param currencyTicker currency ticker such as
* - ZIL
* - BTC
* - ETH
* @throws [[ResolutionError]] if address is not found
* @deprecated since v1.7.0 use Resolution#addr instead
*/
Resolution.prototype.addressOrThrow = function (domain, currencyTicker) {
return __awaiter(this, void 0, void 0, function () {
var method, addr, error_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.warn('Resolution#addressOrThrow is deprecated since v1.7.0, use Resolution#addr instead');
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, method.record(domain, "crypto." + currencyTicker.toUpperCase() + ".address")];
case 2:
addr = _a.sent();
return [2 /*return*/, addr];
case 3:
error_2 = _a.sent();
// re-throw an error for back compatability. old method throws deprecated UnspecifiedCurrency code since before v1.7.0
if (error_2 instanceof resolutionError_1.default &&
error_2.code === resolutionError_1.ResolutionErrorCode.RecordNotFound) {
throw new resolutionError_1.default(resolutionError_1.ResolutionErrorCode.UnspecifiedCurrency, {
domain: domain,
currencyTicker: currencyTicker,
});
}
throw error_2;
case 4: return [2 /*return*/];
}
});
});
};
/**
* @returns the resolver address for a specific domain
* @param domain - domain to look for
*/
Resolution.prototype.resolver = function (domain) {
return __awaiter(this, void 0, void 0, function () {
var resolver;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
return [4 /*yield*/, this.getNamingMethodOrThrow(domain).resolver(domain)];
case 1:
resolver = _a.sent();
if (!resolver) {
throw new resolutionError_1.default(resolutionError_1.ResolutionErrorCode.UnspecifiedResolver, { domain: domain });
}
return [2 /*return*/, resolver];
}
});
});
};
/**
* @param domain - domain name
* @returns An owner address of the domain
*/
Resolution.prototype.owner = function (domain) {
return __awaiter(this, void 0, void 0, function () {
var method;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
return [4 /*yield*/, method.owner(domain)];
case 1: return [2 /*return*/, (_a.sent()) || null];
}
});
});
};
/**
* @param domain - domain name
* @param recordKey - a name of a record to be resolved
* @returns A record value promise for a given record name
*/
Resolution.prototype.record = function (domain, recordKey) {
return __awaiter(this, void 0, void 0, function () {
var method;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
return [4 /*yield*/, method.record(domain, recordKey)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* @param domain domain name
* @param keys Array of record keys to be resolved
* @returns A Promise with key-value mapping of domain records
*/
Resolution.prototype.records = function (domain, keys) {
return __awaiter(this, void 0, void 0, function () {
var method;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
return [4 /*yield*/, method.records(domain, keys)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* This method is only for ens at the moment. Reverse the ens address to a ens registered domain name
* @async
* @param address - address you wish to reverse
* @param currencyTicker - currency ticker like BTC, ETH, ZIL
* @returns Domain name attached to this address
*/
Resolution.prototype.reverse = function (address, currencyTicker) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.findNamingService(publicTypes_1.NamingServiceName.ENS).reverse(address, currencyTicker)];
});
});
};
/**
* @returns Produces a namehash from supported naming service in hex format with 0x prefix.
* Corresponds to ERC721 token id in case of Ethereum based naming service like ENS or CNS.
* @param domain domain name to be converted
* @param options formatting options
* @throws [[ResolutionError]] with UnsupportedDomain error code if domain extension is unknown
*/
Resolution.prototype.namehash = function (domain, options) {
if (options === void 0) { options = publicTypes_1.NamehashOptionsDefault; }
domain = this.prepareDomain(domain);
return this.formatNamehash(this.getNamingMethodOrThrow(domain).namehash(domain), options);
};
/**
* @returns a namehash of a subdomain with name label
* @param parent namehash of a parent domain
* @param label subdomain name
* @param method "ENS", "CNS" or "ZNS"
* @param options formatting options
*/
Resolution.prototype.childhash = function (parent, label, method, options) {
if (options === void 0) { options = publicTypes_1.NamehashOptionsDefault; }
return this.formatNamehash(this.findNamingService(method).childhash(parent, label), options);
};
Resolution.prototype.formatNamehash = function (hash, options) {
hash = hash.replace('0x', '');
if (options.format === 'dec') {
return new bn_js_1.default(hash, 'hex').toString(10);
}
else {
return options.prefix ? '0x' + hash : hash;
}
};
/**
* Checks weather the domain name matches the hash
* @param domain - domain name to check againt
* @param hash - hash obtained from the blockchain
*/
Resolution.prototype.isValidHash = function (domain, hash) {
domain = this.prepareDomain(domain);
return this.namehash(domain) === hash;
};
/**
* Checks if the domain name is valid according to naming service rules
* for valid domain names.
* Example: ENS doesn't allow domains that start from '-' symbol.
* @param domain - domain name to be checked
*/
Resolution.prototype.isSupportedDomain = function (domain) {
domain = this.prepareDomain(domain);
return !!this.getNamingMethod(domain);
};
/**
* Checks if the domain is supported by the specified network as well as if it is in valid format
* @param domain - domain name to be checked
*/
Resolution.prototype.isSupportedDomainInNetwork = function (domain) {
domain = this.prepareDomain(domain);
var method = this.getNamingMethod(domain);
return !!method && method.isSupportedNetwork();
};
/**
* Returns the name of the service for a domain ENS | CNS | ZNS
* @param domain - domain name to look for
*/
Resolution.prototype.serviceName = function (domain) {
domain = this.prepareDomain(domain);
return this.getNamingMethodOrThrow(domain).serviceName(domain);
};
/**
* Returns all record keys of the domain.
* This method is strongly unrecommended for production use due to lack of support for many ethereum service providers and low performance
* Method is not supported by ENS
* @param domain - domain name
*/
Resolution.prototype.allRecords = function (domain) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
domain = this.prepareDomain(domain);
return [4 /*yield*/, this.getNamingMethodOrThrow(domain).allRecords(domain)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
Resolution.prototype.dns = function (domain, types) {
return __awaiter(this, void 0, void 0, function () {
var dnsUtils, method, dnsRecordKeys, blockchainData;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
dnsUtils = new DnsUtils_1.default();
domain = this.prepareDomain(domain);
method = this.getNamingMethodOrThrow(domain);
dnsRecordKeys = this.getDnsRecordKeys(types);
return [4 /*yield*/, method.records(domain, dnsRecordKeys)];
case 1:
blockchainData = _a.sent();
return [2 /*return*/, dnsUtils.toList(blockchainData)];
}
});
});
};
Resolution.prototype.getDnsRecordKeys = function (types) {
var records = ['dns.ttl'];
types.forEach(function (type) {
records.push("dns." + type);
records.push("dns." + type + ".ttl");
});
return records;
};
Resolution.prototype.getPreferableNewRecord = function (domain, newRecord, oldRecord) {
return __awaiter(this, void 0, void 0, function () {
var records;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.records(domain, [newRecord, oldRecord])];
case 1:
records = _a.sent();
return [2 /*return*/, NamingService_1.default.ensureRecordPresence(domain, newRecord, records[newRecord] || records[oldRecord])];
}
});
});
};
Resolution.prototype.getNamingMethod = function (domain) {
return this.getResolutionMethods().find(function (method) {
return method.isSupportedDomain(domain);
});
};
Resolution.prototype.getResolutionMethods = function () {
return (this.blockchain
? [this.ens, this.zns, this.cns]
: [this.api]).filter(function (v) { return v; });
};
Resolution.prototype.getNamingMethodOrThrow = function (domain) {
var method = this.getNamingMethod(domain);
if (!method) {
throw new resolutionError_1.default(resolutionError_1.ResolutionErrorCode.UnsupportedDomain, {
domain: domain,
});
}
return method;
};
Resolution.prototype.findNamingService = function (name) {
var service = this.getResolutionMethods().find(function (m) { return m.name === name; });
if (!service) {
throw new resolutionError_1.default(resolutionError_1.ResolutionErrorCode.NamingServiceDown, {
method: name,
});
}
return service;
};
Resolution.prototype.prepareDomain = function (domain) {
return domain ? domain.trim().toLowerCase() : '';
};
Resolution.prototype.normalizeSource = function (source, provider) {
switch (typeof source) {
case 'undefined': {
return { provider: provider };
}
case 'boolean': {
return source ? { provider: provider } : false;
}
case 'string': {
return { url: source };
}
case 'object': {
return __assign({ provider: provider }, source);
}
}
throw new Error('Unsupported configuration');
};
return Resolution;
}());
exports.Resolution = Resolution;
exports.default = Resolution;