@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,535 @@
1
+ /*
2
+ THIS FILE IS UNUSED AND PROVIDED AS EXAMPLE FOR USING POLKADOT'S WEBSOCKET API DIRECTLY
3
+
4
+ POLKADOT.JS API VERSION 2.5.1
5
+ */
6
+
7
+ /* eslint-disable */
8
+
9
+ /* istanbul ignore file */
10
+ import uniq from "lodash/uniq";
11
+ import compact from "lodash/compact";
12
+ import { BigNumber } from "bignumber.js";
13
+ import { getEnv } from "@ledgerhq/coin-framework/env";
14
+ //@ts-expect-error
15
+ import { WsProvider, ApiPromise } from "@polkadot/api";
16
+ import { u8aToString } from "@polkadot/util";
17
+ import { AccountId, Registration } from "@polkadot/types/interfaces";
18
+ import { Data, Option } from "@polkadot/types";
19
+ import type { ITuple } from "@polkadot/types/types";
20
+ import type { PolkadotValidator, PolkadotStakingProgress } from "../types";
21
+ type AsyncApiFunction = (api: typeof ApiPromise) => Promise<any>;
22
+ const VALIDATOR_COMISSION_RATIO = 1000000000;
23
+
24
+ const getWsUrl = () => getEnv("API_POLKADOT_NODE");
25
+
26
+ const WEBSOCKET_DEBOUNCE_DELAY = 30000;
27
+ let api;
28
+ let pendingQueries: Array<Promise<any>> = [];
29
+ let apiDisconnectTimeout;
30
+
31
+ /**
32
+ * Connects to Substrate Node, executes calls then disconnects
33
+ *
34
+ * @param {*} execute - the calls to execute on api
35
+ */
36
+ async function withApi(execute: AsyncApiFunction): Promise<any> {
37
+ // If client is instanciated already, ensure it is connected & ready
38
+ if (api) {
39
+ try {
40
+ await api.isReadyOrError;
41
+ } catch (err) {
42
+ // definitely not connected...
43
+ api = null;
44
+ pendingQueries = [];
45
+ }
46
+ }
47
+
48
+ if (!api) {
49
+ const wsProvider = new WsProvider(
50
+ getWsUrl(),
51
+ /* autoConnectMs = */
52
+ false
53
+ );
54
+ wsProvider.connect();
55
+ api = await new ApiPromise({
56
+ provider: wsProvider,
57
+ }).isReadyOrError;
58
+ }
59
+
60
+ cancelDebouncedDisconnect();
61
+
62
+ try {
63
+ const query = execute(api);
64
+ pendingQueries.push(query.catch((err) => err));
65
+ const res = await query;
66
+ return res;
67
+ } finally {
68
+ debouncedDisconnect();
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Disconnects Websocket API client after all pending queries are flushed.
74
+ */
75
+ export const disconnect = async () => {
76
+ cancelDebouncedDisconnect();
77
+
78
+ if (api) {
79
+ const disconnecting = api;
80
+ const pending = pendingQueries;
81
+ api = undefined;
82
+ pendingQueries = [];
83
+ await Promise.all(pending);
84
+ await disconnecting.disconnect();
85
+ }
86
+ };
87
+
88
+ const cancelDebouncedDisconnect = () => {
89
+ if (apiDisconnectTimeout) {
90
+ clearTimeout(apiDisconnectTimeout);
91
+ apiDisconnectTimeout = null;
92
+ }
93
+ };
94
+
95
+ /**
96
+ * Disconnects Websocket client after a delay.
97
+ */
98
+ const debouncedDisconnect = () => {
99
+ cancelDebouncedDisconnect();
100
+ apiDisconnectTimeout = setTimeout(disconnect, WEBSOCKET_DEBOUNCE_DELAY);
101
+ };
102
+
103
+ /**
104
+ * Returns true if ElectionStatus is Close. If ElectionStatus is Open, some features must be disabled.
105
+ */
106
+ export const isElectionClosed = async (): Promise<Boolean> =>
107
+ withApi(async (api: typeof ApiPromise) => {
108
+ const status = await api.query.staking.eraElectionStatus();
109
+ const res = status.isClose;
110
+ return !!res;
111
+ });
112
+
113
+ /**
114
+ * Returns true if the address is a new account with no balance
115
+ *
116
+ * @param {*} addr
117
+ */
118
+ export const isNewAccount = async (address: string): Promise<Boolean> =>
119
+ withApi(async (api: typeof ApiPromise) => {
120
+ const {
121
+ nonce,
122
+ data: { free },
123
+ } = await api.query.system.account(address);
124
+ return (
125
+ new BigNumber(0).isEqualTo(nonce) && new BigNumber(0).isEqualTo(free)
126
+ );
127
+ });
128
+
129
+ /**
130
+ * Returns true if the address is a new account with no balance
131
+ *
132
+ * @param {*} addr
133
+ */
134
+ export const isControllerAddress = async (address: string): Promise<Boolean> =>
135
+ withApi(async (api: typeof ApiPromise) => {
136
+ const ledgetOpt = await api.query.staking.ledger(address);
137
+ return ledgetOpt.isSome;
138
+ });
139
+
140
+ /**
141
+ * Get all validators addresses to check for validity.
142
+ */
143
+ const getValidatorsStashesAddresses = async (): Promise<string[]> =>
144
+ withApi(async (api: typeof ApiPromise) => {
145
+ const list = await api.derive.staking.stashes();
146
+ return list.map((v) => v.toString());
147
+ });
148
+
149
+ /**
150
+ * Returns all addresses that are not validators
151
+ */
152
+ export const verifyValidatorAddresses = async (
153
+ validators: string[]
154
+ ): Promise<string[]> => {
155
+ const allValidators = await getValidatorsStashesAddresses();
156
+ return validators.filter((v) => !allValidators.includes(v));
157
+ };
158
+
159
+ /**
160
+ * Get all account-related data
161
+ *
162
+ * @param {*} addr
163
+ */
164
+ export const getAccount = async (addr: string) =>
165
+ withApi(async () => {
166
+ const balances = await getBalances(addr);
167
+ const stakingInfo = await getStakingInfo(addr);
168
+ const nominations = await getNominations(addr);
169
+ return { ...balances, ...stakingInfo, nominations };
170
+ });
171
+
172
+ /**
173
+ * Returns all the balances for an account
174
+ *
175
+ * @param {*} addr - the account address
176
+ */
177
+ export const getBalances = async (addr: string) =>
178
+ withApi(async (api: typeof ApiPromise) => {
179
+ const [finalizedHash, balances] = await Promise.all([
180
+ api.rpc.chain.getFinalizedHead(),
181
+ api.derive.balances.all(addr),
182
+ ]);
183
+ const { number } = await api.rpc.chain.getHeader(finalizedHash);
184
+ return {
185
+ blockHeight: number.toNumber(),
186
+ balance: new BigNumber(balances.freeBalance),
187
+ spendableBalance: new BigNumber(balances.availableBalance),
188
+ nonce: balances.accountNonce.toNumber(),
189
+ lockedBalance: new BigNumber(balances.lockedBalance),
190
+ };
191
+ });
192
+
193
+ /**
194
+ * Returns all staking-related data for an account
195
+ *
196
+ * @param {*} addr
197
+ */
198
+ export const getStakingInfo = async (addr: string) =>
199
+ withApi(async (api: typeof ApiPromise) => {
200
+ const [controlledLedgerOpt, bonded] = await Promise.all([
201
+ api.query.staking.ledger(addr),
202
+ api.query.staking.bonded(addr),
203
+ ]);
204
+ // NOTE: controlledLedgerOpt is not the current stash ledger...
205
+ const stash = controlledLedgerOpt.isSome
206
+ ? controlledLedgerOpt.unwrap().stash.toString()
207
+ : null;
208
+ const controller = bonded.isSome ? bonded.unwrap().toString() : null;
209
+
210
+ // If account is not a stash, no need to fetch corresponding ledger
211
+ if (!controller) {
212
+ return {
213
+ controller: null,
214
+ stash: stash || null,
215
+ unlockedBalance: new BigNumber(0),
216
+ unlockingBalance: new BigNumber(0),
217
+ unlockings: [],
218
+ };
219
+ }
220
+
221
+ const [activeOpt, ledgerOpt] = await Promise.all([
222
+ api.query.staking.activeEra(),
223
+ api.query.staking.ledger(controller),
224
+ ]);
225
+ const now = new Date();
226
+ const { index, start } = activeOpt.unwrapOrDefault();
227
+ const activeEraIndex = index.toNumber();
228
+ const activeEraStart = start.unwrap().toNumber();
229
+ const blockTime = api.consts.babe.expectedBlockTime; // 6000 ms
230
+
231
+ const epochDuration = api.consts.babe.epochDuration; // 2400 blocks
232
+
233
+ const eraLength = api.consts.staking.sessionsPerEra // 6 sessions
234
+ .mul(epochDuration)
235
+ .mul(blockTime)
236
+ .toNumber();
237
+ const ledger = ledgerOpt.isSome ? ledgerOpt.unwrap() : null;
238
+ const unlockings = ledger
239
+ ? ledger.unlocking.map((lock) => ({
240
+ amount: new BigNumber(lock.value),
241
+ completionDate: new Date(
242
+ activeEraStart + (lock.era - activeEraIndex) * eraLength
243
+ ), // This is an estimation of the date of completion, since it depends on block validation speed
244
+ }))
245
+ : [];
246
+ const unlocked = unlockings.filter((lock) => lock.completionDate <= now);
247
+ const unlockingBalance = unlockings.reduce(
248
+ (sum, lock) => sum.plus(lock.amount),
249
+ new BigNumber(0)
250
+ );
251
+ const unlockedBalance = unlocked.reduce(
252
+ (sum, lock) => sum.plus(lock.amount),
253
+ new BigNumber(0)
254
+ );
255
+ return {
256
+ controller,
257
+ stash,
258
+ unlockedBalance,
259
+ unlockingBalance,
260
+ unlockings,
261
+ };
262
+ });
263
+
264
+ /**
265
+ * Returns nominations for an account including validator address, status and associated stake.
266
+ *
267
+ * @param {*} addr
268
+ */
269
+ export const getNominations = async (addr: string) =>
270
+ withApi(async (api: typeof ApiPromise) => {
271
+ const [{ activeEra }, nominationsOpt] = await Promise.all([
272
+ api.derive.session.indexes(),
273
+ api.query.staking.nominators(addr),
274
+ ]);
275
+ if (nominationsOpt.isNone) return [];
276
+ const targets = nominationsOpt.unwrap().targets;
277
+ const [exposures, stashes] = await Promise.all([
278
+ api.query.staking.erasStakers.multi(
279
+ targets.map((target) => [activeEra, target])
280
+ ),
281
+ api.derive.staking.stashes(),
282
+ ]);
283
+ const allStashes = stashes.map((stash) => stash.toString());
284
+ return targets.map((target, index) => {
285
+ const exposure = exposures[index];
286
+ const individualExposure = exposure.others.find(
287
+ (o) => o.who.toString() === addr
288
+ );
289
+ const value = individualExposure
290
+ ? new BigNumber(individualExposure.value)
291
+ : new BigNumber(0);
292
+ const status = exposure.others.length
293
+ ? individualExposure
294
+ ? "active"
295
+ : "inactive"
296
+ : allStashes.includes(target.toString())
297
+ ? "waiting"
298
+ : null;
299
+ return {
300
+ address: target.toString(),
301
+ value,
302
+ status,
303
+ };
304
+ });
305
+ });
306
+
307
+ /**
308
+ * Returns all the params from the chain to build an extrinsic (a transaction on Substrate)
309
+ */
310
+ export const getTransactionParams = async () =>
311
+ withApi(async (api: typeof ApiPromise) => {
312
+ const chainName = await api.rpc.system.chain();
313
+ const blockHash = await api.rpc.chain.getFinalizedHead();
314
+ const genesisHash = await api.rpc.chain.getBlockHash(0);
315
+ const { number } = await api.rpc.chain.getHeader(blockHash);
316
+ const { specName, specVersion, transactionVersion } =
317
+ await api.rpc.state.getRuntimeVersion(blockHash);
318
+ return {
319
+ blockHash,
320
+ blockNumber: number,
321
+ genesisHash,
322
+ chainName: chainName.toString(),
323
+ specName: specName.toString(),
324
+ specVersion,
325
+ transactionVersion,
326
+ };
327
+ });
328
+
329
+ /**
330
+ * Broadcast the transaction to the substrate node
331
+ *
332
+ * @param {string} extrinsic - the encoded extrinsic to send
333
+ */
334
+ export const submitExtrinsic = async (extrinsic: string) =>
335
+ withApi(async (api: typeof ApiPromise) => {
336
+ const tx = api.tx(extrinsic);
337
+ const hash = await api.rpc.author.submitExtrinsic(tx);
338
+ return hash;
339
+ });
340
+
341
+ /**
342
+ * Retrieve the transaction fees and weights
343
+ * Note: fees on Substrate are not set by the signer, but directly by the blockchain runtime.
344
+ *
345
+ * @param {string} extrinsic - the encoded extrinsic to send with a fake signing
346
+ */
347
+ export const paymentInfo = async (extrinsic: string) =>
348
+ withApi(async (api: typeof ApiPromise) => {
349
+ const info = await api.rpc.payment.queryInfo(extrinsic);
350
+ return info;
351
+ });
352
+
353
+ /**
354
+ * Fetch all reward points for validators for current era
355
+ *
356
+ * @returns Map<String, BigNumber>
357
+ */
358
+ export const fetchRewardPoints = async () =>
359
+ withApi(async (api: typeof ApiPromise) => {
360
+ const activeOpt = await api.query.staking.activeEra();
361
+ const { index: activeEra } = activeOpt.unwrapOrDefault();
362
+ const { individual } = await api.query.staking.erasRewardPoints(activeEra);
363
+ // recast BTreeMap<AccountId,RewardPoint> to Map<String, RewardPoint> because strict equality does not work
364
+ const rewards = new Map<String, BigNumber>(
365
+ [...individual.entries()].map(([k, v]) => [
366
+ k.toString(),
367
+ new BigNumber(v.toString()),
368
+ ])
369
+ );
370
+ return rewards;
371
+ });
372
+
373
+ /**
374
+ * @source https://github.com/polkadot-js/api/blob/master/packages/api-derive/src/accounts/info.ts
375
+ */
376
+ function dataAsString(data: Data): string {
377
+ return data.isRaw
378
+ ? u8aToString(data.asRaw.toU8a(true)).trim()
379
+ : data.isNone
380
+ ? ""
381
+ : data.toHex();
382
+ }
383
+
384
+ /**
385
+ * Fetch identity name of multiple addresses.
386
+ * Get parent identity if any, and concatenate parent name with child name.
387
+ *
388
+ * @param {string[]} addresses
389
+ */
390
+ export const fetchIdentities = async (addresses: string[]) =>
391
+ withApi(async (api: typeof ApiPromise) => {
392
+ const superOfOpts = await api.query.identity.superOf.multi<
393
+ Option<ITuple<[AccountId, Data]>>
394
+ >(addresses);
395
+ const withParent = superOfOpts.map((superOfOpt) =>
396
+ superOfOpt?.isSome ? superOfOpt?.unwrap() : undefined
397
+ );
398
+ const parentAddresses = uniq(
399
+ compact(withParent.map((superOf) => superOf && superOf[0].toString()))
400
+ );
401
+ const [identities, parentIdentities] = await Promise.all([
402
+ api.query.identity.identityOf.multi<Option<Registration>>(addresses),
403
+ api.query.identity.identityOf.multi<Option<Registration>>(
404
+ parentAddresses
405
+ ),
406
+ ]);
407
+ const map = new Map<string, string>(
408
+ addresses.map((addr, index) => {
409
+ const indexOfParent = withParent[index]
410
+ ? parentAddresses.indexOf(withParent[index][0].toString())
411
+ : -1;
412
+ const identityOpt =
413
+ indexOfParent > -1
414
+ ? parentIdentities[indexOfParent]
415
+ : identities[index];
416
+
417
+ if (identityOpt.isNone) {
418
+ return [addr, ""];
419
+ }
420
+
421
+ const {
422
+ info: { display },
423
+ } = identityOpt.unwrap();
424
+ const name = withParent[index]
425
+ ? `${dataAsString(display)} / ${dataAsString(withParent[index][1])}`
426
+ : dataAsString(display);
427
+ return [addr, name];
428
+ })
429
+ );
430
+ return map;
431
+ });
432
+
433
+ /**
434
+ * Transforms each validator into an internal Validator type.
435
+ * @param {*} rewards - map of addres sand corresponding reward
436
+ * @param {*} identities - map of address and corresponding identity
437
+ * @param {*} elected - list of elected validator addresses
438
+ * @param {*} maxNominators - constant for oversubscribed validators
439
+ * @param {*} validator - the validator details to transform.
440
+ */
441
+ const mapValidator = (
442
+ rewards,
443
+ identities,
444
+ elected,
445
+ maxNominators,
446
+ validator
447
+ ): PolkadotValidator => {
448
+ const address = validator.accountId.toString();
449
+ return {
450
+ address: address,
451
+ identity: identities.get(address) || "",
452
+ nominatorsCount: validator.exposure.others.length,
453
+ rewardPoints: rewards.get(address) || null,
454
+ commission: new BigNumber(validator.validatorPrefs.commission).dividedBy(
455
+ VALIDATOR_COMISSION_RATIO
456
+ ),
457
+ totalBonded: new BigNumber(validator.exposure.total),
458
+ selfBonded: new BigNumber(validator.exposure.own),
459
+ isElected: elected.includes(address),
460
+ isOversubscribed: validator.exposure.others.length >= maxNominators,
461
+ };
462
+ };
463
+
464
+ /**
465
+ * List all validators for the current era, and their exposure, and identity.
466
+ */
467
+ export const getValidators = async (
468
+ stashes: string | string[] = "elected"
469
+ ): Promise<PolkadotValidator> =>
470
+ withApi(async (api: typeof ApiPromise) => {
471
+ const [allStashes, elected] = await Promise.all([
472
+ api.derive.staking.stashes(),
473
+ api.query.session.validators(),
474
+ ]);
475
+ let stashIds;
476
+ const allIds = allStashes.map((s) => s.toString());
477
+ const electedIds = elected.map((s) => s.toString());
478
+
479
+ if (Array.isArray(stashes)) {
480
+ stashIds = allIds.filter((s) => stashes.includes(s));
481
+ } else if (stashes === "elected") {
482
+ stashIds = electedIds;
483
+ } else {
484
+ const waitingIds = allIds.filter((v) => !electedIds.includes(v));
485
+
486
+ if (stashes === "waiting") {
487
+ stashIds = waitingIds;
488
+ } else {
489
+ // Keep elected in first positions
490
+ stashIds = [...electedIds, ...waitingIds];
491
+ }
492
+ }
493
+
494
+ const [validators, rewards, identities] = await Promise.all([
495
+ api.derive.staking.accounts(stashIds),
496
+ fetchRewardPoints(),
497
+ fetchIdentities(stashIds),
498
+ ]);
499
+ return validators.map(
500
+ mapValidator.bind(
501
+ null,
502
+ rewards,
503
+ identities,
504
+ electedIds,
505
+ api.consts.staking.maxNominatorRewardedPerValidator
506
+ )
507
+ );
508
+ });
509
+
510
+ /**
511
+ * Get Active Era progress
512
+ */
513
+ export const getStakingProgress = async (): Promise<PolkadotStakingProgress> =>
514
+ withApi(async (api: typeof ApiPromise) => {
515
+ const [activeEraOpt, status] = await Promise.all([
516
+ api.query.staking.activeEra(),
517
+ api.query.staking.eraElectionStatus(),
518
+ ]);
519
+ const { index: activeEra } = activeEraOpt.unwrapOrDefault();
520
+ return {
521
+ activeEra: activeEra.toNumber(),
522
+ electionClosed: !!status.isClose,
523
+ };
524
+ });
525
+
526
+ /**
527
+ * Return TypeRegistry and decorated extrinsics from client
528
+ */
529
+ export const getRegistry = async () =>
530
+ withApi(async (api: typeof ApiPromise) => {
531
+ return {
532
+ registry: api.registry,
533
+ extrinsics: api.tx,
534
+ };
535
+ });
@@ -0,0 +1,81 @@
1
+ import type {
2
+ AccountBridge,
3
+ CurrencyBridge,
4
+ Operation,
5
+ SignedOperation,
6
+ } from "@ledgerhq/types-live";
7
+ import type { Transaction } from "../types";
8
+ import {
9
+ DeviceCommunication,
10
+ makeAccountBridgeReceive,
11
+ makeScanAccounts,
12
+ makeSync,
13
+ } from "@ledgerhq/coin-framework/bridge/jsHelpers";
14
+ import { patchOperationWithHash } from "@ledgerhq/coin-framework/operation";
15
+ import { submitExtrinsic } from "../api";
16
+ import { getPreloadStrategy, preload, hydrate } from "../preload";
17
+ import { getAccountShape } from "../js-synchronisation";
18
+ import createTransaction from "../js-createTransaction";
19
+ import prepareTransaction from "../js-prepareTransaction";
20
+ import getTransactionStatus from "../js-getTransactionStatus";
21
+ import estimateMaxSpendable from "../js-estimateMaxSpendable";
22
+ import buildSignOperation from "../js-signOperation";
23
+ import getAddress from "../hw-getAddress";
24
+ import { loadPolkadotCrypto } from "../polkadot-crypto";
25
+ import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
26
+
27
+ const updateTransaction = (t, patch) => ({ ...t, ...patch });
28
+
29
+ /**
30
+ * Broadcast the signed transaction
31
+ * @param {signature: string, operation: string} signedOperation
32
+ */
33
+ const broadcast = async ({
34
+ signedOperation: { signature, operation },
35
+ }: {
36
+ signedOperation: SignedOperation;
37
+ }): Promise<Operation> => {
38
+ await loadPolkadotCrypto();
39
+ const hash = await submitExtrinsic(signature);
40
+ return patchOperationWithHash(operation, hash);
41
+ };
42
+
43
+ export function buildCurrencyBridge(
44
+ deviceCommunication: DeviceCommunication
45
+ ): CurrencyBridge {
46
+ const scanAccounts = makeScanAccounts({
47
+ getAccountShape,
48
+ deviceCommunication,
49
+ getAddressFn: getAddress,
50
+ });
51
+
52
+ return {
53
+ getPreloadStrategy,
54
+ preload,
55
+ hydrate,
56
+ scanAccounts,
57
+ };
58
+ }
59
+
60
+ export function buildAccountBridge(
61
+ deviceCommunication: DeviceCommunication
62
+ ): AccountBridge<Transaction> {
63
+ const receive = makeAccountBridgeReceive(
64
+ getAddressWrapper(getAddress),
65
+ deviceCommunication
66
+ );
67
+ const signOperation = buildSignOperation(deviceCommunication);
68
+ const sync = makeSync({ getAccountShape });
69
+
70
+ return {
71
+ estimateMaxSpendable,
72
+ createTransaction,
73
+ updateTransaction,
74
+ getTransactionStatus,
75
+ prepareTransaction,
76
+ sync,
77
+ receive,
78
+ signOperation,
79
+ broadcast,
80
+ };
81
+ }