"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
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.assertExpectedChainVersion = exports.getExemptedIds = exports.defusePromise = exports.checkTxType = exports.getPortfolioIdsByName = exports.assembleBatchTransactions = exports.getCheckpointValue = exports.conditionsAreEqual = exports.hasSameElements = exports.toHumanReadable = exports.isModuleOrTagMatch = exports.optionize = exports.periodComplexity = exports.xor = exports.asAsset = exports.asTicker = exports.assertAddressValid = exports.assertIsPositive = exports.assertIsInteger = exports.createProcedureMethod = exports.calculateNextKey = exports.requestAtBlock = exports.requestPaginated = exports.isPrintableAscii = exports.removePadding = exports.padString = exports.filterEventRecords = exports.unwrapValues = exports.unwrapValue = exports.createClaim = exports.getIdentity = exports.asDid = exports.asIdentity = exports.getDid = exports.unserialize = exports.serialize = exports.delay = void 0;
const util_1 = require("@polkadot/util");
const util_crypto_1 = require("@polkadot/util-crypto");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const bluebird_1 = __importDefault(require("bluebird"));
const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
const lodash_1 = require("lodash");
const semver_1 = require("semver");
const websocket_1 = require("websocket");
const internal_1 = require("../internal");
const types_1 = require("../types");
const constants_1 = require("./constants");
const conversion_1 = require("./conversion");
const typeguards_1 = require("./typeguards");
__exportStar(require("../generated/utils"), exports);
/**
 * @hidden
 * Promisified version of a timeout
 *
 * @param amount - time to wait
 */
function delay(amount) {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, amount);
        });
    });
}
exports.delay = delay;
/**
 * @hidden
 * Convert an entity type and its unique Identifiers to a base64 string
 */
function serialize(entityType, uniqueIdentifiers) {
    return Buffer.from(`${entityType}:${(0, json_stable_stringify_1.default)(uniqueIdentifiers)}`).toString('base64');
}
exports.serialize = serialize;
/**
 * @hidden
 * Convert a uuid string to an Identifier object
 */
function unserialize(id) {
    const unserialized = Buffer.from(id, 'base64').toString('utf8');
    const matched = unserialized.match(/^.*?:(.*)/);
    const errorMsg = 'Wrong ID format';
    if (!matched) {
        throw new Error(errorMsg);
    }
    const [, jsonString] = matched;
    try {
        return JSON.parse(jsonString);
    }
    catch (err) {
        throw new Error(errorMsg);
    }
}
exports.unserialize = unserialize;
/**
 * @hidden
 * Extract the DID from an Identity, or return the DID of the signing Identity if no Identity is passed
 */
function getDid(value, context) {
    return __awaiter(this, void 0, void 0, function* () {
        let did;
        if (value) {
            did = (0, conversion_1.signerToString)(value);
        }
        else {
            ({ did } = yield context.getSigningIdentity());
        }
        return did;
    });
}
exports.getDid = getDid;
/**
 * @hidden
 * Given a DID return the corresponding Identity, given an Identity return the Identity
 */
function asIdentity(value, context) {
    return typeof value === 'string' ? new internal_1.Identity({ did: value }, context) : value;
}
exports.asIdentity = asIdentity;
/**
 * @hidden
 * DID | Identity -> DID
 */
function asDid(value) {
    return typeof value === 'string' ? value : value.did;
}
exports.asDid = asDid;
/**
 * @hidden
 * Given an Identity, return the Identity, given a DID returns the corresponding Identity, if value is falsy, then return currentIdentity
 */
function getIdentity(value, context) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!value) {
            return context.getSigningIdentity();
        }
        else {
            return asIdentity(value, context);
        }
    });
}
exports.getIdentity = getIdentity;
/**
 * @hidden
 */
