UNPKG

@npmjs_tdsoftware/subidentity

Version:

This package provides functionality to fetch identities and search identities by address or any identity field from any Substrate chain implementing the identities pallet. It was developed for use in SubIdentity, a Substrate identity directory, and contai

341 lines (340 loc) 14.6 kB
"use strict"; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAccountBalance = exports.getIdentity = exports.searchIdentities = exports.getCompleteIdentities = exports.getIdentities = exports.implementsIdentityPallet = exports.tokenSymbol = void 0; require("@polkadot/api-augment"); const pagination_1 = require("./pagination"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const util_1 = require("@polkadot/util"); const utilities_1 = require("./utilities"); exports.tokenSymbol = {}; /** * check if chain of provided endpoint implements the identity pallet * @param wsAddress Network end point URL * @returns true if identity pallet is implemented */ const implementsIdentityPallet = (wsAddress) => __awaiter(void 0, void 0, void 0, function* () { const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); return typeof api.query.identity !== "undefined"; }); exports.implementsIdentityPallet = implementsIdentityPallet; /** * fetch identitites from a selected substrate based chain * @param wsAddress Network end point URL * @param page requested page number * @param limit number of identity items per each page * @throws TypeError when pagination details are invalid * @returns requested page with identitites */ const getIdentities = (wsAddress, page, limit) => __awaiter(void 0, void 0, void 0, function* () { if (!(0, pagination_1._validatePaginationInput)(page, limit)) throw TypeError("Please provide valid page number or limit"); const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); const chain = (yield api.rpc.system.chain()); return _getIdentityEntries(api, chain.toString(), page, limit); }); exports.getIdentities = getIdentities; /** * fetch all identitites from a selected substrate based chain including their judgements * @param wsAddress Network end point URL * @throws Error when identity pallet is not implemented on selected substrate chain */ const getCompleteIdentities = (wsAddress) => __awaiter(void 0, void 0, void 0, function* () { const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); try { const list = yield api.query.identity.identityOf.entries(); const identities = list.map((identity) => { const { display: { Raw: display }, email: { Raw: email }, legal: { Raw: legal }, riot: { Raw: riot }, twitter: { Raw: twitter }, web: { Raw: web } } = identity[1].toHuman().info; const addressArray = identity[0].toHuman(); let address = ""; if (Array.isArray(addressArray) && addressArray.length > 0) { address = `${addressArray[0]}`; } const judgements = []; if (identity[1].toHuman().judgements) { identity[1].toHuman().judgements.forEach((item) => { if (typeof item[1] === "object") judgements.push(Object.keys(item[1])[0].toString()); else judgements.push(item[1]); }); } const basicInfo = { display, address, riot, twitter, web, legal, email }; return { basicInfo, judgements }; }); return identities; } catch (e) { const message = (yield (0, exports.implementsIdentityPallet)(wsAddress)) ? ("Something went wrong while fetching identities: " + e) : "Can not fetch Identities for a chain that does not implement the identity pallet."; throw new Error(message); } }); exports.getCompleteIdentities = getCompleteIdentities; function _getIdentityEntries(api, chainName, page, limit) { return __awaiter(this, void 0, void 0, function* () { const entries = yield _getBasicInfoOfIdentities(api); const identities = []; entries.forEach(function (value) { const basicInfo = value; const chain = chainName; const identity = { chain, basicInfo }; identities.push(identity); }); return (0, pagination_1._paginate)(identities, page, limit); }); } function _getBasicInfoOfIdentities(api) { return __awaiter(this, void 0, void 0, function* () { const list = yield api.query.identity.identityOf.entries(); return list.map((identity) => { const { display: { Raw: display }, email: { Raw: email }, legal: { Raw: legal }, riot: { Raw: riot }, twitter: { Raw: twitter }, web: { Raw: web } } = identity[1].toHuman().info; const addressArray = identity[0].toHuman(); let address = ""; if (Array.isArray(addressArray) && addressArray.length > 0) { address = `${addressArray[0]}`; } let parsedDisplay = ""; if (display && /^0x/.test(display)) { const { info: { display } } = identity[1].unwrap(); parsedDisplay = (0, util_1.u8aToString)(display.asRaw.toU8a(true)); } return { display: parsedDisplay || display, address, riot, twitter, web, legal, email }; }); }); } /** * search for identitites in a selected substrate based chain * @param wsAddress Network end point URL * @param query search key * @param page requested page number * @param limit number of identity items per each page * @throws TypeError when pagination details are invalid * @returns requested page with identitites matching search query */ const searchIdentities = (wsAddress, query, page, limit) => __awaiter(void 0, void 0, void 0, function* () { if (!(0, pagination_1._validatePaginationInput)(page, limit)) throw TypeError("Please provide valid page number or limit"); if (!query) { return (0, exports.getIdentities)(wsAddress, page, limit); } const searchResults = []; const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); const chainName = ((yield api.rpc.system.chain())).toString(); query = query.trim(); const [fromIndex, fromFields] = yield Promise.all([ _getIdentityFromIndex(api, query), _getIdentityFromFields(api, query) ]); if (fromIndex) { const basicInfo = fromIndex; const chain = chainName; const ident = { chain, basicInfo }; searchResults.push(ident); } fromFields.forEach(function (value) { const basicInfo = value; const chain = chainName; const identity = { chain, basicInfo }; searchResults.push(identity); }); return (0, pagination_1._paginate)(searchResults, page, limit); }); exports.searchIdentities = searchIdentities; function _getIdentityFromIndex(api, index) { var _a; return __awaiter(this, void 0, void 0, function* () { try { const numberRegex = new RegExp("^[0-9]+$"); if (!numberRegex.test(index)) return; const accountData = yield api.query.indices.accounts(index); const account = accountData.toHuman(); if (Array.isArray(account)) { const address = (_a = account[0]) === null || _a === void 0 ? void 0 : _a.toString(); const identity = yield api.derive.accounts.identity(address); //check if Account has an identity if (!Object.prototype.hasOwnProperty.call(identity, "display")) return; return Object.assign(Object.assign({}, identity), { address }); } } catch (ex) { return; } }); } function _getIdentityFromFields(api, field) { return __awaiter(this, void 0, void 0, function* () { const allIdentities = yield _getBasicInfoOfIdentities(api); let query; try { query = new RegExp(`${field}`, "i"); } catch (ex) { throw TypeError("Your search key may contain special characters. Please try escaping them for search. e.g., /*"); } let identities = allIdentities; if (field) { identities = allIdentities .filter((user) => { const { display, address, riot, twitter, web, legal, email } = user; switch (true) { case display && query.test(display): return true; case email && query.test(email): return true; case legal && query.test(legal): return true; case riot && query.test(riot): return true; case twitter && query.test(twitter): return true; case web && query.test(web): return true; case address && query.test(address): return true; default: return false; } }) .sort((a, b) => { const nameA = (a.legal && a.legal.toLowerCase()) || (a.display && a.display.toLowerCase()) || a.address || ""; const nameB = (b.legal && b.legal.toLowerCase()) || (b.display && b.display.toLowerCase()) || b.address || ""; switch (true) { case query.test(nameA) && query.test(nameB) && nameA > nameB: return 1; case query.test(nameA) && query.test(nameB) && nameB > nameA: return -1; case query.test(nameA) && !query.test(nameB): return -1; case !query.test(nameA) && query.test(nameB): return 1; case nameA > nameB: return 1; case nameB > nameA: return -1; default: return 0; } }); } return identities; }); } /** * fetch an Identity from a selected substrate based chain and address * @param wsAddress Network end point URL * @param address * @throws TypeError when an identity with the given address can not be fetched * @returns requested Identitity */ const getIdentity = (wsAddress, address) => __awaiter(void 0, void 0, void 0, function* () { const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); let identity; if (api) { try { identity = yield api.derive.accounts.identity(address); if (!Object.prototype.hasOwnProperty.call(identity, "display")) throw TypeError; } catch (ex) { throw TypeError("Unable to find an identity with the provided address."); } } //process judgements const judgements = []; if (identity && identity.judgements) { let judgement; identity.judgements.forEach((item) => { judgement = item[1].toHuman(); if (typeof judgement === "object") judgements.push(Object.keys(judgement)[0]).toString(); else judgements.push(judgement); }); } const { display, email, legal, riot, twitter, web } = identity; const basicInfo = { display, address, riot, twitter, web, legal, email }; const balance = yield (0, exports.getAccountBalance)(wsAddress, address); const chain = (yield api.rpc.system.chain()).toString(); return { chain, basicInfo, judgements, balance }; }); exports.getIdentity = getIdentity; /** * fetch the balance of an account with given address from a given substrate based chain * @param wsAddress Network end point URL * @param address account address * @throws Error when the balance for the given address could not be fetched * @returns requested Balance */ const getAccountBalance = (wsAddress, address) => __awaiter(void 0, void 0, void 0, function* () { // calculating total balance const api = yield (0, utilities_1.connectToWsProvider)(wsAddress); let balances, decimals, total = ""; if (api) { try { balances = yield api.derive.balances.account(address); if (!Object.prototype.hasOwnProperty.call(balances, "freeBalance")) throw TypeError; } catch (ex) { throw Error("Unable to find the balance for the provided address."); } if (api.registry) { decimals = api.registry.chainDecimals; decimals = new bignumber_js_1.default(decimals).toNumber(); } } if (balances) { const { freeBalance, reservedBalance } = balances; const base = new bignumber_js_1.default(10).pow(decimals); total = new bignumber_js_1.default(freeBalance.toHex()) .plus(reservedBalance.toHex()) .div(base) .toFixed(2); } // extracting the token information from the chain let symbol = ""; if (exports.tokenSymbol[wsAddress]) { symbol = exports.tokenSymbol[wsAddress]; } else { const properties = (yield api.rpc.system.properties()); if (properties) { const { tokenSymbol } = properties.toHuman(); if (tokenSymbol && Array.isArray(tokenSymbol) && tokenSymbol.length > 0) { symbol = tokenSymbol.shift(); } } exports.tokenSymbol[wsAddress] = symbol; } const balance = { total, symbol }; return balance; }); exports.getAccountBalance = getAccountBalance;