"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 types_1 = require("@polkadot/types");
const types_2 = require("@polkadot/types/types");
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 polkadot API instance
 * - Holds the middleware API instance (if any)
 * - Holds the Signing Manager (if any)
 */
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, ss58Format } = params;
        this._middlewareApi = middlewareApi;
        this._polymeshApi = polymeshApi;
        this.polymeshApi = Context.createPolymeshApiProxy(this);
        this.ss58Format = ss58Format;
    }
    /**
     * @hidden
     */
    static createPolymeshApiProxy(ctx) {
        return new Proxy(ctx._polymeshApi, {
            get: (target, prop) => {
                if (prop === 'tx' && !ctx.signingAddress) {
                    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, signingManager } = params;
            const ss58Format = (0, conversion_1.u8ToBigNumber)(polymeshApi.consts.system.ss58Prefix);
            const context = new Context({ polymeshApi, middlewareApi, signingManager, ss58Format });
            const isArchiveNodePromise = context.isCurrentNodeArchive();
            if (signingManager) {
                yield context.setSigningManager(signingManager);
            }
            context.isArchiveNode = yield isArchiveNodePromise;
            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((0, conversion_1.bigNumberToU32)(new bignumber_js_1.default(0), this));
                const balance = yield balances.totalIssuance.at(blockHash);
                return (0, conversion_1.balanceToBigNumber)(balance).gt(new bignumber_js_1.default(0));
            }
            catch (e) {
                return false;
            }
        });
    }
    /**
     * @hidden
     *
     * @note the signing Account will be set to the Signing Manager's first Account. If the Signing Manager has
     *   no Accounts yet, the signing Account will be left empty
     */
    setSigningManager(signingManager) {
        return __awaiter(this, void 0, void 0, function* () {
            this._signingManager = signingManager;
            signingManager.setSs58Format(this.ss58Format.toNumber());
            // this could be undefined
            const [firstAccount] = yield signingManager.getAccounts();
            if (!firstAccount) {
                this.signingAddress = undefined;
            }
            else {
                return this.setSigningAddress(firstAccount);
            }
        });
    }
    /**
     * @hidden
     */
    get signingManager() {
        const { _signingManager: manager } = this;
        if (!manager) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.General,
                message: 'There is no Signing Manager attached to the SDK',
            });
        }
        return manager;
    }
    /**
     * @hidden
     *
     * Retrieve a list of Accounts that can sign transactions
     */
    getSigningAccounts() {
        return __awaiter(this, void 0, void 0, function* () {
            const { signingManager } = this;
            const accounts = yield signingManager.getAccounts();
            return accounts.map(address => new internal_1.Account({ address }, this));
        });
    }
    /**
     * @hidden
     *
     * Set the signing Account from among the existing ones in the Signing Manager
     *
     * @throws if the passed address isn't valid, or isn't present in the Signing Manager
     */
    setSigningAddress(signingAddress) {
        return __awaiter(this, void 0, void 0, function* () {
            const { signingManager } = this;
            const newAddress = signingAddress;
            (0, internal_2.assertAddressValid)(newAddress, this.ss58Format);
            const accounts = yield signingManager.getAccounts();
            const newSigningAddress = accounts.find(account => {
                return account === newAddress;
            });
            if (!newSigningAddress) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.General,
                    message: 'The Account is not part of the Signing Manager attached to the SDK',
                });
            }
            this.signingAddress = newSigningAddress;
        });
    }
    // 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 = (0, conversion_1.signerToString)(account);
            }
            else {
                ({ address } = this.getSigningAccount());
            }
            const rawAddress = (0, 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 = (0, conversion_1.balanceToBigNumber)(rawReserved);
                const total = (0, conversion_1.balanceToBigNumber)(rawFree).plus(reserved);
                const locked = bignumber_js_1.default.max((0, conversion_1.balanceToBigNumber)(miscFrozen), (0, 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 = (0, conversion_1.signerToString)(account);
            }
            else {
                ({ address } = this.getSigningAccount());
            }
            const rawAddress = (0, conversion_1.stringToAccountId)(address, this);
            const assembleResult = (meshSubsidy) => {
                if (meshSubsidy.isNone) {
                    return null;
                }
                const { paying_key: payingKey, remaining } = meshSubsidy.unwrap();
                const allowance = (0, conversion_1.balanceToBigNumber)(remaining);
                const subsidy = new internal_1.Subsidy({ beneficiary: address, subsidizer: (0, conversion_1.accountIdToString)(payingKey) }, this);
                return {
                    subsidy,
                    allowance,
                };
            };
            if (callback) {
                return relayer.subsidies(rawAddress, subsidy => {
                    callback(assembleResult(subsidy));
                });
            }
            const subsidies = yield relayer.subsidies(rawAddress);
            return assembleResult(subsidies);
        });
    }
    /**
     * @hidden
     *
     * Retrieve the signing Account
     *
     * @throws if there is no signing Account associated to the SDK instance
     */
    getSigningAccount() {
        const address = this.getSigningAddress();
        return new internal_1.Account({ address }, this);
    }
    /**
     * @hidden
     *
     * Retrieve the signing Identity
     *
     * @throws if there is no Identity associated to the signing Account (or there is no signing Account associated to the SDK instance)
     */
    getSigningIdentity() {
        return __awaiter(this, void 0, void 0, function* () {
            const account = this.getSigningAccount();
            const identity = yield account.getIdentity();
            if (identity === null) {
                throw new internal_1.PolymeshError({
                    code: types_5.ErrorCode.DataUnavailable,
                    message: 'The signing Account does not have an associated Identity',
                });
            }
            return identity;
        });
    }
    /**
     * @hidden
     *
     * Retrieve the signing address
     *
     * @throws if there is no signing Account associated to the SDK instance
     */
    getSigningAddress() {
        const { signingAddress } = this;
        if (!signingAddress) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.General,
                message: 'There is no signing Account associated with the SDK instance',
            });
        }
        return signingAddress;
    }
    /**
     * @hidden
     *
     * Retrieve the external signer from the Signing Manager
     */
    getExternalSigner() {
        const { signingManager } = this;
        return signingManager.getExternalSigner();
    }
    /**
     * @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 => (0, 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 = (0, 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 (0, conversion_1.balanceToBigNumber)(baseFee).multipliedBy((0, 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 { 
        /*
         * we use the non-proxy polkadot instance since we shouldn't need to
         * have a signer Account for this method
         */
        _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 = (0, 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 = (0, types_1.getTypeDef)(type.toString());
            const argName = (0, 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 = (0, 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((0, conversion_1.stringToTicker)(ticker, this))));
                const eligibleCas = (0, 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 = (0, conversion_1.u32ToBigNumber)(rawId);
                    const ticker = (0, conversion_1.tickerToString)(rawTicker);
                    const caId = (0, conversion_1.corporateActionIdentifierToCaId)({ ticker, localId }, this);
                    const details = yield corporateActionQuery.details(caId);
                    const action = corporateAction.unwrap();
                    return {
                        ticker,
                        localId,
                        caId,
                        corporateAction: (0, 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 = (0, lodash_1.chunk)(distributionsMultiParams, constants_1.MAX_PAGE_SIZE.toNumber());
            const requestChunks = (0, 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 = [];
            (0, 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]), (0, conversion_1.distributionToDividendDistributionParams)(dist, this)), this),
                    details: {
                        remainingFunds: (0, conversion_1.balanceToBigNumber)(remaining),
                        fundsReclaimed: (0, 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 = (0, lodash_1.flatMap)(targets, target => claimTypes.map(claimType => {
                return {
                    target: (0, conversion_1.signerToString)(target),
                    // eslint-disable-next-line @typescript-eslint/naming-convention
                    claim_type: (0, conversion_1.claimTypeToMeshClaimType)(claimType, this),
                };
            }));
            const claimIssuerDids = trustedClaimIssuers === null || trustedClaimIssuers === void 0 ? void 0 : trustedClaimIssuers.map(trustedClaimIssuer => (0, 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 ? (0, conversion_1.momentToDate)(rawExpiry.unwrap()) : null;
                    if ((!includeExpired && (expiry === null || expiry > new Date())) || includeExpired) {
                        data.push({
                            target: new internal_1.Identity({ did: (0, conversion_1.identityIdToString)(target) }, this),
                            issuer: new internal_1.Identity({ did: (0, conversion_1.identityIdToString)(claimIssuer) }, this),
                            issuedAt: (0, conversion_1.momentToDate)(issuanceDate),
                            expiry,
                            claim: (0, conversion_1.meshClaimToClaim)(claim),
                        });
                    }
                });
                return data;
            }));
            return (0, 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((0, queries_1.didsWithClaims)({
                dids: targets === null || targets === void 0 ? void 0 : targets.map(target => (0, conversion_1.signerToString)(target)),
                trustedClaimIssuers: trustedClaimIssuers === null || trustedClaimIssuers === void 0 ? void 0 : trustedClaimIssuers.map(trustedClaimIssuer => (0, conversion_1.signerToString)(trustedClaimIssuer)),
                claimTypes: claimTypes === null || claimTypes === void 0 ? void 0 : claimTypes.map(ct => types_4.ClaimTypeEnum[ct]),
                includeExpired,
                count: size === null || size === void 0 ? void 0 : size.toNumber(),
                skip: start === null || start === void 0 ? void 0 : start.toNumber(),
            }));
            const { data: { didsWithClaims: { items: didsWithClaimsList, totalCount }, }, } = result;
            const count = new bignumber_js_1.default(totalCount);
            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: (0, internal_2.createClaim)(type, jurisdiction, scope, cddId, undefined),
                    });
                });
            });
            const next = (0, 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((0, 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 (0, 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 (0, 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 signing Accounts
     */
    clone() {
        const cloned = (0, lodash_1.clone)(this);
        cloned.polymeshApi = Context.createPolymeshApiProxy(cloned);
        return cloned;
    }
    /**
     *  @hidden
     *
     * Creates an instance of a type as registered in the polymeshApi instance
     */
    createType(type, params) {
        try {
            return this.polymeshApi.createType(type, params);
        }
        catch (error) {
            throw new internal_1.PolymeshError({
                code: types_5.ErrorCode.UnexpectedError,
                message: `Could not create internal Polymesh type: "${type}". Please report this error to the Polymath team`,
                data: { type, params, error },
            });
        }
    }
}
exports.Context = Context;
//# sourceMappingURL=Context.js.map