function createClaim(claimType, jurisdiction, middlewareScope, cddId, scopeId) {
    const type = claimType;
    const scope = (middlewareScope ? (0, conversion_1.middlewareScopeToScope)(middlewareScope) : {});
    switch (type) {
        case types_1.ClaimType.Jurisdiction: {
            return {
                type,
                // this assertion is necessary because CountryCode is not in the middleware types
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                code: (0, util_1.stringUpperFirst)(jurisdiction.toLowerCase()),
                scope,
            };
        }
        case types_1.ClaimType.NoData: {
            return {
                type,
            };
        }
        case types_1.ClaimType.CustomerDueDiligence: {
            return {
                type,
                id: cddId,
            };
        }
        case types_1.ClaimType.InvestorUniqueness: {
            return {
                type,
                scope,
                scopeId: scopeId,
                cddId: cddId,
            };
        }
        case types_1.ClaimType.InvestorUniquenessV2: {
            return {
                type,
                cddId: cddId,
            };
        }
    }
    return { type, scope };
}
exports.createClaim = createClaim;
/**
 * @hidden
 *
 * Unwrap a Post Transaction Value
 */
function unwrapValue(value) {
    if (value instanceof internal_1.PostTransactionValue) {
        return value.value;
    }
    return value;
}
exports.unwrapValue = unwrapValue;
/**
 * @hidden
 *
 * Unwrap all Post Transaction Values present in a tuple
 */
function unwrapValues(values) {
    return values.map(unwrapValue);
}
exports.unwrapValues = unwrapValues;
/**
 * @hidden
 * Find every occurrence of a specific event inside a receipt
 *
 * @throws If the event is not found
 */
function filterEventRecords(receipt, mod, eventName) {
    const eventRecords = receipt.filterRecords(mod, eventName);
    if (!eventRecords.length) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.UnexpectedError,
            message: `Event "${mod}.${eventName}" wasn't fired even though the corresponding transaction was completed. Please report this to the Polymath team`,
        });
    }
    return eventRecords.map(eventRecord => eventRecord.event);
}
exports.filterEventRecords = filterEventRecords;
/**
 * @hidden
 */
function padString(value, length) {
    return (0, lodash_1.padEnd)(value, length, '\0');
}
exports.padString = padString;
/**
 * @hidden
 */
function removePadding(value) {
    // eslint-disable-next-line no-control-regex
    return value.replace(/\u0000/g, '');
}
exports.removePadding = removePadding;
/**
 * @hidden
 *
 * Return whether the string is fully printable ASCII
 */
function isPrintableAscii(value) {
    // eslint-disable-next-line no-control-regex
    return /^[\x00-\x7F]*$/.test(value);
}
exports.isPrintableAscii = isPrintableAscii;
/**
 * @hidden
 *
 * Makes an entries request to the chain. If pagination options are supplied,
 *  the request will be paginated. Otherwise, all entries will be requested at once
 */
function requestPaginated(query, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const { arg, paginationOpts } = opts;
        let entries;
        let lastKey = null;
        const args = arg ? [arg] : [];
        if (paginationOpts) {
            const { size: pageSize, start: startKey } = paginationOpts;
            entries = yield query.entriesPaged({
                args,
                pageSize: pageSize.toNumber(),
                startKey,
            });
            if (pageSize.eq(entries.length)) {
                lastKey = entries[entries.length - 1][0].toHex();
            }
        }
        else {
            /*
             * NOTE @monitz87: this assertion is required because types
             *   are inconsistent in the polkadot repo
             */
            entries = yield query.entries(...args);
        }
        return {
            entries,
            lastKey,
        };
    });
}
exports.requestPaginated = requestPaginated;
/**
 * @hidden
 *
 * Makes a request to the chain. If a block hash is supplied,
 *   the request will be made at that block. Otherwise, the most recent block will be queried
 */
function requestAtBlock(query, opts, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const { blockHash, args } = opts;
        if (blockHash) {
            if (!context.isArchiveNode) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.DataUnavailable,
                    message: 'Cannot query previous blocks in a non-archive node',
                });
            }
            return query.at(blockHash, ...args);
        }
        return query(...args);
    });
}
exports.requestAtBlock = requestAtBlock;
/**
 * @hidden
 *
 * Calculates next page number for paginated GraphQL ResultSet.
 * Returns null if there is no next page.
 *
 * @param size - page size requested
 * @param start - start index requested
 * @param totalCount - total amount of elements returned by query
 *
 * @hidden
 *
 */
