UNPKG

@substrate/api-sidecar

Version:

REST service that makes it easy to interact with blockchain nodes built using Substrate's FRAME framework.

192 lines 10 kB
"use strict"; // Copyright 2017-2025 Parity Technologies (UK) Ltd. // This file is part of Substrate API Sidecar. // // Substrate API Sidecar is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const types_1 = require("@polkadot/types"); const bn_js_1 = __importDefault(require("bn.js")); const http_errors_1 = require("http-errors"); const apiRegistry_1 = require("../../../apiRegistry"); const middleware_1 = require("../../../middleware"); const services_1 = require("../../../services"); const kusamaEarlyErasBlockInfo_json_1 = __importDefault(require("../../../services/accounts/kusamaEarlyErasBlockInfo.json")); const AbstractController_1 = __importDefault(require("../../AbstractController")); /** * GET payout information for a stash account on the relay chain. * * Path params: * - `address`: SS58 address of the account. Must be a _Stash_ account. * * Query params: * - (Optional) `depth`: The number of eras to query for payouts of. Must be less * than `HISTORY_DEPTH`. In cases where `era - (depth -1)` is less * than 0, the first era queried will be 0. Defaults to 1. * - (Optional) `era`: The era to query at. Max era payout info is available for * is the latest finished era: `active_era - 1`. Defaults to `active_era - 1`. * - (Optional) `unclaimedOnly`: Only return unclaimed rewards. Defaults to true. * * Returns: * - `at`: * - `hash`: The block's hash. * - `height`: The block's height. * - `eraPayouts`: array of * - `era`: Era this information is associated with. * - `totalEraRewardPoints`: Total reward points for the era. * - `totalEraPayout`: Total payout for the era. Validators split the payout * based on the portion of `totalEraRewardPoints` they have. * - `payouts`: array of * - `validatorId`: AccountId of the validator the payout is coming from. * - `nominatorStakingPayout`: Payout for the reward destination associated with the * accountId the query was made for. * - `claimed`: Whether or not the reward has been claimed. * - `totalValidatorRewardPoints`: Number of reward points earned by the validator. * - `validatorCommission`: The percentage of the total payout that the validator * takes as commission, expressed as a Perbill. * - `totalValidatorExposure`: The sum of the validator's and its nominators' stake. * - `nominatorExposure`: The amount of stake the nominator has behind the validator. * * Description: * Returns payout information for the last specified eras on the relay chain. If specifying both * the depth and era query params, this endpoint will return information for * (era - depth) through era. (i.e. if depth=5 and era=20 information will be * returned for eras 16 through 20). N.B. You cannot query eras less then * `current_era - HISTORY_DEPTH`. * * N.B. The `nominator*` fields correspond to the address being queried, even if it * is a validator's _stash_ address. This is because a validator is technically * nominating itself. * * `payouts` Is an array of payouts for a nominating stash address and information * about the validator they were nominating. `eraPayouts` contains an array of * objects that has staking reward metadata for each era, and an array of the * aformentioned payouts. */ class RcAccountsStakingPayoutsController extends AbstractController_1.default { constructor(_api) { var _a; const rcApiSpecName = (_a = apiRegistry_1.ApiPromiseRegistry.getSpecNameByType('relay')) === null || _a === void 0 ? void 0 : _a.values(); const rcSpecName = rcApiSpecName ? Array.from(rcApiSpecName)[0] : undefined; if (!rcSpecName) { throw new Error('Relay chain API spec name is not defined.'); } super(rcSpecName, '/rc/accounts/:address/staking-payouts', new services_1.AccountsStakingPayoutsService(rcSpecName)); /** * Get the payouts of `address` for `depth` starting from the `era` on the relay chain. * * @param req Express Request * @param res Express Response */ this.getStakingPayoutsByAccountId = async ({ params: { address }, query: { depth, era, unclaimedOnly, at } }, res) => { var _a; const rcApi = (_a = apiRegistry_1.ApiPromiseRegistry.getApiByType('relay')[0]) === null || _a === void 0 ? void 0 : _a.api; if (!rcApi) { throw new Error('Relay chain API not found, please use SAS_SUBSTRATE_MULTI_CHAIN_URL env variable'); } const { specName } = this; const isKusama = specName.toString().toLowerCase() === 'kusama'; let hash = await this.getHashFromAt(at, { api: rcApi }); let apiAt = await rcApi.at(hash); const { eraArg, currentEra } = await this.getEraAndHash(apiAt, this.verifyAndCastOr('era', era, undefined)); const sanitizedDepth = this.sanitizeDepth({ isKusama, depth: depth === null || depth === void 0 ? void 0 : depth.toString(), era: era === null || era === void 0 ? void 0 : era.toString(), currentEra, }); if (isKusama && currentEra < 518) { const earlyErasBlockInfo = kusamaEarlyErasBlockInfo_json_1.default; const block = earlyErasBlockInfo[currentEra].start; hash = await this.getHashFromAt(block.toString(), { api: rcApi }); apiAt = await rcApi.at(hash); } if (!apiAt.query.staking) { throw new http_errors_1.BadRequest('Staking pallet not found for queried runtime'); } // For RC endpoints, we always use the regular fetchAccountStakingPayout method // since we're querying the relay chain directly const result = await this.service.fetchAccountStakingPayout(hash, address, this.verifyAndCastOr('depth', sanitizedDepth, 1), eraArg, unclaimedOnly === 'false' ? false : true, currentEra, apiAt); RcAccountsStakingPayoutsController.sanitizedSend(res, result); }; this.initRoutes(); } initRoutes() { this.router.use(this.path, middleware_1.validateAddress, (0, middleware_1.validateBoolean)(['unclaimedOnly'])); this.safeMountAsyncGetHandlers([['', this.getStakingPayoutsByAccountId]]); } sanitizeDepth({ isKusama, depth, era, currentEra, }) { if (!isKusama) return depth; if (currentEra <= 519) { if (depth !== undefined) throw new http_errors_1.InternalServerError('The `depth` query parameter is disabled for eras less than 518.'); if (era !== undefined) throw new http_errors_1.InternalServerError('The `era` query parameter is disabled for eras less than 518.'); return undefined; } if (depth) { return Math.min(Number(depth), currentEra - 518).toString(); } return undefined; } async getEraAndHash(apiAt, era) { const currentEra = await this.getCurrentEra(apiAt); const activeEra = await this.getActiveEra(apiAt, currentEra); if (era !== undefined && era > activeEra - 1) { throw new http_errors_1.BadRequest(`The specified era (${era}) is too large. Largest era payout info is available for is ${activeEra - 1}`); } return { eraArg: era !== null && era !== void 0 ? era : activeEra - 1, currentEra, }; } async getCurrentEra(apiAt) { const currentEraMaybe = (await apiAt.query.staking.currentEra()); if (currentEraMaybe instanceof types_1.Option) { if (currentEraMaybe.isNone) throw new http_errors_1.InternalServerError('CurrentEra is None when Some was expected'); return currentEraMaybe.unwrap().toNumber(); } if (currentEraMaybe instanceof bn_js_1.default) { return currentEraMaybe.toNumber(); } throw new http_errors_1.InternalServerError('Query for current_era returned a non-processable result.'); } async getActiveEra(apiAt, currentEra) { if (!apiAt.query.staking.activeEra) { const sessionIndex = (await apiAt.query.session.currentIndex()).toNumber(); return currentEra < 518 || sessionIndex % 6 > 0 ? currentEra : currentEra - 1; } const activeEraOption = await apiAt.query.staking.activeEra(); if (activeEraOption.isNone) { const historicActiveEra = await apiAt.query.staking.currentEra(); if (historicActiveEra.isNone) throw new http_errors_1.InternalServerError('ActiveEra is None when Some was expected'); return historicActiveEra.unwrap().toNumber(); } return activeEraOption.unwrap().index.toNumber(); } } RcAccountsStakingPayoutsController.controllerName = 'RcAccountsStakingPayouts'; RcAccountsStakingPayoutsController.requiredPallets = [ ['Staking', 'System'], ['ParachainStaking', 'ParachainSystem'], ['ParachainStaking', 'System'], ['Staking', 'ParachainSystem'], ]; exports.default = RcAccountsStakingPayoutsController; //# sourceMappingURL=RcAccountsStakingPayoutsController.js.map