@ledgerhq/coin-polkadot 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.eslintrc.js +57 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/CHANGELOG.md +12 -0
  4. package/jest.config.js +10 -0
  5. package/lib/account.d.ts +15 -0
  6. package/lib/account.d.ts.map +1 -0
  7. package/lib/account.js +133 -0
  8. package/lib/account.js.map +1 -0
  9. package/lib/address.d.ts +7 -0
  10. package/lib/address.d.ts.map +1 -0
  11. package/lib/address.js +28 -0
  12. package/lib/address.js.map +1 -0
  13. package/lib/api/bisontrails.d.ts +11 -0
  14. package/lib/api/bisontrails.d.ts.map +1 -0
  15. package/lib/api/bisontrails.js +351 -0
  16. package/lib/api/bisontrails.js.map +1 -0
  17. package/lib/api/common.d.ts +12 -0
  18. package/lib/api/common.d.ts.map +1 -0
  19. package/lib/api/common.js +41 -0
  20. package/lib/api/common.js.map +1 -0
  21. package/lib/api/index.d.ts +3 -0
  22. package/lib/api/index.d.ts.map +1 -0
  23. package/lib/api/index.js +53 -0
  24. package/lib/api/index.js.map +1 -0
  25. package/lib/api/sidecar.d.ts +193 -0
  26. package/lib/api/sidecar.d.ts.map +1 -0
  27. package/lib/api/sidecar.js +844 -0
  28. package/lib/api/sidecar.js.map +1 -0
  29. package/lib/api/sidecar.types.d.ts +163 -0
  30. package/lib/api/sidecar.types.d.ts.map +1 -0
  31. package/lib/api/sidecar.types.js +3 -0
  32. package/lib/api/sidecar.types.js.map +1 -0
  33. package/lib/api/subscan.d.ts +6 -0
  34. package/lib/api/subscan.d.ts.map +1 -0
  35. package/lib/api/subscan.js +375 -0
  36. package/lib/api/subscan.js.map +1 -0
  37. package/lib/api/websocket.d.ts +92 -0
  38. package/lib/api/websocket.d.ts.map +1 -0
  39. package/lib/api/websocket.js +759 -0
  40. package/lib/api/websocket.js.map +1 -0
  41. package/lib/bridge/js.d.ts +6 -0
  42. package/lib/bridge/js.d.ts.map +1 -0
  43. package/lib/bridge/js.js +120 -0
  44. package/lib/bridge/js.js.map +1 -0
  45. package/lib/bridge.integration.test.d.ts +4 -0
  46. package/lib/bridge.integration.test.d.ts.map +1 -0
  47. package/lib/bridge.integration.test.js +795 -0
  48. package/lib/bridge.integration.test.js.map +1 -0
  49. package/lib/cache.d.ts +65 -0
  50. package/lib/cache.d.ts.map +1 -0
  51. package/lib/cache.js +182 -0
  52. package/lib/cache.js.map +1 -0
  53. package/lib/cli-transaction.d.ts +44 -0
  54. package/lib/cli-transaction.d.ts.map +1 -0
  55. package/lib/cli-transaction.js +148 -0
  56. package/lib/cli-transaction.js.map +1 -0
  57. package/lib/deviceTransactionConfig.d.ts +16 -0
  58. package/lib/deviceTransactionConfig.d.ts.map +1 -0
  59. package/lib/deviceTransactionConfig.js +171 -0
  60. package/lib/deviceTransactionConfig.js.map +1 -0
  61. package/lib/errors.d.ts +37 -0
  62. package/lib/errors.d.ts.map +1 -0
  63. package/lib/errors.js +17 -0
  64. package/lib/errors.js.map +1 -0
  65. package/lib/hw-getAddress.d.ts +4 -0
  66. package/lib/hw-getAddress.d.ts.map +1 -0
  67. package/lib/hw-getAddress.js +64 -0
  68. package/lib/hw-getAddress.js.map +1 -0
  69. package/lib/js-buildTransaction.d.ts +25 -0
  70. package/lib/js-buildTransaction.d.ts.map +1 -0
  71. package/lib/js-buildTransaction.js +265 -0
  72. package/lib/js-buildTransaction.js.map +1 -0
  73. package/lib/js-createTransaction.d.ts +9 -0
  74. package/lib/js-createTransaction.d.ts.map +1 -0
  75. package/lib/js-createTransaction.js +22 -0
  76. package/lib/js-createTransaction.js.map +1 -0
  77. package/lib/js-estimateMaxSpendable.d.ts +15 -0
  78. package/lib/js-estimateMaxSpendable.d.ts.map +1 -0
  79. package/lib/js-estimateMaxSpendable.js +89 -0
  80. package/lib/js-estimateMaxSpendable.js.map +1 -0
  81. package/lib/js-getFeesForTransaction.d.ts +14 -0
  82. package/lib/js-getFeesForTransaction.d.ts.map +1 -0
  83. package/lib/js-getFeesForTransaction.js +94 -0
  84. package/lib/js-getFeesForTransaction.js.map +1 -0
  85. package/lib/js-getTransactionStatus.d.ts +18 -0
  86. package/lib/js-getTransactionStatus.d.ts.map +1 -0
  87. package/lib/js-getTransactionStatus.js +316 -0
  88. package/lib/js-getTransactionStatus.js.map +1 -0
  89. package/lib/js-prepareTransaction.d.ts +9 -0
  90. package/lib/js-prepareTransaction.d.ts.map +1 -0
  91. package/lib/js-prepareTransaction.js +83 -0
  92. package/lib/js-prepareTransaction.js.map +1 -0
  93. package/lib/js-signOperation.d.ts +27 -0
  94. package/lib/js-signOperation.d.ts.map +1 -0
  95. package/lib/js-signOperation.js +235 -0
  96. package/lib/js-signOperation.js.map +1 -0
  97. package/lib/js-synchronisation.d.ts +3 -0
  98. package/lib/js-synchronisation.d.ts.map +1 -0
  99. package/lib/js-synchronisation.js +104 -0
  100. package/lib/js-synchronisation.js.map +1 -0
  101. package/lib/logic.d.ts +107 -0
  102. package/lib/logic.d.ts.map +1 -0
  103. package/lib/logic.js +242 -0
  104. package/lib/logic.js.map +1 -0
  105. package/lib/platformAdapter.d.ts +11 -0
  106. package/lib/platformAdapter.d.ts.map +1 -0
  107. package/lib/platformAdapter.js +25 -0
  108. package/lib/platformAdapter.js.map +1 -0
  109. package/lib/platformAdapter.test.d.ts +2 -0
  110. package/lib/platformAdapter.test.d.ts.map +1 -0
  111. package/lib/platformAdapter.test.js +51 -0
  112. package/lib/platformAdapter.test.js.map +1 -0
  113. package/lib/polkadot-crypto.d.ts +2 -0
  114. package/lib/polkadot-crypto.d.ts.map +1 -0
  115. package/lib/polkadot-crypto.js +14 -0
  116. package/lib/polkadot-crypto.js.map +1 -0
  117. package/lib/preload.d.ts +14 -0
  118. package/lib/preload.d.ts.map +1 -0
  119. package/lib/preload.js +173 -0
  120. package/lib/preload.js.map +1 -0
  121. package/lib/serialization.d.ts +4 -0
  122. package/lib/serialization.d.ts.map +1 -0
  123. package/lib/serialization.js +51 -0
  124. package/lib/serialization.js.map +1 -0
  125. package/lib/specs.d.ts +7 -0
  126. package/lib/specs.d.ts.map +1 -0
  127. package/lib/specs.js +274 -0
  128. package/lib/specs.js.map +1 -0
  129. package/lib/speculos-deviceActions.d.ts +4 -0
  130. package/lib/speculos-deviceActions.d.ts.map +1 -0
  131. package/lib/speculos-deviceActions.js +85 -0
  132. package/lib/speculos-deviceActions.js.map +1 -0
  133. package/lib/transaction.d.ts +15 -0
  134. package/lib/transaction.d.ts.map +1 -0
  135. package/lib/transaction.js +51 -0
  136. package/lib/transaction.js.map +1 -0
  137. package/lib/types.d.ts +96 -0
  138. package/lib/types.d.ts.map +1 -0
  139. package/lib/types.js +3 -0
  140. package/lib/types.js.map +1 -0
  141. package/lib/validators.d.ts +3 -0
  142. package/lib/validators.d.ts.map +1 -0
  143. package/lib/validators.js +7 -0
  144. package/lib/validators.js.map +1 -0
  145. package/package.json +95 -0
  146. package/src/account.ts +170 -0
  147. package/src/address.ts +28 -0
  148. package/src/api/bisontrails.ts +343 -0
  149. package/src/api/common.ts +52 -0
  150. package/src/api/index.ts +38 -0
  151. package/src/api/sidecar.ts +735 -0
  152. package/src/api/sidecar.types.ts +162 -0
  153. package/src/api/subscan.ts +362 -0
  154. package/src/api/websocket.ts +535 -0
  155. package/src/bridge/js.ts +81 -0
  156. package/src/bridge.integration.test.ts +764 -0
  157. package/src/cache.ts +202 -0
  158. package/src/cli-transaction.ts +184 -0
  159. package/src/deviceTransactionConfig.ts +209 -0
  160. package/src/errors.ts +39 -0
  161. package/src/hw-getAddress.ts +14 -0
  162. package/src/js-buildTransaction.ts +206 -0
  163. package/src/js-createTransaction.ts +22 -0
  164. package/src/js-estimateMaxSpendable.ts +37 -0
  165. package/src/js-getFeesForTransaction.ts +41 -0
  166. package/src/js-getTransactionStatus.ts +350 -0
  167. package/src/js-prepareTransaction.ts +27 -0
  168. package/src/js-signOperation.ts +232 -0
  169. package/src/js-synchronisation.ts +57 -0
  170. package/src/logic.ts +266 -0
  171. package/src/platformAdapter.test.ts +61 -0
  172. package/src/platformAdapter.ts +29 -0
  173. package/src/polkadot-crypto.ts +10 -0
  174. package/src/preload.ts +148 -0
  175. package/src/serialization.ts +48 -0
  176. package/src/specs.ts +305 -0
  177. package/src/speculos-deviceActions.ts +84 -0
  178. package/src/transaction.ts +64 -0
  179. package/src/types.ts +118 -0
  180. package/src/validators.ts +2 -0
  181. package/tsconfig.json +21 -0
