Newer
Older
import { ApiPromise } from '@polkadot/api';
import { RequestHandler } from 'express';
import { BadRequest, InternalServerError } from 'http-errors';
import { validateAddress } from '../../middleware';
import { AccountsStakingPayoutsService } from '../../services';
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { IAddressParam } from '../../types/requests';
import AbstractController from '../AbstractController';
/**
* GET payout information for a stash account.
*
* 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. 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.
*
*/
export default class AccountsStakingPayoutsController extends AbstractController<
AccountsStakingPayoutsService
> {
constructor(api: ApiPromise) {
super(
api,
'/accounts/:address/staking-payouts',
new AccountsStakingPayoutsService(api)
);
this.initRoutes();
}
protected initRoutes(): void {
this.router.use(this.path, validateAddress);
this.safeMountAsyncGetHandlers([
['', this.getStakingPayoutsByAccountId],
]);
}
/**
* Get the payouts of `address` for `depth` starting from the `era`.
*
* @param req Express Request
* @param res Express Response
*/
private getStakingPayoutsByAccountId: RequestHandler<
IAddressParam
> = async (
{ params: { address }, query: { depth, era, unclaimedOnly } },
res
): Promise<void> => {
const { hash, eraArg, currentEra } = await this.getEraAndHash(
this.verifyAndCastOr('era', era, undefined)
);
const unclaimedOnlyArg = unclaimedOnly === 'false' ? false : true;
AccountsStakingPayoutsController.sanitizedSend(
res,
await this.service.fetchAccountStakingPayout(
hash,
address,
this.verifyAndCastOr('depth', depth, 1) as number,
eraArg,
unclaimedOnlyArg,
currentEra
)
);
};
private async getEraAndHash(era?: number) {
const [hash, activeEraOption, currentEraOption] = await Promise.all([
this.api.rpc.chain.getFinalizedHead(),
this.api.query.staking.activeEra(),
this.api.query.staking.currentEra(),
]);
if (activeEraOption.isNone) {
throw new InternalServerError(
'ActiveEra is None when Some was expected'
);
}
const activeEra = activeEraOption.unwrap().index.toNumber();
if (currentEraOption.isNone) {
throw new InternalServerError(
'CurrentEra is None when Some was expected'
);
}
const currentEra = currentEraOption.unwrap().toNumber();
if (era !== undefined && era > activeEra - 1) {
throw new BadRequest(
`The specified era (${era}) is too large. ` +
`Largest era payout info is available for is ${
activeEra - 1
}`
);
}
return {
hash,
eraArg: era === undefined ? activeEra - 1 : era,
currentEra,
};
}
}