function calculateNextKey(totalCount, size, start) {
    const next = (start !== null && start !== void 0 ? start : new bignumber_js_1.default(0)).plus(size !== null && size !== void 0 ? size : constants_1.DEFAULT_GQL_PAGE_SIZE);
    return totalCount.gt(next) ? next : null;
}
exports.calculateNextKey = calculateNextKey;
// eslint-disable-next-line require-jsdoc
function createProcedureMethod(args, context) {
    const { getProcedureAndArgs, transformer, voidArgs } = args;
    if (voidArgs) {
        const voidMethod = (opts = {}) => {
            const [proc, procArgs] = getProcedureAndArgs();
            return proc().prepare({ args: procArgs, transformer }, context, opts);
        };
        voidMethod.checkAuthorization = (opts = {}) => __awaiter(this, void 0, void 0, function* () {
            const [proc, procArgs] = getProcedureAndArgs();
            return proc().checkAuthorization(procArgs, context, opts);
        });
        return voidMethod;
    }
    const method = (methodArgs, opts = {}) => {
        const [proc, procArgs] = getProcedureAndArgs(methodArgs);
        return proc().prepare({ args: procArgs, transformer }, context, opts);
    };
    method.checkAuthorization = (methodArgs, opts = {}) => __awaiter(this, void 0, void 0, function* () {
        const [proc, procArgs] = getProcedureAndArgs(methodArgs);
        return proc().checkAuthorization(procArgs, context, opts);
    });
    return method;
}
exports.createProcedureMethod = createProcedureMethod;
/**
 * @hidden
 */
function assertIsInteger(value) {
    if (!value.isInteger()) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: 'The number must be an integer',
        });
    }
}
exports.assertIsInteger = assertIsInteger;
/**
 * @hidden
 */
function assertIsPositive(value) {
    if (value.isNegative()) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: 'The number must be positive',
        });
    }
}
exports.assertIsPositive = assertIsPositive;
/**
 * @hidden
 */
function assertAddressValid(address, ss58Format) {
    let encodedAddress;
    try {
        encodedAddress = (0, util_crypto_1.encodeAddress)((0, util_crypto_1.decodeAddress)(address), ss58Format.toNumber());
    }
    catch (err) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: 'The supplied address is not a valid SS58 address',
        });
    }
    if (address !== encodedAddress) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: "The supplied address is not encoded with the chain's SS58 format",
            data: {
                ss58Format,
            },
        });
    }
}
exports.assertAddressValid = assertAddressValid;
/**
 * @hidden
 */
function asTicker(asset) {
    return typeof asset === 'string' ? asset : asset.ticker;
}
exports.asTicker = asTicker;
/**
 * @hidden
 */
function asAsset(asset, context) {
    return typeof asset === 'string' ? new internal_1.Asset({ ticker: asset }, context) : asset;
}
exports.asAsset = asAsset;
/**
 * @hidden
 */
function xor(a, b) {
    return a !== b;
}
exports.xor = xor;
/**
 * @hidden
 */
function secondsInUnit(unit) {
    const SECOND = new bignumber_js_1.default(1);
    const MINUTE = SECOND.multipliedBy(60);
    const HOUR = MINUTE.multipliedBy(60);
    const DAY = HOUR.multipliedBy(24);
    const WEEK = DAY.multipliedBy(7);
    const MONTH = DAY.multipliedBy(30);
    const YEAR = DAY.multipliedBy(365);
    switch (unit) {
        case types_1.CalendarUnit.Second: {
            return SECOND;
        }
        case types_1.CalendarUnit.Minute: {
            return MINUTE;
        }
        case types_1.CalendarUnit.Hour: {
            return HOUR;
        }
        case types_1.CalendarUnit.Day: {
            return DAY;
        }
        case types_1.CalendarUnit.Week: {
            return WEEK;
        }
        case types_1.CalendarUnit.Month: {
            return MONTH;
        }
        case types_1.CalendarUnit.Year: {
            return YEAR;
        }
    }
}
/**
 * @hidden
 * Calculate the numeric complexity of a calendar period
 */
