@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.
- package/.eslintrc.js +57 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +12 -0
- package/jest.config.js +10 -0
- package/lib/account.d.ts +15 -0
- package/lib/account.d.ts.map +1 -0
- package/lib/account.js +133 -0
- package/lib/account.js.map +1 -0
- package/lib/address.d.ts +7 -0
- package/lib/address.d.ts.map +1 -0
- package/lib/address.js +28 -0
- package/lib/address.js.map +1 -0
- package/lib/api/bisontrails.d.ts +11 -0
- package/lib/api/bisontrails.d.ts.map +1 -0
- package/lib/api/bisontrails.js +351 -0
- package/lib/api/bisontrails.js.map +1 -0
- package/lib/api/common.d.ts +12 -0
- package/lib/api/common.d.ts.map +1 -0
- package/lib/api/common.js +41 -0
- package/lib/api/common.js.map +1 -0
- package/lib/api/index.d.ts +3 -0
- package/lib/api/index.d.ts.map +1 -0
- package/lib/api/index.js +53 -0
- package/lib/api/index.js.map +1 -0
- package/lib/api/sidecar.d.ts +193 -0
- package/lib/api/sidecar.d.ts.map +1 -0
- package/lib/api/sidecar.js +844 -0
- package/lib/api/sidecar.js.map +1 -0
- package/lib/api/sidecar.types.d.ts +163 -0
- package/lib/api/sidecar.types.d.ts.map +1 -0
- package/lib/api/sidecar.types.js +3 -0
- package/lib/api/sidecar.types.js.map +1 -0
- package/lib/api/subscan.d.ts +6 -0
- package/lib/api/subscan.d.ts.map +1 -0
- package/lib/api/subscan.js +375 -0
- package/lib/api/subscan.js.map +1 -0
- package/lib/api/websocket.d.ts +92 -0
- package/lib/api/websocket.d.ts.map +1 -0
- package/lib/api/websocket.js +759 -0
- package/lib/api/websocket.js.map +1 -0
- package/lib/bridge/js.d.ts +6 -0
- package/lib/bridge/js.d.ts.map +1 -0
- package/lib/bridge/js.js +120 -0
- package/lib/bridge/js.js.map +1 -0
- package/lib/bridge.integration.test.d.ts +4 -0
- package/lib/bridge.integration.test.d.ts.map +1 -0
- package/lib/bridge.integration.test.js +795 -0
- package/lib/bridge.integration.test.js.map +1 -0
- package/lib/cache.d.ts +65 -0
- package/lib/cache.d.ts.map +1 -0
- package/lib/cache.js +182 -0
- package/lib/cache.js.map +1 -0
- package/lib/cli-transaction.d.ts +44 -0
- package/lib/cli-transaction.d.ts.map +1 -0
- package/lib/cli-transaction.js +148 -0
- package/lib/cli-transaction.js.map +1 -0
- package/lib/deviceTransactionConfig.d.ts +16 -0
- package/lib/deviceTransactionConfig.d.ts.map +1 -0
- package/lib/deviceTransactionConfig.js +171 -0
- package/lib/deviceTransactionConfig.js.map +1 -0
- package/lib/errors.d.ts +37 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +17 -0
- package/lib/errors.js.map +1 -0
- package/lib/hw-getAddress.d.ts +4 -0
- package/lib/hw-getAddress.d.ts.map +1 -0
- package/lib/hw-getAddress.js +64 -0
- package/lib/hw-getAddress.js.map +1 -0
- package/lib/js-buildTransaction.d.ts +25 -0
- package/lib/js-buildTransaction.d.ts.map +1 -0
- package/lib/js-buildTransaction.js +265 -0
- package/lib/js-buildTransaction.js.map +1 -0
- package/lib/js-createTransaction.d.ts +9 -0
- package/lib/js-createTransaction.d.ts.map +1 -0
- package/lib/js-createTransaction.js +22 -0
- package/lib/js-createTransaction.js.map +1 -0
- package/lib/js-estimateMaxSpendable.d.ts +15 -0
- package/lib/js-estimateMaxSpendable.d.ts.map +1 -0
- package/lib/js-estimateMaxSpendable.js +89 -0
- package/lib/js-estimateMaxSpendable.js.map +1 -0
- package/lib/js-getFeesForTransaction.d.ts +14 -0
- package/lib/js-getFeesForTransaction.d.ts.map +1 -0
- package/lib/js-getFeesForTransaction.js +94 -0
- package/lib/js-getFeesForTransaction.js.map +1 -0
- package/lib/js-getTransactionStatus.d.ts +18 -0
- package/lib/js-getTransactionStatus.d.ts.map +1 -0
- package/lib/js-getTransactionStatus.js +316 -0
- package/lib/js-getTransactionStatus.js.map +1 -0
- package/lib/js-prepareTransaction.d.ts +9 -0
- package/lib/js-prepareTransaction.d.ts.map +1 -0
- package/lib/js-prepareTransaction.js +83 -0
- package/lib/js-prepareTransaction.js.map +1 -0
- package/lib/js-signOperation.d.ts +27 -0
- package/lib/js-signOperation.d.ts.map +1 -0
- package/lib/js-signOperation.js +235 -0
- package/lib/js-signOperation.js.map +1 -0
- package/lib/js-synchronisation.d.ts +3 -0
- package/lib/js-synchronisation.d.ts.map +1 -0
- package/lib/js-synchronisation.js +104 -0
- package/lib/js-synchronisation.js.map +1 -0
- package/lib/logic.d.ts +107 -0
- package/lib/logic.d.ts.map +1 -0
- package/lib/logic.js +242 -0
- package/lib/logic.js.map +1 -0
- package/lib/platformAdapter.d.ts +11 -0
- package/lib/platformAdapter.d.ts.map +1 -0
- package/lib/platformAdapter.js +25 -0
- package/lib/platformAdapter.js.map +1 -0
- package/lib/platformAdapter.test.d.ts +2 -0
- package/lib/platformAdapter.test.d.ts.map +1 -0
- package/lib/platformAdapter.test.js +51 -0
- package/lib/platformAdapter.test.js.map +1 -0
- package/lib/polkadot-crypto.d.ts +2 -0
- package/lib/polkadot-crypto.d.ts.map +1 -0
- package/lib/polkadot-crypto.js +14 -0
- package/lib/polkadot-crypto.js.map +1 -0
- package/lib/preload.d.ts +14 -0
- package/lib/preload.d.ts.map +1 -0
- package/lib/preload.js +173 -0
- package/lib/preload.js.map +1 -0
- package/lib/serialization.d.ts +4 -0
- package/lib/serialization.d.ts.map +1 -0
- package/lib/serialization.js +51 -0
- package/lib/serialization.js.map +1 -0
- package/lib/specs.d.ts +7 -0
- package/lib/specs.d.ts.map +1 -0
- package/lib/specs.js +274 -0
- package/lib/specs.js.map +1 -0
- package/lib/speculos-deviceActions.d.ts +4 -0
- package/lib/speculos-deviceActions.d.ts.map +1 -0
- package/lib/speculos-deviceActions.js +85 -0
- package/lib/speculos-deviceActions.js.map +1 -0
- package/lib/transaction.d.ts +15 -0
- package/lib/transaction.d.ts.map +1 -0
- package/lib/transaction.js +51 -0
- package/lib/transaction.js.map +1 -0
- package/lib/types.d.ts +96 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib/validators.d.ts +3 -0
- package/lib/validators.d.ts.map +1 -0
- package/lib/validators.js +7 -0
- package/lib/validators.js.map +1 -0
- package/package.json +95 -0
- package/src/account.ts +170 -0
- package/src/address.ts +28 -0
- package/src/api/bisontrails.ts +343 -0
- package/src/api/common.ts +52 -0
- package/src/api/index.ts +38 -0
- package/src/api/sidecar.ts +735 -0
- package/src/api/sidecar.types.ts +162 -0
- package/src/api/subscan.ts +362 -0
- package/src/api/websocket.ts +535 -0
- package/src/bridge/js.ts +81 -0
- package/src/bridge.integration.test.ts +764 -0
- package/src/cache.ts +202 -0
- package/src/cli-transaction.ts +184 -0
- package/src/deviceTransactionConfig.ts +209 -0
- package/src/errors.ts +39 -0
- package/src/hw-getAddress.ts +14 -0
- package/src/js-buildTransaction.ts +206 -0
- package/src/js-createTransaction.ts +22 -0
- package/src/js-estimateMaxSpendable.ts +37 -0
- package/src/js-getFeesForTransaction.ts +41 -0
- package/src/js-getTransactionStatus.ts +350 -0
- package/src/js-prepareTransaction.ts +27 -0
- package/src/js-signOperation.ts +232 -0
- package/src/js-synchronisation.ts +57 -0
- package/src/logic.ts +266 -0
- package/src/platformAdapter.test.ts +61 -0
- package/src/platformAdapter.ts +29 -0
- package/src/polkadot-crypto.ts +10 -0
- package/src/preload.ts +148 -0
- package/src/serialization.ts +48 -0
- package/src/specs.ts +305 -0
- package/src/speculos-deviceActions.ts +84 -0
- package/src/transaction.ts +64 -0
- package/src/types.ts +118 -0
- package/src/validators.ts +2 -0
- 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
|
+
);
|