"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.Context = void 0;
const api_1 = require("@polkadot/api");
const types_1 = require("@polkadot/types");
const types_2 = require("@polkadot/types/types");
const util_1 = require("@polkadot/util");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const bluebird_1 = __importDefault(require("bluebird"));
const lodash_1 = require("lodash");
const definitions_1 = require("../polkadot/definitions");
const types_3 = require("../polkadot/types");
const internal_1 = require("../internal");
const queries_1 = require("../middleware/queries");
const types_4 = require("../middleware/types");
const types_5 = require("../types");
const constants_1 = require("../utils/constants");
const conversion_1 = require("../utils/conversion");
const internal_2 = require("../utils/internal");
/**
 * @hidden
 *
 * Context in which the SDK is being used
 *
 * - Holds the current low level API
 * - Holds the current keyring pair
 * - Holds the current Identity
 */
class Context {
    /**
     * @hidden
     */
    constructor(params) {
        this.isDisconnected = false;
        /**
         * Whether the current node is an archive node (contains a full history from genesis onward) or not
         */
        this.isArchiveNode = false;
        const { polymeshApi, middlewareApi, keyring, ss58Format } = params;
        this._middlewareApi = middlewareApi;
        this._polymeshApi = polymeshApi;
        this.polymeshApi = Context.createPolymeshApiProxy(this);
        this.keyring = keyring;
        this.ss58Format = ss58Format;
        const currentPair = keyring.getPairs()[0];
        if (currentPair) {
            internal_2.assertFormatValid(currentPair.address, ss58Format);
            this.currentPair = currentPair;
        }
    }
    /**
     * @hidden
     */
    static createPolymeshApiProxy(ctx) {
        return new Proxy(ctx._polymeshApi, {
            get: (target, prop) => {
                if (prop === 'tx' && !ctx.currentPair) {
                    throw new internal_1.PolymeshError({
                        code: types_5.ErrorCode.General,
                        message: 'Cannot perform transactions without an active Account',
                    });
                }
                return target[prop];
            },
        });
    }
    /**
     * @hidden
     *
     * Create the Context instance
     */
    static create(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi, middlewareApi, accountSeed, keyring: passedKeyring, accountUri, accountMnemonic, } = params;
            const ss58Format = conversion_1.u8ToBigNumber(polymeshApi.consts.system.ss58Prefix).toNumber();
            let keyring = new api_1.Keyring({ type: 'sr25519', ss58Format });
            if (passedKeyring) {
                keyring = internal_2.getCommonKeyring(passedKeyring);
                internal_2.assertKeyringFormatValid(keyring, ss58Format);
            }
            else {
                Context._addPair({
                    accountSeed,
                    accountMnemonic,
                    accountUri,
                    keyring,
                });
            }
            const context = new Context({ polymeshApi, middlewareApi, keyring, ss58Format });
            context.isArchiveNode = yield context.isCurrentNodeArchive();
            return new Proxy(context, {
                get: (target, prop) => {
                    if (target.isDisconnected) {
                        throw new internal_1.PolymeshError({
                            code: types_5.ErrorCode.FatalError,
                            message: 'Client disconnected. Please create a new instance via "Polymesh.connect()"',
                        });
                    }
                    return target[prop];
                },
            });
        });
    }
    /**
     * @hidden
     */
    isCurrentNodeArchive() {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { balances, system }, }, } = this;
            try {
                const blockHash = yield system.blockHash(conversion_1.numberToU32(0, this));
                const balance = yield balances.totalIssuance.at(blockHash);
                return conversion_1.balanceToBigNumber(balance).gt(new bignumber_js_1.default(0));
            }
            catch (e) {
                return false;
            }
        });
    }
    /**
     * @hidden
     *
     * Retrieve a list of Accounts that can act as signers. The first Account in the array is the current Account (default signer)
     */
    getAccounts() {
        const { keyring, currentPair } = this;
        if (!currentPair) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.General,
                message: 'There is no Account associated with the SDK',
            });
        }
        const pairs = [...keyring.getPairs()];
        const [first] = lodash_1.remove(pairs, ({ address }) => currentPair.address === address);
        return [
            new internal_1.Account({ address: first.address }, this),
            ...pairs.map(({ address }) => new internal_1.Account({ address }, this)),
        ];
    }
    /**
     * @hidden
     *
     * Add a signing pair to the Keyring
     */
    addPair(params) {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        return Context._addPair(Object.assign(Object.assign({}, params), { keyring: this.keyring }));
    }
    /**
     * @hidden
     */
    static _addPair(params) {
        const { accountSeed, accountUri, accountMnemonic, keyring, pair } = params;
        let newPair;
        if (accountSeed) {
            if (accountSeed.length !== 66) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.ValidationError,
                    message: 'Seed must be 66 characters in length',
                });
            }
            newPair = keyring.addFromSeed(util_1.hexToU8a(accountSeed), undefined, 'sr25519');
        }
        else if (accountUri) {
            newPair = keyring.addFromUri(accountUri);
        }
        else if (accountMnemonic) {
            newPair = keyring.addFromMnemonic(accountMnemonic);
        }
        else if (pair) {
            /*
             * NOTE @monitz87: the only way to avoid this assertion is to import the Keyring package
             *   which doesn't make sense just for this
             */
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            newPair = keyring.addPair(pair);
        }
        else {
            return;
        }
        return newPair;
    }
    /**
     * @hidden
     *
     * Set a pair as the current Account keyring pair
     */
    setPair(address) {
        const { keyring } = this;
        let newCurrentPair;
        try {
            internal_2.assertFormatValid(address, this.ss58Format);
            newCurrentPair = keyring.getPair(address);
        }
        catch (e) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.General,
                message: 'The address is not present in the keyring set',
            });
        }
        this.currentPair = newCurrentPair;
    }
    // eslint-disable-next-line require-jsdoc
    accountBalance(account, callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { system }, }, } = this;
            let address;
            if (account) {
                address = conversion_1.signerToString(account);
            }
            else {
                ({ address } = this.getCurrentAccount());
            }
            const rawAddress = conversion_1.stringToAccountId(address, this);
            const assembleResult = ({ data: { free: rawFree, miscFrozen, feeFrozen, reserved: rawReserved }, }) => {
                /*
                 * The chain's "free" balance is the balance that isn't locked. Here we calculate it so
                 * the free balance is what the Account is able to spend
                 */
                const reserved = conversion_1.balanceToBigNumber(rawReserved);
                const total = conversion_1.balanceToBigNumber(rawFree).plus(reserved);
                const locked = bignumber_js_1.default.max(conversion_1.balanceToBigNumber(miscFrozen), conversion_1.balanceToBigNumber(feeFrozen));
                return {
                    total,
                    locked,
                    free: total.minus(locked).minus(reserved),
                };
            };
            if (callback) {
                return system.account(rawAddress, info => {
                    callback(assembleResult(info));
                });
            }
            const accountInfo = yield system.account(rawAddress);
            return assembleResult(accountInfo);
        });
    }
    // eslint-disable-next-line require-jsdoc
    accountSubsidy(account, callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { relayer }, }, } = this;
            let address;
            if (account) {
                address = conversion_1.signerToString(account);
            }
            else {
                ({ address } = this.getCurrentAccount());
            }
            const rawAddress = conversion_1.stringToAccountId(address, this);
            const assembleResult = (subsidy) => {
                if (subsidy.isNone) {
                    return null;
                }
                const { paying_key: payingKey, remaining } = subsidy.unwrap();
                const allowance = conversion_1.balanceToBigNumber(remaining);
                const subsidizer = new internal_1.Account({ address: conversion_1.accountIdToString(payingKey) }, this);
                return {
                    allowance,
                    subsidizer,
                };
            };
            if (callback) {
                return relayer.subsidies(rawAddress, subsidy => {
                    callback(assembleResult(subsidy));
                });
            }
            const subsidies = yield relayer.subsidies(rawAddress);
            return assembleResult(subsidies);
        });
    }
    /**
     * @hidden
     *
     * Retrieve the current Account
     *
     * @throws if there is no current Account associated to the SDK instance
     */
    getCurrentAccount() {
        const { address } = this.getCurrentPair();
        return new internal_1.Account({ address }, this);
    }
    /**
     * @hidden
     *
     * Retrieve the current Identity
     *
     * @throws if there is no Identity associated to the current Account (or there is no current Account associated to the SDK instance)
     */
    getCurrentIdentity() {
        return __awaiter(this, void 0, void 0, function* () {
            const account = this.getCurrentAccount();
            const identity = yield account.getIdentity();
            if (identity === null) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.DataUnavailable,
                    message: 'The current Account does not have an associated Identity',
                });
            }
            return identity;
        });
    }
    /**
     * @hidden
     *
     * Retrieve the current Keyring Pair
     *
     * @throws if there is no Account associated to the SDK instance
     */
    getCurrentPair() {
        const { currentPair } = this;
        if (!currentPair) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.General,
                message: 'There is no Account associated with the current SDK instance',
            });
        }
        return currentPair;
    }
    /**
     * @hidden
     *
     * Retrieve the signer address (or keyring pair)
     */
    getSigner() {
        const currentPair = this.getCurrentPair();
        const { isLocked, address } = currentPair;
        /*
         * if the keyring pair is locked, it means it most likely got added from the polkadot extension
         * with a custom signer. This means we have to pass just the address to signAndSend
         */
        return isLocked ? address : currentPair;
    }
    /**
     * @hidden
     *
     * Check whether a set of Identities exist
     */
    getInvalidDids(identities) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { identity }, }, } = this;
            const dids = identities.map(conversion_1.signerToString);
            const rawIdentities = dids.map(did => conversion_1.stringToIdentityId(did, this));
            const records = yield identity.didRecords.multi(rawIdentities);
            const invalidDids = [];
            records.forEach((record, index) => {
                if (record.isEmpty) {
                    invalidDids.push(dids[index]);
                }
            });
            return invalidDids;
        });
    }
    /**
     * @hidden
     *
     * Returns an Identity when given a did string
     *
     * @throws if the Identity does not exist
     */
    getIdentity(identity) {
        return __awaiter(this, void 0, void 0, function* () {
            if (identity instanceof internal_1.Identity) {
                return identity;
            }
            const id = new internal_1.Identity({ did: identity }, this);
            const exists = yield id.exists();
            if (!exists) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.DataUnavailable,
                    message: 'The Identity does not exist',
                });
            }
            return id;
        });
    }
    /**
     * @hidden
     *
     * Retrieve the protocol fees associated with running a specific transaction
     *
     * @param tag - transaction tag (i.e. TxTags.asset.CreateAsset or "asset.createAsset")
     */
    getProtocolFees({ tag }) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { protocolFee }, }, } = this;
            let protocolOp;
            try {
                protocolOp = conversion_1.txTagToProtocolOp(tag, this);
            }
            catch (err) {
                return new bignumber_js_1.default(0);
            }
            const [baseFee, coefficient] = yield Promise.all([
                protocolFee.baseFees(protocolOp),
                protocolFee.coefficient(),
            ]);
            return conversion_1.balanceToBigNumber(baseFee).multipliedBy(conversion_1.posRatioToBigNumber(coefficient));
        });
    }
    /**
     * @hidden
     *
     * Return whether the passed transaction can be subsidized
     */
    supportsSubsidy({ tag }) {
        const moduleName = tag.split('.')[0];
        return [
            types_3.ModuleName.Asset,
            types_3.ModuleName.ComplianceManager,
            types_3.ModuleName.CorporateAction,
            types_3.ModuleName.ExternalAgents,
            types_3.ModuleName.Portfolio,
            types_3.ModuleName.Settlement,
            types_3.ModuleName.Statistics,
            types_3.ModuleName.Sto,
            types_3.ModuleName.Relayer,
        ].includes(moduleName);
    }
    /**
     * Retrieve the types of arguments that a certain transaction requires to be run
     *
     * @param args.tag - tag associated with the transaction that will be executed if the proposal passes
     */
    getTransactionArguments({ tag }) {
        const { polymeshApi: { tx }, } = this;
        const { types } = definitions_1.polymesh;
        const [section, method] = tag.split('.');
        const getRootType = (type) => {
            const rootType = constants_1.ROOT_TYPES[type];
            if (rootType) {
                return {
                    type: rootType,
                };
            }
            if (type === 'Null') {
                return {
                    type: types_5.TransactionArgumentType.Null,
                };
            }
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const definition = types[type];
            if (!definition) {
                return {
                    type: types_5.TransactionArgumentType.Unknown,
                };
            }
            const typeDef = types_1.getTypeDef(JSON.stringify(definition));
            if (typeDef.info === types_2.TypeDefInfo.Plain) {
                return getRootType(definition);
            }
            // eslint-disable-next-line @typescript-eslint/no-use-before-define
            return processType(typeDef, '');
        };
        const processType = (rawType, name) => {
            const { type, info, sub } = rawType;
            const arg = {
                name,
                optional: false,
                _rawType: rawType,
            };
            switch (info) {
                case types_2.TypeDefInfo.Plain: {
                    return Object.assign(Object.assign({}, getRootType(type)), arg);
                }
                case types_2.TypeDefInfo.Compact: {
                    return Object.assign(Object.assign({}, processType(sub, name)), arg);
                }
                case types_2.TypeDefInfo.Option: {
                    return Object.assign(Object.assign(Object.assign({}, processType(sub, name)), arg), { optional: true });
                }
                case types_2.TypeDefInfo.Tuple: {
                    return Object.assign(Object.assign({ type: types_5.TransactionArgumentType.Tuple }, arg), { internal: sub.map((def, index) => processType(def, `${index}`)) });
                }
                case types_2.TypeDefInfo.Vec: {
                    return Object.assign(Object.assign({ type: types_5.TransactionArgumentType.Array }, arg), { internal: processType(sub, '') });
                }
                case types_2.TypeDefInfo.VecFixed: {
                    return Object.assign({ type: types_5.TransactionArgumentType.Text }, arg);
                }
                case types_2.TypeDefInfo.Enum: {
                    const subTypes = sub;
                    const isSimple = subTypes.every(({ type: subType }) => subType === 'Null');
                    if (isSimple) {
                        return Object.assign(Object.assign({ type: types_5.TransactionArgumentType.SimpleEnum }, arg), { 
                            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                            internal: subTypes.map(({ name: subName }) => subName) });
                    }
                    return Object.assign(Object.assign({ type: types_5.TransactionArgumentType.RichEnum }, arg), { 
                        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                        internal: subTypes.map(def => processType(def, def.name)) });
                }
                case types_2.TypeDefInfo.Struct: {
                    return Object.assign(Object.assign({ type: types_5.TransactionArgumentType.Object }, arg), { 
                        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                        internal: sub.map(def => processType(def, def.name)) });
                }
                default: {
                    return Object.assign({ type: types_5.TransactionArgumentType.Unknown }, arg);
                }
            }
        };
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return tx[section][method].meta.args.map(({ name, type }) => {
            const typeDef = types_1.getTypeDef(type.toString());
            const argName = conversion_1.textToString(name);
            return processType(typeDef, argName);
        });
    }
    /**
     * @hidden
     */
    getDividendDistributionsForAssets(args) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { corporateAction: corporateActionQuery, capitalDistribution }, }, } = this;
            const { assets } = args;
            const distributionsMultiParams = [];
            const corporateActionParams = [];
            const corporateActionIds = [];
            const tickers = [];
            const assetChunks = lodash_1.chunk(assets, constants_1.MAX_CONCURRENT_REQUESTS);
            yield bluebird_1.default.each(assetChunks, (assetChunk) => __awaiter(this, void 0, void 0, function* () {
                const corporateActions = yield Promise.all(assetChunk.map(({ ticker }) => corporateActionQuery.corporateActions.entries(conversion_1.stringToTicker(ticker, this))));
                const eligibleCas = lodash_1.flatten(corporateActions).filter(([, action]) => {
                    const kind = action.unwrap().kind;
                    return kind.isUnpredictableBenefit || kind.isPredictableBenefit;
                });
                const corporateActionData = yield bluebird_1.default.map(eligibleCas, ([{ args: [rawTicker, rawId], }, corporateAction,]) => __awaiter(this, void 0, void 0, function* () {
                    const localId = conversion_1.u32ToBigNumber(rawId);
                    const ticker = conversion_1.tickerToString(rawTicker);
                    const caId = conversion_1.corporateActionIdentifierToCaId({ ticker, localId }, this);
                    const details = yield corporateActionQuery.details(caId);
                    const action = corporateAction.unwrap();
                    return {
                        ticker,
                        localId,
                        caId,
                        corporateAction: conversion_1.meshCorporateActionToCorporateActionParams(action, details, this),
                    };
                }));
                corporateActionData.forEach(({ ticker, localId, caId, corporateAction }) => {
                    tickers.push(ticker);
                    corporateActionIds.push(localId);
                    distributionsMultiParams.push(caId);
                    corporateActionParams.push(corporateAction);
                });
            }));
            /*
             * Divide the requests to account for practical limits
             */
            const paramChunks = lodash_1.chunk(distributionsMultiParams, constants_1.MAX_PAGE_SIZE);
            const requestChunks = lodash_1.chunk(paramChunks, constants_1.MAX_CONCURRENT_REQUESTS);
            const distributions = yield bluebird_1.default.mapSeries(requestChunks, requestChunk => Promise.all(requestChunk.map(paramChunk => capitalDistribution.distributions.multi(paramChunk))));
            const result = [];
            lodash_1.flattenDeep(distributions).forEach((distribution, index) => {
                if (distribution.isNone) {
                    return;
                }
                const dist = distribution.unwrap();
                const { reclaimed, remaining } = dist;
                result.push({
                    distribution: new internal_1.DividendDistribution(Object.assign(Object.assign({ ticker: tickers[index], id: corporateActionIds[index] }, corporateActionParams[index]), conversion_1.distributionToDividendDistributionParams(dist, this)), this),
                    details: {
                        remainingFunds: conversion_1.balanceToBigNumber(remaining),
                        fundsReclaimed: conversion_1.boolToBoolean(reclaimed),
                    },
                });
            });
            return result;
        });
    }
    /**
     * @hidden
     *
     * @note no claimTypes value means ALL claim types
     */
    getIdentityClaimsFromChain(args) {
        return __awaiter(this, void 0, void 0, function* () {
            const { polymeshApi: { query: { identity }, }, } = this;
            const { targets, claimTypes = Object.values(types_5.ClaimType), trustedClaimIssuers, includeExpired, } = args;
            const claim1stKeys = lodash_1.flatMap(targets, target => claimTypes.map(claimType => {
                return {
                    target: conversion_1.signerToString(target),
                    // eslint-disable-next-line @typescript-eslint/naming-convention
                    claim_type: conversion_1.claimTypeToMeshClaimType(claimType, this),
                };
            }));
            const claimIssuerDids = trustedClaimIssuers === null || trustedClaimIssuers === void 0 ? void 0 : trustedClaimIssuers.map(trustedClaimIssuer => conversion_1.signerToString(trustedClaimIssuer));
            const claimData = yield bluebird_1.default.map(claim1stKeys, (claim1stKey) => __awaiter(this, void 0, void 0, function* () {
                const entries = yield identity.claims.entries(claim1stKey);
                const data = [];
                entries.forEach(([key, { claim_issuer: claimIssuer, issuance_date: issuanceDate, expiry: rawExpiry, claim },]) => {
                    const { target } = key.args[0];
                    const expiry = !rawExpiry.isEmpty ? conversion_1.momentToDate(rawExpiry.unwrap()) : null;
                    if ((!includeExpired && (expiry === null || expiry > new Date())) || includeExpired) {
                        data.push({
                            target: new internal_1.Identity({ did: conversion_1.identityIdToString(target) }, this),
                            issuer: new internal_1.Identity({ did: conversion_1.identityIdToString(claimIssuer) }, this),
                            issuedAt: conversion_1.momentToDate(issuanceDate),
                            expiry,
                            claim: conversion_1.meshClaimToClaim(claim),
                        });
                    }
                });
                return data;
            }));
            return lodash_1.flatten(claimData).filter(({ issuer }) => claimIssuerDids ? claimIssuerDids.includes(issuer.did) : true);
        });
    }
    /**
     * @hidden
     */
    getIdentityClaimsFromMiddleware(args) {
        return __awaiter(this, void 0, void 0, function* () {
            const { targets, claimTypes, trustedClaimIssuers, includeExpired, size, start } = args;
            const data = [];
            const result = yield this.queryMiddleware(queries_1.didsWithClaims({
                dids: targets === null || targets === void 0 ? void 0 : targets.map(target => conversion_1.signerToString(target)),
                trustedClaimIssuers: trustedClaimIssuers === null || trustedClaimIssuers === void 0 ? void 0 : trustedClaimIssuers.map(trustedClaimIssuer => conversion_1.signerToString(trustedClaimIssuer)),
                claimTypes: claimTypes === null || claimTypes === void 0 ? void 0 : claimTypes.map(ct => types_4.ClaimTypeEnum[ct]),
                includeExpired,
                count: size,
                skip: start,
            }));
            const { data: { didsWithClaims: { items: didsWithClaimsList, totalCount: count }, }, } = result;
            didsWithClaimsList.forEach(({ claims }) => {
                claims.forEach(({ targetDID: target, issuer, issuance_date: issuanceDate, expiry, type, jurisdiction, scope, cdd_id: cddId, }) => {
                    data.push({
                        target: new internal_1.Identity({ did: target }, this),
                        issuer: new internal_1.Identity({ did: issuer }, this),
                        issuedAt: new Date(issuanceDate),
                        expiry: expiry ? new Date(expiry) : null,
                        claim: internal_2.createClaim(type, jurisdiction, scope, cddId, undefined),
                    });
                });
            });
            const next = internal_2.calculateNextKey(count, size, start);
            return {
                data,
                next,
                count,
            };
        });
    }
    /**
     * @hidden
     *
     * Retrieve a list of claims. Can be filtered using parameters
     *
     * @param opts.targets - identities (or Identity IDs) for which to fetch claims (targets). Defaults to all targets
     * @param opts.trustedClaimIssuers - identity IDs of claim issuers. Defaults to all claim issuers
     * @param opts.claimTypes - types of the claims to fetch. Defaults to any type
     * @param opts.includeExpired - whether to include expired claims. Defaults to true
     * @param opts.size - page size
     * @param opts.start - page offset
     *
     * @note uses the middleware (optional)
     */
    issuedClaims(opts = {}) {
        return __awaiter(this, void 0, void 0, function* () {
            const { targets, trustedClaimIssuers, claimTypes, includeExpired = true, size, start } = opts;
            const isMiddlewareAvailable = yield this.isMiddlewareAvailable();
            if (isMiddlewareAvailable) {
                return this.getIdentityClaimsFromMiddleware({
                    targets,
                    trustedClaimIssuers,
                    claimTypes,
                    includeExpired,
                    size,
                    start,
                });
            }
            if (!targets) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.MiddlewareError,
                    message: 'Cannot perform this action without an active middleware connection',
                });
            }
            const identityClaimsFromChain = yield this.getIdentityClaimsFromChain({
                targets,
                claimTypes,
                trustedClaimIssuers,
                includeExpired,
            });
            return {
                data: identityClaimsFromChain,
                next: null,
                count: undefined,
            };
        });
    }
    /**
     * @hidden
     *
     * Retrieve the middleware client
     *
     * @throws if the middleware is not enabled
     */
    get middlewareApi() {
        const { _middlewareApi: api } = this;
        if (!api) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.MiddlewareError,
                message: 'Cannot perform this action without an active middleware connection',
            });
        }
        return api;
    }
    /**
     * @hidden
     *
     * Make a query to the middleware server using the apollo client
     */
    queryMiddleware(query) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            let result;
            try {
                result = yield this.middlewareApi.query(query);
            }
            catch (err) {
                const resultMessage = (_b = (_a = err.networkError) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.message;
                const { message: errorMessage } = err;
                const message = resultMessage !== null && resultMessage !== void 0 ? resultMessage : errorMessage;
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.MiddlewareError,
                    message: `Error in middleware query: ${message}`,
                });
            }
            return result;
        });
    }
    /**
     * @hidden
     *
     * Return whether the middleware was enabled at startup
     */
    isMiddlewareEnabled() {
        return !!this._middlewareApi;
    }
    /**
     * @hidden
     *
     * Return whether the middleware is enabled and online
     */
    isMiddlewareAvailable() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                this.middlewareApi.query(queries_1.heartbeat());
            }
            catch (err) {
                return false;
            }
            return true;
        });
    }
    /**
     * @hidden
     *
     * Retrieve the latest block number
     */
    getLatestBlock() {
        return __awaiter(this, void 0, void 0, function* () {
            const { number } = yield this.polymeshApi.rpc.chain.getHeader();
            return conversion_1.u32ToBigNumber(number.unwrap());
        });
    }
    /**
     * @hidden
     *
     * Retrieve the network version
     */
    getNetworkVersion() {
        return __awaiter(this, void 0, void 0, function* () {
            const version = yield this.polymeshApi.rpc.system.version();
            return conversion_1.textToString(version);
        });
    }
    /**
     * @hidden
     *
     * Disconnect the Polkadot API, middleware, and render this instance unusable
     *
     * @note after disconnecting, trying to access any property in this object will result
     *   in an error
     */
    disconnect() {
        const { polymeshApi } = this;
        let middlewareApi;
        if (this.isMiddlewareEnabled()) {
            ({ middlewareApi } = this);
        }
        this.isDisconnected = true;
        middlewareApi && middlewareApi.stop();
        return polymeshApi.disconnect();
    }
    /**
     * @hidden
     *
     * Returns a (shallow) clone of this instance. Useful for providing a separate
     *   Context to Procedures with different signers
     */
    clone() {
        const cloned = lodash_1.clone(this);
        cloned.polymeshApi = Context.createPolymeshApiProxy(cloned);
        return cloned;
    }
}
exports.Context = Context;
//# sourceMappingURL=Context.js.map