function periodComplexity(period) {
    const secsInYear = secondsInUnit(types_1.CalendarUnit.Year);
    const { amount, unit } = period;
    if (amount.isZero()) {
        return new bignumber_js_1.default(1);
    }
    const secsInUnit = secondsInUnit(unit);
    const complexity = secsInYear.dividedBy(secsInUnit.multipliedBy(amount));
    return bignumber_js_1.default.maximum(2, complexity.integerValue(bignumber_js_1.default.ROUND_FLOOR));
}
exports.periodComplexity = periodComplexity;
/**
 * @hidden
 * Transform a conversion util into a version that returns null if the input is falsy
 */
function optionize(converter) {
    return (value, ...rest) => {
        const data = value !== null && value !== void 0 ? value : null;
        return data && converter(data, ...rest);
    };
}
exports.optionize = optionize;
/**
 * @hidden
 * Compare two tags/modules and return true if they are equal, or if one is the other one's module
 */
function isModuleOrTagMatch(a, b) {
    const aIsTag = a.includes('.');
    const bIsTag = b.includes('.');
    // a tag b module
    if (aIsTag && !bIsTag) {
        return a.split('.')[0] === b;
    }
    // a module b tag
    if (!aIsTag && bIsTag) {
        return a === b.split('.')[0];
    }
    // both tags or both modules
    return a === b;
}
exports.isModuleOrTagMatch = isModuleOrTagMatch;
/**
 * @hidden
 *
 * Recursively convert a value into a human readable (JSON compliant) version:
 *   - Entities are converted via their `.toJson` method
 *   - Dates are converted to ISO strings
 *   - BigNumbers are converted to numerical strings
 */
function toHumanReadable(obj) {
    if ((0, typeguards_1.isEntity)(obj)) {
        return obj.toJson();
    }
    if (obj instanceof bignumber_js_1.default) {
        return obj.toString();
    }
    if (obj instanceof Date) {
        return obj.toISOString();
    }
    if (Array.isArray(obj)) {
        return obj.map(toHumanReadable);
    }
    if (obj && typeof obj === 'object') {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return (0, lodash_1.mapValues)(obj, val => toHumanReadable(val));
    }
    return obj;
}
exports.toHumanReadable = toHumanReadable;
/**
 * @hidden
 *
 * Return whether the two arrays have same elements.
 * It uses a `comparator` function to check if elements are equal.
 * If no comparator function is provided, it uses `isEqual` function of `lodash`
 */
function hasSameElements(first, second, comparator = lodash_1.isEqual) {
    return !(0, lodash_1.differenceWith)(first, second, comparator).length && first.length === second.length;
}
exports.hasSameElements = hasSameElements;
/**
 * @hidden
 *
 * Perform a deep comparison between two compliance conditions
 */
function conditionsAreEqual(a, b) {
    let equalClaims = false;
    const { type: aType, trustedClaimIssuers: aClaimIssuers = [] } = a;
    const { type: bType, trustedClaimIssuers: bClaimIssuers = [] } = b;
    if ((0, typeguards_1.isSingleClaimCondition)(a) && (0, typeguards_1.isSingleClaimCondition)(b)) {
        equalClaims = (0, lodash_1.isEqual)(a.claim, b.claim);
    }
    else if ((0, typeguards_1.isMultiClaimCondition)(a) && (0, typeguards_1.isMultiClaimCondition)(b)) {
        const { claims: aClaims } = a;
        const { claims: bClaims } = b;
        equalClaims = hasSameElements(aClaims, bClaims);
    }
    else if (aType === types_1.ConditionType.IsIdentity && bType === types_1.ConditionType.IsIdentity) {
        equalClaims = (0, conversion_1.signerToString)(a.identity) === (0, conversion_1.signerToString)(b.identity);
    }
    else if (aType === types_1.ConditionType.IsExternalAgent && bType === types_1.ConditionType.IsExternalAgent) {
        equalClaims = true;
    }
    const equalClaimIssuers = hasSameElements(aClaimIssuers, bClaimIssuers, ({ identity: aIdentity, trustedFor: aTrustedFor }, { identity: bIdentity, trustedFor: bTrustedFor }) => (0, conversion_1.signerToString)(aIdentity) === (0, conversion_1.signerToString)(bIdentity) &&
        hasSameElements(aTrustedFor || [], bTrustedFor || []));
    return equalClaims && equalClaimIssuers;
}
exports.conditionsAreEqual = conditionsAreEqual;
/**
 * @hidden
 *
 * Transforms `InputCACheckpoint` values to `Checkpoint | CheckpointSchedule | Date` for easier processing
 */
