/* This file is part of web3.js. web3.js is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. web3.js is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ /** * @file ENS.js * * @author Samuel Furter * @date 2018 */ "use strict"; var config = require('./config'); var formatters = require('web3-core-helpers').formatters; var utils = require('web3-utils'); var Registry = require('./contracts/Registry'); var ResolverMethodHandler = require('./lib/ResolverMethodHandler'); var contenthash = require('./lib/contentHash'); /** * Constructs a new instance of ENS * * @param {Eth} eth * * @constructor */ function ENS(eth) { this.eth = eth; var registryAddress = null; this._detectedAddress = null; this._lastSyncCheck = null; Object.defineProperty(this, 'registry', { get: function () { return new Registry(this); }, enumerable: true }); Object.defineProperty(this, 'resolverMethodHandler', { get: function () { return new ResolverMethodHandler(this.registry); }, enumerable: true }); Object.defineProperty(this, 'registryAddress', { get: function () { return registryAddress; }, set: function (value) { if (value === null) { registryAddress = value; return; } registryAddress = formatters.inputAddressFormatter(value); }, enumerable: true }); } /** * Returns true if the given interfaceId is supported and otherwise false. * * @method supportsInterface * * @param {string} name * @param {string} interfaceId * @param {function} callback * * @callback callback callback(error, result) * @returns {Promise} */ ENS.prototype.supportsInterface = function (name, interfaceId, callback) { return this.getResolver(name).then(function (resolver) { if (!utils.isHexStrict(interfaceId)) { interfaceId = utils.sha3(interfaceId).slice(0, 10); } return resolver.methods.supportsInterface(interfaceId).call(callback); }).catch(function (error) { if (typeof callback === 'function') { callback(error, null); return; } throw error; }); }; /** * Returns the Resolver by the given address * * @deprecated Please use the "getResolver" method instead of "resolver" * * @method resolver * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {Promise} */ ENS.prototype.resolver = function (name, callback) { return this.registry.resolver(name, callback); }; /** * Returns the Resolver by the given address * * @method getResolver * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {Promise} */ ENS.prototype.getResolver = function (name, callback) { return this.registry.getResolver(name, callback); }; /** * Does set the resolver of the given name * * @method setResolver * * @param {string} name * @param {string} address * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setResolver = function (name, address, txConfig, callback) { return this.registry.setResolver(name, address, txConfig, callback); }; /** * Sets the owner, resolver, and TTL for an ENS record in a single operation. * * @method setRecord * * @param {string} name * @param {string} owner * @param {string} resolver * @param {string | number} ttl * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setRecord = function (name, owner, resolver, ttl, txConfig, callback) { return this.registry.setRecord(name, owner, resolver, ttl, txConfig, callback); }; /** * Sets the owner, resolver and TTL for a subdomain, creating it if necessary. * * @method setSubnodeRecord * * @param {string} name * @param {string} label * @param {string} owner * @param {string} resolver * @param {string | number} ttl * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setSubnodeRecord = function (name, label, owner, resolver, ttl, txConfig, callback) { return this.registry.setSubnodeRecord(name, label, owner, resolver, ttl, txConfig, callback); }; /** * Sets or clears an approval by the given operator. * * @method setApprovalForAll * * @param {string} operator * @param {boolean} approved * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setApprovalForAll = function (operator, approved, txConfig, callback) { return this.registry.setApprovalForAll(operator, approved, txConfig, callback); }; /** * Returns true if the operator is approved * * @method isApprovedForAll * * @param {string} owner * @param {string} operator * @param {function} callback * * @callback callback callback(error, result) * @returns {Promise} */ ENS.prototype.isApprovedForAll = function (owner, operator, callback) { return this.registry.isApprovedForAll(owner, operator, callback); }; /** * Returns true if the record exists * * @method recordExists * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {Promise} */ ENS.prototype.recordExists = function (name, callback) { return this.registry.recordExists(name, callback); }; /** * Returns the address of the owner of an ENS name. * * @method setSubnodeOwner * * @param {string} name * @param {string} label * @param {string} address * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setSubnodeOwner = function (name, label, address, txConfig, callback) { return this.registry.setSubnodeOwner(name, label, address, txConfig, callback); }; /** * Returns the address of the owner of an ENS name. * * @method getTTL * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getTTL = function (name, callback) { return this.registry.getTTL(name, callback); }; /** * Returns the address of the owner of an ENS name. * * @method setTTL * * @param {string} name * @param {number} ttl * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setTTL = function (name, ttl, txConfig, callback) { return this.registry.setTTL(name, ttl, txConfig, callback); }; /** * Returns the owner by the given name and current configured or detected Registry * * @method getOwner * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getOwner = function (name, callback) { return this.registry.getOwner(name, callback); }; /** * Returns the address of the owner of an ENS name. * * @method setOwner * * @param {string} name * @param {string} address * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setOwner = function (name, address, txConfig, callback) { return this.registry.setOwner(name, address, txConfig, callback); }; /** * Returns the address record associated with a name. * * @method getAddress * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getAddress = function (name, callback) { return this.resolverMethodHandler.method(name, 'addr', []).call(callback); }; /** * Sets a new address * * @method setAddress * * @param {string} name * @param {string} address * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setAddress = function (name, address, txConfig, callback) { return this.resolverMethodHandler.method(name, 'setAddr', [address]).send(txConfig, callback); }; /** * Returns the public key * * @method getPubkey * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getPubkey = function (name, callback) { return this.resolverMethodHandler.method(name, 'pubkey', [], null, callback).call(callback); }; /** * Set the new public key * * @method setPubkey * * @param {string} name * @param {string} x * @param {string} y * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setPubkey = function (name, x, y, txConfig, callback) { return this.resolverMethodHandler.method(name, 'setPubkey', [x, y]).send(txConfig, callback); }; /** * Returns the content * * @method getContent * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getContent = function (name, callback) { return this.resolverMethodHandler.method(name, 'content', []).call(callback); }; /** * Set the content * * @method setContent * * @param {string} name * @param {string} hash * @param {function} callback * @param {TransactionConfig} txConfig * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setContent = function (name, hash, txConfig, callback) { return this.resolverMethodHandler.method(name, 'setContent', [hash]).send(txConfig, callback); }; /** * Returns the contenthash * * @method getContenthash * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getContenthash = function (name, callback) { return this.resolverMethodHandler.method(name, 'contenthash', [], contenthash.decode).call(callback); }; /** * Set the contenthash * * @method setContent * * @param {string} name * @param {string} hash * @param {function} callback * @param {TransactionConfig} txConfig * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setContenthash = function (name, hash, txConfig, callback) { var encoded; try { encoded = contenthash.encode(hash); } catch (err) { var error = new Error('Could not encode ' + hash + '. See docs for supported hash protocols.'); if (typeof callback === 'function') { callback(error, null); return; } throw error; } return this.resolverMethodHandler.method(name, 'setContenthash', [encoded]).send(txConfig, callback); }; /** * Get the multihash * * @method getMultihash * * @param {string} name * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.getMultihash = function (name, callback) { return this.resolverMethodHandler.method(name, 'multihash', []).call(callback); }; /** * Set the multihash * * @method setMultihash * * @param {string} name * @param {string} hash * @param {TransactionConfig} txConfig * @param {function} callback * * @callback callback callback(error, result) * @returns {PromiEvent} */ ENS.prototype.setMultihash = function (name, hash, txConfig, callback) { return this.resolverMethodHandler.method(name, 'multihash', [hash]).send(txConfig, callback); }; /** * Checks if the current used network is synced and looks for ENS support there. * Throws an error if not. * * @returns {Promise} */ ENS.prototype.checkNetwork = async function () { var now = new Date() / 1000; if (!this._lastSyncCheck || (now - this._lastSyncCheck) > 3600) { var block = await this.eth.getBlock('latest'); var headAge = now - block.timestamp; if (headAge > 3600) { throw new Error("Network not synced; last block was " + headAge + " seconds ago"); } this._lastSyncCheck = now; } if (this.registryAddress) { return this.registryAddress; } if (!this._detectedAddress) { var networkType = await this.eth.net.getNetworkType(); var addr = config.addresses[networkType]; if (typeof addr === 'undefined') { throw new Error("ENS is not supported on network " + networkType); } this._detectedAddress = addr; return this._detectedAddress; } return this._detectedAddress; }; module.exports = ENS;