@@ -0,0 +1,735 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import querystring from "querystring";
3
+ import { TypeRegistry } from "@polkadot/types";
4
+ import { getSpecTypes } from "@polkadot/types-known";
5
+ import { Metadata } from "@polkadot/types/metadata";
6
+ import { expandMetadata } from "@polkadot/types/metadata/decorate";
7
+ import { Extrinsics } from "@polkadot/types/metadata/decorate/types";
8
+ import { makeLRUCache } from "@ledgerhq/coin-framework/cache";
9
+ import type { CacheRes } from "@ledgerhq/coin-framework/cache";
10
+ import { getEnv } from "@ledgerhq/coin-framework/env";
11
+ import network from "@ledgerhq/coin-framework/network";
12
+ import type {
13
+ PolkadotValidator,
14
+ PolkadotStakingProgress,
15
+ PolkadotUnlocking,
16
+ PolkadotNomination,
17
+ } from "../types";
18
+ import type {
19
+ SidecarAccountBalanceInfo,
20
+ SidecarPalletStorageItem,
21
+ SidecarStakingInfo,
22
+ SidecarNominations,
23
+ SidecarConstants,
24
+ SidecarValidatorsParamStatus,
25
+ SidecarValidatorsParamAddresses,
26
+ SidecarValidators,
27
+ SidecarPalletStakingProgress,
28
+ SidecarTransactionMaterial,
29
+ SidecarTransactionBroadcast,
30
+ SidecarPaymentInfo,
31
+ SidecarRuntimeSpec,
32
+ } from "./sidecar.types";
33
+
34
+ /**
35
+ * Get indexer base url.
36
+ *
37
+ * @returns {string}
38
+ */
39
+ const getBaseSidecarUrl = (): string => getEnv("API_POLKADOT_SIDECAR");
40
+
41
+ /**
42
+ * Returns the full indexer url for en route endpoint.
43
+ *
44
+ * @param {*} route
45
+ *
46
+ * @returns {string}
47
+ */
48
+ const getSidecarUrl = (route): string => `${getBaseSidecarUrl()}${route || ""}`;
49
+
50
+ const VALIDATOR_COMISSION_RATIO = 1000000000;
51
+ const ELECTION_STATUS_OPTIMISTIC_THRESHOLD =
52
+ getEnv("POLKADOT_ELECTION_STATUS_THRESHOLD") || 25;
53
+
54
+ // blocks = 2 minutes 30
55
+
56
+ /**
57
+ * Fetch Balance from the api.
58
+ *
59
+ * @async
60
+ * @param {string} addr
61
+ *
62
+ * @returns {SidecarAccountBalanceInfo}
63
+ */
64
+ const fetchBalanceInfo = async (
65
+ addr: string
66
+ ): Promise<SidecarAccountBalanceInfo> => {
67
+ const {
68
+ data,
69
+ }: {
70
+ data: SidecarAccountBalanceInfo;
71
+ } = await network({
72
+ method: "GET",
73
+ url: getSidecarUrl(`/accounts/${addr}/balance-info`),
74
+ });
75
+ return data;
76
+ };
77
+
78
+ /**
79
+ * Fetch the stash address associated to an account.
80
+ *
81
+ * @async
82
+ * @param {string} addr
83
+ *
84
+ * @returns {string}
85
+ */
86
+ const fetchStashAddr = async (addr: string): Promise<string | null> => {
87
+ const {
88
+ data,
89
+ }: {
90
+ data: SidecarPalletStorageItem;
91
+ } = await network({
92
+ method: "GET",
93
+ url: getSidecarUrl(
94
+ `/pallets/staking/storage/ledger?keys[]=${addr}&key1=${addr}`
95
+ ),
96
+ });
97
+ return data.value?.stash ?? null;
98
+ };
99
+
100
+ /**
101
+ * Fetch the controller address associated to an account.
102
+ *
103
+ * @async
104
+ * @param {string} addr
105
+ *
106
+ * @returns {string}
107
+ */
108
+ const fetchControllerAddr = async (addr: string): Promise<string | null> => {
109
+ const {
110
+ data,
111
+ }: {
112
+ data: SidecarPalletStorageItem;
113
+ } = await network({
114
+ method: "GET",
115
+ url: getSidecarUrl(
116
+ `/pallets/staking/storage/bonded?keys[]=${addr}&key1=${addr}`
117
+ ),
118
+ });
119
+ return data.value ?? null;
120
+ };
121
+
122
+ /**
123
+ * Fetch the staking info for an account.
124
+ *
125
+ * @async
126
+ * @param {string} addr
127
+ *
128
+ * @returns {SidecarStakingInfo}
129
+ */
130
+ const fetchStakingInfo = async (addr: string): Promise<SidecarStakingInfo> => {
131
+ const {
132
+ data,
133
+ }: {
134
+ data: SidecarStakingInfo;
135
+ } = await network({
136
+ method: "GET",
137
+ url: getSidecarUrl(`/accounts/${addr}/staking-info`),
138
+ });
139
+ return data;
140
+ };
141
+
142
+ /**
143
+ * Returns the list of nominations for an account, with status and associated stake if relevant.
144
+ *
145
+ * @async
146
+ * @param {string} addr
147
+ *
148
+ * @returns {SidecarNominations}
149
+ */
150
+ const fetchNominations = async (addr: string): Promise<SidecarNominations> => {
151
+ const {
152
+ data,
153
+ }: {
154
+ data: SidecarNominations;
155
+ } = await network({
156
+ method: "GET",
157
+ url: getSidecarUrl(`/accounts/${addr}/nominations`),
158
+ });
159
+ return data;
160
+ };
161
+
162
+ /**
163
+ * Returns the blockchain's runtime constants.
164
+ *
165
+ * @async
166
+ * @param {string} addr
167
+ *
168
+ * @returns {Object}
169
+ */
170
+ const fetchConstants = async (): Promise<Record<string, any>> => {
171
+ const {
172
+ data,
173
+ }: {
174
+ data: SidecarConstants;
175
+ } = await network({
176
+ method: "GET",
177
+ url: getSidecarUrl(`/runtime/constants`),
178
+ });
179
+ return data.consts;
180
+ };
181
+
182
+ /**
183
+ * Returns the activeEra info
184
+ *
185
+ * @async
186
+ *
187
+ * @returns {SidecarPalletStorageItem}
188
+ */
189
+ const fetchActiveEra = async (): Promise<SidecarPalletStorageItem> => {
190
+ const {
191
+ data,
192
+ }: {
193
+ data: SidecarPalletStorageItem;
194
+ } = await network({
195
+ method: "GET",
196
+ url: getSidecarUrl("/pallets/staking/storage/activeEra"),
197
+ });
198
+ return data;
199
+ };
200
+
201
+ /**
202
+ * Fetch the minimum value allowed for a bond
203
+ *
204
+ * @async
205
+ * @param {string} addr
206
+ *
207
+ * @returns {string}
208
+ */
209
+ export const getMinimumBondBalance = async (): Promise<BigNumber> => {
210
+ const { data }: { data: SidecarPalletStorageItem } = await network({
211
+ method: "GET",
212
+ url: getSidecarUrl(`/pallets/staking/storage/minNominatorBond`),
213
+ });
214
+
215
+ return (data.value && new BigNumber(data.value)) || new BigNumber(0);
216
+ };
217
+
218
+ /**
219
+ * Fetch a list of validators with some info and indentity.
220
+ * It fetches the list providing a status (all, elected, waiting) and/or a list of
221
+ * addresses (comma-separated or as multiple query params).
222
+ *
223
+ * @async
224
+ * @param {string} status
225
+ * @param {string[]} addresses
226
+ *
227
+ * @returns {SidecarValidators}
228
+ */
229
+ const fetchValidators = async (
230
+ status: SidecarValidatorsParamStatus = "all",
231
+ addresses?: SidecarValidatorsParamAddresses
232
+ ): Promise<SidecarValidators> => {
233
+ let params = {};
234
+
235
+ if (status) {
236
+ params = { ...params, status };
237
+ }
238
+
239
+ if (addresses && addresses.length) {
240
+ params = { ...params, addresses: addresses.join(",") };
241
+ }
242
+
243
+ const {
244
+ data,
245
+ }: {
246
+ data: SidecarValidators;
247
+ } = await network({
248
+ method: "GET",
249
+ url: getSidecarUrl(`/validators?${querystring.stringify(params)}`),
250
+ });
251
+ return data;
252
+ };
253
+
254
+ /**
255
+ * Fetch the progress info concerning staking.
256
+ *
257
+ * @async
258
+ *
259
+ * @returns {SidecarPalletStakingProgress}
260
+ */
261
+ const fetchStakingProgress =
262
+ async (): Promise<SidecarPalletStakingProgress> => {
263
+ const {
264
+ data,
265
+ }: {
266
+ data: SidecarPalletStakingProgress;
267
+ } = await network({
268
+ method: "GET",
269
+ url: getSidecarUrl("/pallets/staking/progress"),
270
+ });
271
+ return data;
272
+ };
273
+
274
+ /**
275
+ * Fetch the transaction params needed to sign a transaction
276
+ *
277
+ * @async
278
+ *
279
+ * @returns {SidecarTransactionMaterial}
280
+ */
281
+ const fetchTransactionMaterial = async (
282
+ // By default we don't want any metadata.
283
+ withMetadata = false
284
+ ): Promise<SidecarTransactionMaterial> => {
285
+ const params = withMetadata ? "?metadata=scale" : "?noMeta=true";
286
+ const {
287
+ data,
288
+ }: {
289
+ data: SidecarTransactionMaterial;
290
+ } = await network({
291
+ method: "GET",
292
+ url: getSidecarUrl(`/transaction/material${params}`),
293
+ });
294
+ return data;
295
+ };
296
+
297
+ /**
298
+ * Fetch the blockchain specs
299
+ *
300
+ * @async
301
+ *
302
+ * @returns {SidecarRuntimeSpec}
303
+ */
304
+ const fetchChainSpec = async () => {
305
+ const {
306
+ data,
307
+ }: {
308
+ data: SidecarRuntimeSpec;
309
+ } = await network({
310
+ method: "GET",
311
+ url: getSidecarUrl("/runtime/spec"),
312
+ });
313
+ return data;
314
+ };
315
+
316
+ // Not relevant when not using websocket
317
+ export const disconnect = () => {};
318
+
319
+ /**
320
+ * Returns true if ElectionStatus is Close.
321
+ * If ElectionStatus is Open, some features must be disabled.
322
+ *
323
+ * @async
324
+ *
325
+ * @returns {boolean}
326
+ */
327
+ export const isElectionClosed = async (): Promise<boolean> => {
328
+ const progress = await fetchStakingProgress();
329
+ return !progress.electionStatus?.status?.Open;
330
+ };
331
+
332
+ /**
333
+ * Returns true if the address is a new account with no balance.
334
+ *
335
+ * @async
336
+ * @param {*} addr
337
+ *
338
+ * @returns {boolean}
339
+ */
340
+ export const isNewAccount = async (addr: string): Promise<boolean> => {
341
+ const { nonce, free } = await fetchBalanceInfo(addr);
342
+ return new BigNumber(0).isEqualTo(nonce) && new BigNumber(0).isEqualTo(free);
343
+ };
344
+
345
+ /**
346
+ * Returns true if the address is a controller, i.e. it is associated to a stash.
347
+ *
348
+ * @async
349
+ * @param {*} addr
350
+ *
351
+ * @returns {boolean}
352
+ */
353
+ export const isControllerAddress = async (addr: string): Promise<boolean> => {
354
+ const stash = await fetchStashAddr(addr);
355
+ return !!stash;
356
+ };
357
+
358
+ /**
359
+ * Returns all addresses that are not validators.
360
+ *
361
+ * @async
362
+ * @param {string[]} - address to verify
363
+ *
364
+ * @returns {string[]} - addresses that are not validators
365
+ */
366
+ export const verifyValidatorAddresses = async (
367
+ validators: string[]
368
+ ): Promise<string[]> => {
369
+ const existingValidators = await fetchValidators("all", validators);
370
+ const existingIds = existingValidators.map((v) => v.accountId);
371
+ return validators.filter((v) => !existingIds.includes(v));
372
+ };
373
+
374
+ /**
375
+ * Get all account-related data
376
+ *
377
+ * @async
378
+ * @param {*} addr
379
+ */
380
+ export const getAccount = async (addr: string) => {
381
+ const balances = await getBalances(addr);
382
+ const stakingInfo = await getStakingInfo(addr);
383
+ const nominations = await getNominations(addr);
384
+ return { ...balances, ...stakingInfo, nominations };
385
+ };
386
+
387
+ /**
388
+ * Returns all the balances for an account
389
+ *
390
+ * @async
391
+ * @param {*} addr - the account address
392
+ */
393
+ const getBalances = async (addr: string) => {
394
+ const balanceInfo = await fetchBalanceInfo(addr);
395
+ // Locked is the highest value among locks
396
+ const totalLocked = balanceInfo.locks.reduce((total, lock) => {
397
+ const amount = new BigNumber(lock.amount);
398
+
399
+ if (amount.gt(total)) {
400
+ return amount;
401
+ }
402
+
403
+ return total;
404
+ }, new BigNumber(0));
405
+ const balance = new BigNumber(balanceInfo.free);
406
+ const spendableBalance = totalLocked.gt(balance)
407
+ ? new BigNumber(0)
408
+ : balance.minus(totalLocked);
409
+ return {
410
+ blockHeight: balanceInfo.at?.height ? Number(balanceInfo.at.height) : null,
411
+ balance,
412
+ spendableBalance,
413
+ nonce: Number(balanceInfo.nonce),
414
+ lockedBalance: new BigNumber(balanceInfo.miscFrozen),
415
+ };
416
+ };
417
+
418
+ /**
419
+ * Returns all staking-related data for an account
420
+ *
421
+ * @async
422
+ * @param {*} addr
423
+ */
424
+ export const getStakingInfo = async (addr: string) => {
425
+ const [stash, controller] = await Promise.all([
426
+ fetchStashAddr(addr),
427
+ fetchControllerAddr(addr),
428
+ ]);
429
+
430
+ // If account is not a stash, no need to fetch staking-info (it would return an error)
431
+ if (!controller) {
432
+ return {
433
+ controller: null,
434
+ stash: stash || null,
435
+ unlockedBalance: new BigNumber(0),
436
+ unlockingBalance: new BigNumber(0),
437
+ unlockings: [],
438
+ };
439
+ }
440
+
441
+ const [stakingInfo, activeEra, consts] = await Promise.all([
442
+ fetchStakingInfo(addr),
443
+ fetchActiveEra(),
444
+ getConstants(),
445
+ ]);
446
+ const activeEraIndex = Number(activeEra.value?.index || 0);
447
+ const activeEraStart = Number(activeEra.value?.start || 0);
448
+ const blockTime = new BigNumber(consts?.babe?.expectedBlockTime || 6000); // 6000 ms
449
+
450
+ const epochDuration = new BigNumber(consts?.babe?.epochDuration || 2400); // 2400 blocks
451
+
452
+ const sessionsPerEra = new BigNumber(consts?.staking?.sessionsPerEra || 6); // 6 sessions
453
+
454
+ const eraLength = sessionsPerEra
455
+ .multipliedBy(epochDuration)
456
+ .multipliedBy(blockTime)
457
+ .toNumber();
458
+ const unlockings = stakingInfo?.staking.unlocking
459
+ ? stakingInfo?.staking?.unlocking.map<PolkadotUnlocking>((lock) => ({
460
+ amount: new BigNumber(lock.value),
461
+ completionDate: new Date(
462
+ activeEraStart + (Number(lock.era) - activeEraIndex) * eraLength
463
+ ), // This is an estimation of the date of completion, since it depends on block validation speed
464
+ }))
465
+ : [];
466
+ const now = new Date();
467
+ const unlocked = unlockings.filter((lock) => lock.completionDate <= now);
468
+ const unlockingBalance = unlockings.reduce(
469
+ (sum, lock) => sum.plus(lock.amount),
470
+ new BigNumber(0)
471
+ );
472
+ const unlockedBalance = unlocked.reduce(
473
+ (sum, lock) => sum.plus(lock.amount),
474
+ new BigNumber(0)
475
+ );
476
+ const numSlashingSpans = Number(stakingInfo?.numSlashingSpans || 0);
477
+ return {
478
+ controller: controller || null,
479
+ stash: stash || null,
480
+ unlockedBalance,
481
+ unlockingBalance,
482
+ unlockings,
483
+ numSlashingSpans,
484
+ };
485
+ };
486
+
487
+ /**
488
+ * Returns nominations for an account including validator address, status and associated stake.
489
+ *
490
+ * @async
491
+ * @param {*} addr
492
+ *
493
+ * @returns {PolkadotNomination[}
494
+ */
495
+ export const getNominations = async (
496
+ addr: string
497
+ ): Promise<PolkadotNomination[]> => {
498
+ const nominations = await fetchNominations(addr);
499
+ if (!nominations) return [];
500
+ return nominations.targets.map<PolkadotNomination>((nomination) => ({
501
+ address: nomination.address,
502
+ value: new BigNumber(nomination.value || 0),
503
+ status: nomination.status,
504
+ }));
505
+ };
506
+
507
+ /**
508
+ * Returns all the params from the chain to build an extrinsic (a transaction on Substrate)
509
+ *
510
+ * @async
511
+ */
512
+ export const getTransactionParams = async () => {
513
+ const material = await fetchTransactionMaterial();
514
+ return {
515
+ blockHash: material.at.hash,
516
+ blockNumber: material.at.height,
517
+ chainName: material.chainName,
518
+ genesisHash: material.genesisHash,
519
+ specName: material.specName,
520
+ transactionVersion: material.txVersion,
521
+ specVersion: material.specVersion,
522
+ };
523
+ };
524
+
525
+ /**
526
+ * Broadcast the transaction to the substrate node
527
+ *
528
+ * @async
529
+ * @param {string} extrinsic - the encoded extrinsic to send
530
+ *
531
+ * @returns {string>} - the broadcasted transaction's hah
532
+ */
533
+ export const submitExtrinsic = async (extrinsic: string): Promise<string> => {
534
+ const {
535
+ data,
536
+ }: {
537
+ data: SidecarTransactionBroadcast;
538
+ } = await network({
539
+ method: "POST",
540
+ url: getSidecarUrl("/transaction"),
541
+ data: {
542
+ tx: extrinsic,
543
+ },
544
+ });
545
+ return data.hash;
546
+ };
547
+
548
+ /**
549
+ * Retrieve the transaction fees and weights
550
+ * Note: fees on Substrate are not set by the signer, but directly by the blockchain runtime.
551
+ *
552
+ * @async
553
+ * @param {string} extrinsic - the encoded extrinsic to send with a fake signing
554
+ *
555
+ * @returns {SidecarPaymentInfo}
556
+ */
557
+ export const paymentInfo = async (
558
+ extrinsic: string
559
+ ): Promise<SidecarPaymentInfo> => {
560
+ const {
561
+ data,
562
+ }: {
563
+ data: SidecarPaymentInfo;
564
+ } = await network({
565
+ method: "POST",
566
+ url: getSidecarUrl("/transaction/fee-estimate"),
567
+ data: {
568
+ tx: extrinsic,
569
+ },
570
+ });
571
+ return data;
572
+ };
573
+
574
+ /**
575
+ * List all validators for the current era, and their exposure, and identity.
576
+ *
577
+ * @async
578
+ * @param {string | string[]} stashes - stashes or validator status
579
+ *
580
+ * @returns {PolkadotValidator[]}
581
+ */
582
+ export const getValidators = async (
583
+ stashes:
584
+ | SidecarValidatorsParamStatus
585
+ | SidecarValidatorsParamAddresses = "elected"
586
+ ): Promise<PolkadotValidator[]> => {
587
+ let validators;
588
+
589
+ if (Array.isArray(stashes)) {
590
+ validators = await fetchValidators("all", stashes);
591
+ } else {
592
+ validators = await fetchValidators(stashes);
593
+ }
594
+
595
+ return validators.map((v) => ({
596
+ address: v.accountId,
597
+ identity: v.identity
598
+ ? [v.identity.displayParent, v.identity.display]
599
+ .filter(Boolean)
600
+ .join(" - ")
601
+ .trim()
602
+ : "",
603
+ nominatorsCount: Number(v.nominatorsCount),
604
+ rewardPoints: v.rewardsPoints ? new BigNumber(v.rewardsPoints) : null,
605
+ commission: new BigNumber(v.commission).dividedBy(
606
+ VALIDATOR_COMISSION_RATIO
607
+ ),
608
+ totalBonded: new BigNumber(v.total),
609
+ selfBonded: new BigNumber(v.own),
610
+ isElected: v.isElected,
611
+ isOversubscribed: v.isOversubscribed,
612
+ }));
613
+ };
614
+
615
+ /**
616
+ * Get Active Era progress
617
+ *
618
+ * @async
619
+ *
620
+ * @returns {PolkadotStakingProgress}
621
+ */
622
+ export const getStakingProgress =
623
+ async (): Promise<PolkadotStakingProgress> => {
624
+ const [progress, consts] = await Promise.all([
625
+ fetchStakingProgress(),
626
+ getConstants(),
627
+ ]);
628
+ const activeEra = Number(progress.activeEra);
629
+ const currentBlock = Number(progress.at.height);
630
+ const toggleEstimate = Number(progress.electionStatus?.toggleEstimate);
631
+ const electionClosed = !progress.electionStatus?.status?.Open;
632
+ // Consider election open if in the THERSHOLD blocks before the real expected change
633
+ // It disables staking flows that are subject to fail or block because of election status
634
+ // update while user is signing
635
+ const optimisticElectionClosed =
636
+ electionClosed &&
637
+ activeEra &&
638
+ currentBlock &&
639
+ toggleEstimate &&
640
+ currentBlock >= toggleEstimate - ELECTION_STATUS_OPTIMISTIC_THRESHOLD
641
+ ? false
642
+ : electionClosed;
643
+ return {
644
+ activeEra,
645
+ electionClosed: optimisticElectionClosed,
646
+ maxNominatorRewardedPerValidator:
647
+ Number(consts.staking.maxNominatorRewardedPerValidator) || 128,
648
+ bondingDuration: Number(consts.staking.bondingDuration) || 28,
649
+ };
650
+ };
651
+
652
+ /**
653
+ * Create a new Registry for creating Polkadot.JS types (or any Substrate)
654
+ *
655
+ * @async
656
+ *
657
+ * @returns {Object} - { registry, extrinsics }
658
+ */
659
+ export const getRegistry = async (): Promise<{
660
+ registry: TypeRegistry;
661
+ extrinsics: Extrinsics;
662
+ }> => {
663
+ const [material, spec] = await Promise.all([
664
+ getTransactionMaterialWithMetadata(),
665
+ fetchChainSpec(),
666
+ ]);
667
+ const registry: any = new TypeRegistry();
668
+ const metadata = new Metadata(registry, material.metadata);
669
+ // Register types specific to chain/runtimeVersion
670
+ registry.register(
671
+ getSpecTypes(
672
+ registry,
673
+ material.chainName,
674
+ material.specName,
675
+ Number(material.specVersion)
676
+ ) as any
677
+ );
678
+ // Register the chain properties for this registry
679
+ registry.setChainProperties(
680
+ registry.createType("ChainProperties", {
681
+ ss58Format: Number(spec.properties.ss58Format),
682
+ tokenDecimals: Number(spec.properties.tokenDecimals),
683
+ tokenSymbol: spec.properties.tokenSymbol,
684
+ })
685
+ );
686
+ registry.setMetadata(metadata);
687
+ const extrinsics = expandMetadata(registry, metadata).tx;
688
+ return {
689
+ registry,
690
+ extrinsics,
691
+ };
692
+ };
693
+
694
+ /*
695
+ * CACHED REQUESTS
696
+ * NOTE: we don't use the cache from family's `cache.js` to avoid cyclic imports.
697
+ */
698
+
699
+ /**
700
+ * Cache the fetchConstants to avoid too many calls
701
+ *
702
+ * @async
703
+ *
704
+ * @returns {Promise<Object>} consts
705
+ */
706
+ export const getConstants: CacheRes<
707
+ Array<void>,
708
+ Record<string, any>
709
+ > = makeLRUCache(
710
+ async (): Promise<Record<string, any>> => fetchConstants(),
711
+ () => "polkadot",
712
+ {
713
+ max: 1, // Store only one constants object since we only have polkadot.
714
+ ttl: 60 * 60 * 1000, // 1 hour
715
+ }
716
+ );
717
+
718
+ /**
719
+ * Cache the fetchTransactionMaterial(true) to avoid too many calls
720
+ *
721
+ * @async
722
+ *
723
+ * @returns {Promise<Object>} consts
724
+ */
725
+ export const getTransactionMaterialWithMetadata: CacheRes<
726
+ Array<void>,
727
+ SidecarTransactionMaterial
728
+ > = makeLRUCache(
729
+ async (): Promise<SidecarTransactionMaterial> =>
730
+ fetchTransactionMaterial(true),
731
+ () => "polkadot",
732
+ {
733
+ ttl: 60 * 60 * 1000, // 1 hour - could be Infinity
734
+ }
735
+ );