function getCheckpointValue(checkpoint, asset, context) {
    return __awaiter(this, void 0, void 0, function* () {
        if (checkpoint instanceof internal_1.Checkpoint ||
            checkpoint instanceof internal_1.CheckpointSchedule ||
            checkpoint instanceof Date) {
            return checkpoint;
        }
        const assetEntity = asAsset(asset, context);
        const { type, id } = checkpoint;
        if (type === types_1.CaCheckpointType.Existing) {
            return assetEntity.checkpoints.getOne({ id });
        }
        else {
            return (yield assetEntity.checkpoints.schedules.getOne({
                id,
            })).schedule;
        }
    });
}
exports.getCheckpointValue = getCheckpointValue;
// * TODO @monitz87: delete this function when we eliminate `addBatchTransaction`
/**
 * @hidden
 */
function mapArgs({ transaction, argsArray, }) {
    return argsArray.map(args => ({
        transaction,
        args,
    }));
}
// * TODO @monitz87: delete this function when we eliminate `addBatchTransaction`
/**
 * Assemble the `transactions` array that has to be passed to `addBatchTransaction` from a set of parameter arrays with their
 *   respective transaction
 */
function assembleBatchTransactions(txsAndArgs) {
    return (0, lodash_1.flatMap)(txsAndArgs, mapArgs);
}
exports.assembleBatchTransactions = assembleBatchTransactions;
/**
 * @hidden
 *
 * Returns portfolio numbers for a set of portfolio names
 */
function getPortfolioIdsByName(rawIdentityId, rawNames, context) {
    return __awaiter(this, void 0, void 0, function* () {
        console.log('CALL');
        const { polymeshApi: { query: { portfolio }, }, } = context;
        const rawPortfolioNumbers = yield portfolio.nameToNumber.multi(rawNames.map(name => [rawIdentityId, name]));
        const portfolioIds = rawPortfolioNumbers.map(number => (0, conversion_1.u64ToBigNumber)(number));
        // TODO @prashantasdeveloper remove this logic once nameToNumber returns Option<PortfolioNumber>
        /**
         * since nameToNumber returns 1 for non-existing portfolios, if a name maps to number 1,
         *  we need to check if the given name actually matches the first portfolio
         */
        let firstPortfolioName;
        /*
         * even though we make this call without knowing if we will need
         *  the result, we only await for it if necessary, so it's still
         *  performant
         */
        const gettingFirstPortfolioName = portfolio.portfolios(rawIdentityId, (0, conversion_1.bigNumberToU64)(new bignumber_js_1.default(1), context));
        return bluebird_1.default.map(portfolioIds, (id, index) => __awaiter(this, void 0, void 0, function* () {
            if (id.eq(1)) {
                firstPortfolioName = yield gettingFirstPortfolioName;
                if (!firstPortfolioName.eq(rawNames[index])) {
                    return null;
                }
            }
            return id;
        }));
    });
}
exports.getPortfolioIdsByName = getPortfolioIdsByName;
/**
 * @hidden
 *
 * Check if a transaction matches the type of its args. Returns the same value but stripped of the types. This function has no logic, it's strictly
 *   for type safety around `addBatchTransaction`
 */
function checkTxType(tx) {
    return tx;
}
exports.checkTxType = checkTxType;
/**
 * @hidden
 *
 * Add an empty handler to a promise to avoid false positive unhandled promise errors. The original promise
 *   is returned, so rejections are still bubbled up and caught properly. This is an ugly hack and should be used
 *   sparingly and only if you KNOW that rejections will be handled properly down the line
 *
 * More info:
 *
 * - https://github.com/facebook/jest/issues/6028#issuecomment-567851031
 * - https://stackoverflow.com/questions/59060508/how-to-handle-an-unhandled-promise-rejection-asynchronously
 * - https://stackoverflow.com/questions/40920179/should-i-refrain-from-handling-promise-rejection-asynchronously/40921505#40921505
 */
function defusePromise(promise) {
    promise.catch(lodash_1.noop);
    return promise;
}
exports.defusePromise = defusePromise;
/**
 * @hidden
 *
 * Transform an array of Identities into exempted IDs for Transfer Managers. If the asset requires
 *   investor uniqueness, Scope IDs are fetched and returned. Otherwise, we use Identity IDs
 *
 * @note fetches missing scope IDs from the chain
 * @note even though the signature for `addExemptedEntities` requires `ScopeId`s as parameters,
 *   it accepts and handles `IdentityId` parameters as well. Nothing special has to be done typing-wise since they're both aliases
 *   for `U8aFixed`
 *
 * @throws
 *   - if the Asset requires Investor Uniqueness and one or more of the passed Identities don't have Scope IDs
 *   - if there are duplicated Identities/ScopeIDs
 */
function getExemptedIds(identities, context, ticker) {
    return __awaiter(this, void 0, void 0, function* () {
        const asset = new internal_1.Asset({ ticker }, context);
        const { requiresInvestorUniqueness } = yield asset.details();
        const didsWithNoScopeId = [];
        const exemptedIds = [];
        const identityEntities = identities.map(identity => asIdentity(identity, context));
        if (requiresInvestorUniqueness) {
            const scopeIds = yield bluebird_1.default.map(identityEntities, (identity) => __awaiter(this, void 0, void 0, function* () { return identity.getScopeId({ asset: ticker }); }));
            scopeIds.forEach((scopeId, index) => {
                if (!scopeId) {
                    didsWithNoScopeId.push(identityEntities[index].did);
                }
                else {
                    exemptedIds.push(scopeId);
                }
            });
            if (didsWithNoScopeId.length) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.ValidationError,
                    message: `Identities must have an Investor Uniqueness claim Scope ID in order to be exempted from Transfer Restrictions for Asset "${ticker}"`,
                    data: {
                        didsWithNoScopeId,
                    },
                });
            }
        }
        else {
            exemptedIds.push(...identityEntities.map(identity => asDid(identity), context));
        }
        const hasDuplicates = (0, lodash_1.uniq)(exemptedIds).length !== exemptedIds.length;
        if (hasDuplicates) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'One or more of the passed exempted Identities are repeated or have the same Scope ID',
            });
        }
        return exemptedIds;
    });
}
exports.getExemptedIds = getExemptedIds;
/**
 * @hidden
 *
 * Checks chain version. This function uses a websocket as it's intended to be called during initialization
 * @param nodeUrl - URL for the chain node
 * @returns A promise that resolves if the version is in the expected range, otherwise it will reject
 */
function assertExpectedChainVersion(nodeUrl) {
    return new Promise((resolve, reject) => {
        const client = new websocket_1.w3cwebsocket(nodeUrl);
        client.onopen = () => {
            const msg = Object.assign(Object.assign({}, constants_1.SYSTEM_VERSION_RPC_CALL), { id: 'assertExpectedChainVersion' });
            client.send(JSON.stringify(msg));
        };
        client.onmessage = msg => {
            client.close();
            const { result: version } = JSON.parse(msg.data.toString());
            if (!(0, semver_1.satisfies)(version, constants_1.SUPPORTED_VERSION_RANGE)) {
                const error = new internal_1.PolymeshError({
                    code: types_1.ErrorCode.FatalError,
                    message: 'Unsupported Polymesh version. Please upgrade the SDK',
                    data: {
                        polymeshVersion: version,
                        supportedVersionRange: constants_1.SUPPORTED_VERSION_RANGE,
                    },
                });
                reject(error);
            }
            else {
                resolve();
            }
        };
        client.onerror = (error) => {
            client.close();
            const err = new internal_1.PolymeshError({
                code: types_1.ErrorCode.FatalError,
                message: `Could not connect to the Polymesh node at ${nodeUrl}`,
                data: { error },
            });
            reject(err);
        };
    });
}
exports.assertExpectedChainVersion = assertExpectedChainVersion;
//# sourceMappingURL=internal.js.map