@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
package/src/cache.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Extrinsics } from "@polkadot/types/metadata/decorate/types";
|
|
2
|
+
import { BigNumber } from "bignumber.js";
|
|
3
|
+
import { TypeRegistry } from "@polkadot/types";
|
|
4
|
+
import { makeLRUCache } from "@ledgerhq/coin-framework/cache";
|
|
5
|
+
import type { CacheRes } from "@ledgerhq/coin-framework/cache";
|
|
6
|
+
import type { PolkadotAccount, Transaction } from "./types";
|
|
7
|
+
import {
|
|
8
|
+
isNewAccount as apiIsNewAccount,
|
|
9
|
+
isControllerAddress as apiIsControllerAddress,
|
|
10
|
+
isElectionClosed as apiIsElectionClosed,
|
|
11
|
+
getRegistry as apiGetRegistry,
|
|
12
|
+
getTransactionParams as apiGetTransactionParams,
|
|
13
|
+
paymentInfo as apiPaymentInfo,
|
|
14
|
+
getMinimumBondBalance as apiGetMinimumBondBalance,
|
|
15
|
+
} from "./api";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create a hash for a transaction that is params-specific and stay unchanged if no influcing fees
|
|
19
|
+
*
|
|
20
|
+
* @param {*} a
|
|
21
|
+
* @param {*} t
|
|
22
|
+
*
|
|
23
|
+
* @returns {string} hash
|
|
24
|
+
*/
|
|
25
|
+
const hashTransactionParams = (
|
|
26
|
+
a: PolkadotAccount,
|
|
27
|
+
t: Transaction,
|
|
28
|
+
signedTx: string
|
|
29
|
+
) => {
|
|
30
|
+
// Nonce is added to discard previous estimation when account is synced.
|
|
31
|
+
const prefix = `${a.id}_${a.polkadotResources?.nonce || 0}_${t.mode}`;
|
|
32
|
+
// Fees depends on extrinsic bytesize
|
|
33
|
+
const byteSize = signedTx.length;
|
|
34
|
+
|
|
35
|
+
// And on extrinsic weight (which varies with the method called)
|
|
36
|
+
switch (t.mode) {
|
|
37
|
+
case "send":
|
|
38
|
+
return `${prefix}_${byteSize}`;
|
|
39
|
+
|
|
40
|
+
case "bond":
|
|
41
|
+
return t.rewardDestination
|
|
42
|
+
? `${prefix}_${byteSize}_${t.rewardDestination}`
|
|
43
|
+
: `${prefix}_${byteSize}`;
|
|
44
|
+
|
|
45
|
+
case "unbond":
|
|
46
|
+
case "rebond":
|
|
47
|
+
return `${prefix}_${byteSize}`;
|
|
48
|
+
|
|
49
|
+
case "nominate":
|
|
50
|
+
return `${prefix}_${t.validators?.length ?? "0"}`;
|
|
51
|
+
|
|
52
|
+
case "withdrawUnbonded":
|
|
53
|
+
return `${prefix}_${t.numSlashingSpans ?? "0"}`;
|
|
54
|
+
|
|
55
|
+
case "chill":
|
|
56
|
+
return `${prefix}`;
|
|
57
|
+
case "setController":
|
|
58
|
+
return `${prefix}`;
|
|
59
|
+
case "claimReward":
|
|
60
|
+
return `${prefix}_${t.era || "0"}`;
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
throw new Error("Unknown mode in transaction");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Cache the getTransactionInfo for fees estimation to avoid multiple calls during 5 minutes.
|
|
69
|
+
* Should be force refresh when signing operation.
|
|
70
|
+
*
|
|
71
|
+
* @param {Account} account
|
|
72
|
+
*
|
|
73
|
+
* @returns {Promise<Object>} txInfo
|
|
74
|
+
*/
|
|
75
|
+
export const getTransactionParams: CacheRes<
|
|
76
|
+
Array<void>,
|
|
77
|
+
Record<string, any>
|
|
78
|
+
> = makeLRUCache(
|
|
79
|
+
async (): Promise<Record<string, any>> => apiGetTransactionParams(),
|
|
80
|
+
() => "polkadot",
|
|
81
|
+
{
|
|
82
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cache the payment info (fee estimate), with a hash depending on fees-changing transaction params and tx size.
|
|
88
|
+
*
|
|
89
|
+
* @param {PolkadotAccount} arg1.a
|
|
90
|
+
* @param {Transaction} arg1.t
|
|
91
|
+
*
|
|
92
|
+
* @returns {Promise<BigBumber>}
|
|
93
|
+
*/
|
|
94
|
+
export const getPaymentInfo: CacheRes<
|
|
95
|
+
Array<{
|
|
96
|
+
a: PolkadotAccount;
|
|
97
|
+
t: Transaction;
|
|
98
|
+
signedTx: string;
|
|
99
|
+
}>,
|
|
100
|
+
{
|
|
101
|
+
partialFee: string;
|
|
102
|
+
}
|
|
103
|
+
> = makeLRUCache(
|
|
104
|
+
async ({
|
|
105
|
+
signedTx,
|
|
106
|
+
}): Promise<{
|
|
107
|
+
partialFee: string;
|
|
108
|
+
}> => {
|
|
109
|
+
return await apiPaymentInfo(signedTx);
|
|
110
|
+
},
|
|
111
|
+
({ a, t, signedTx }) => hashTransactionParams(a, t, signedTx),
|
|
112
|
+
{
|
|
113
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
export const getRegistry: CacheRes<
|
|
117
|
+
Array<void>,
|
|
118
|
+
{ registry: TypeRegistry; extrinsics: Extrinsics }
|
|
119
|
+
> = makeLRUCache(
|
|
120
|
+
async (): Promise<{
|
|
121
|
+
registry: TypeRegistry;
|
|
122
|
+
extrinsics: Extrinsics;
|
|
123
|
+
}> => {
|
|
124
|
+
return await apiGetRegistry();
|
|
125
|
+
},
|
|
126
|
+
() => "polkadot",
|
|
127
|
+
{
|
|
128
|
+
ttl: 60 * 60 * 1000, // 1 hour - could be Infinity
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Cache the isNewAccount to avoid multiple calls get checking tx status.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} address
|
|
136
|
+
*
|
|
137
|
+
* @returns {Promise<boolean>}
|
|
138
|
+
*/
|
|
139
|
+
export const isNewAccount: CacheRes<Array<string>, boolean> = makeLRUCache(
|
|
140
|
+
async (address): Promise<boolean> => {
|
|
141
|
+
return apiIsNewAccount(address);
|
|
142
|
+
},
|
|
143
|
+
(address) => address,
|
|
144
|
+
{
|
|
145
|
+
ttl: 60 * 1000, // 1 minute
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Cache the isControllerAddress to avoid multiple calls get checking tx status.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} address
|
|
153
|
+
*
|
|
154
|
+
* @returns {Promise<boolean>}
|
|
155
|
+
*/
|
|
156
|
+
export const isControllerAddress: CacheRes<
|
|
157
|
+
Array<string>,
|
|
158
|
+
boolean
|
|
159
|
+
> = makeLRUCache(
|
|
160
|
+
async (address): Promise<boolean> => {
|
|
161
|
+
return apiIsControllerAddress(address);
|
|
162
|
+
},
|
|
163
|
+
(address) => address,
|
|
164
|
+
{
|
|
165
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Cache the isElectionClosed to avoid multiple calls get checking tx status.
|
|
171
|
+
* Should be unused if currency is preloaded.
|
|
172
|
+
*
|
|
173
|
+
* @returns {Promise<boolean>}
|
|
174
|
+
*/
|
|
175
|
+
export const isElectionClosed: CacheRes<Array<void>, boolean> = makeLRUCache(
|
|
176
|
+
async (): Promise<boolean> => {
|
|
177
|
+
return apiIsElectionClosed();
|
|
178
|
+
},
|
|
179
|
+
() => "",
|
|
180
|
+
{
|
|
181
|
+
ttl: 60 * 1000, // 1 minute
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Cache the getMinimumBondBalance to avoid too many calls
|
|
187
|
+
*
|
|
188
|
+
* @async
|
|
189
|
+
*
|
|
190
|
+
* @returns {Promise<BigNumber>} consts
|
|
191
|
+
*/
|
|
192
|
+
export const getMinimumBondBalance: CacheRes<
|
|
193
|
+
Array<void>,
|
|
194
|
+
BigNumber
|
|
195
|
+
> = makeLRUCache(
|
|
196
|
+
async (): Promise<BigNumber> => apiGetMinimumBondBalance(),
|
|
197
|
+
() => "polkadot",
|
|
198
|
+
{
|
|
199
|
+
max: 1, // Store only one object since we only have polkadot.
|
|
200
|
+
ttl: 60 * 60 * 1000, // 1 hour
|
|
201
|
+
}
|
|
202
|
+
);
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { from, Observable } from "rxjs";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { getValidators } from "./validators";
|
|
4
|
+
import invariant from "invariant";
|
|
5
|
+
import flatMap from "lodash/flatMap";
|
|
6
|
+
import { isAccount } from "@ledgerhq/coin-framework/account/index";
|
|
7
|
+
import {
|
|
8
|
+
getCryptoCurrencyById,
|
|
9
|
+
formatCurrencyUnit,
|
|
10
|
+
} from "@ledgerhq/coin-framework/currencies/index";
|
|
11
|
+
import {
|
|
12
|
+
SidecarValidatorsParamAddresses,
|
|
13
|
+
SidecarValidatorsParamStatus,
|
|
14
|
+
} from "./api/sidecar.types";
|
|
15
|
+
import { AccountLike } from "@ledgerhq/types-live";
|
|
16
|
+
import { PolkadotAccount, Transaction } from "./types";
|
|
17
|
+
const options = [
|
|
18
|
+
{
|
|
19
|
+
name: "mode",
|
|
20
|
+
type: String,
|
|
21
|
+
desc: "mode of transaction: send, nominate, bond, claimReward, withdrawUnbonded",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "fees",
|
|
25
|
+
type: String,
|
|
26
|
+
desc: "how much fees",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "validator",
|
|
30
|
+
type: String,
|
|
31
|
+
multiple: true,
|
|
32
|
+
desc: "address of recipient validator that will receive the delegate",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "era",
|
|
36
|
+
type: String,
|
|
37
|
+
desc: "Era of when to claim rewards",
|
|
38
|
+
},
|
|
39
|
+
/** Different possible destionation :
|
|
40
|
+
Staked - Pay into the stash account, increasing the amount at stake accordingly.
|
|
41
|
+
Stash - Pay into the stash account, not increasing the amount at stake.
|
|
42
|
+
Account - Pay into a custom account, like so: Account DMTHrNcmA8QbqRS4rBq8LXn8ipyczFoNMb1X4cY2WD9tdBX.
|
|
43
|
+
Controller - Pay into the controller account.
|
|
44
|
+
*/
|
|
45
|
+
{
|
|
46
|
+
name: "rewardDestination",
|
|
47
|
+
type: String,
|
|
48
|
+
desc: "Reward destination",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
const polkadotValidatorsFormatters = {
|
|
52
|
+
json: (list) => JSON.stringify(list),
|
|
53
|
+
csv: (list) => {
|
|
54
|
+
const polkadotUnit = getCryptoCurrencyById("polkadot").units[0];
|
|
55
|
+
const csvHeader =
|
|
56
|
+
"ADDRESS IDENTITY TOTAL_BONDED SELF_BONDED NOMINATORS_COUNT COMMISSION REWARD_POINTS\n";
|
|
57
|
+
const csvList = list
|
|
58
|
+
.map(
|
|
59
|
+
(v) =>
|
|
60
|
+
`${v.address} "${v.identity}" ${formatCurrencyUnit(
|
|
61
|
+
polkadotUnit,
|
|
62
|
+
v.totalBonded,
|
|
63
|
+
{
|
|
64
|
+
showCode: false,
|
|
65
|
+
disableRounding: true,
|
|
66
|
+
useGrouping: false,
|
|
67
|
+
}
|
|
68
|
+
)} ${formatCurrencyUnit(polkadotUnit, v.selfBonded, {
|
|
69
|
+
showCode: false,
|
|
70
|
+
disableRounding: true,
|
|
71
|
+
useGrouping: false,
|
|
72
|
+
})} ${v.nominatorsCount} ${v.commission
|
|
73
|
+
.multipliedBy(100)
|
|
74
|
+
.toFixed(2)} ${v.rewardPoints}`
|
|
75
|
+
)
|
|
76
|
+
.join("\n");
|
|
77
|
+
return csvHeader + csvList;
|
|
78
|
+
},
|
|
79
|
+
default: (list) => {
|
|
80
|
+
const polkadotUnit = getCryptoCurrencyById("polkadot").units[0];
|
|
81
|
+
const tableList = list
|
|
82
|
+
.map(
|
|
83
|
+
(v) =>
|
|
84
|
+
`${v.address} "${v.identity}" ${formatCurrencyUnit(
|
|
85
|
+
polkadotUnit,
|
|
86
|
+
v.totalBonded,
|
|
87
|
+
{
|
|
88
|
+
showCode: true,
|
|
89
|
+
disableRounding: true,
|
|
90
|
+
useGrouping: false,
|
|
91
|
+
}
|
|
92
|
+
)} ${formatCurrencyUnit(polkadotUnit, v.selfBonded, {
|
|
93
|
+
showCode: true,
|
|
94
|
+
disableRounding: true,
|
|
95
|
+
useGrouping: false,
|
|
96
|
+
})} ${v.nominatorsCount} ${
|
|
97
|
+
v.commission.multipliedBy(100).toFixed(2) + "%"
|
|
98
|
+
} ${v.rewardPoints + " PTS"}`
|
|
99
|
+
)
|
|
100
|
+
.join("\n");
|
|
101
|
+
return tableList;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
const polkadotValidators = {
|
|
105
|
+
args: [
|
|
106
|
+
{
|
|
107
|
+
name: "format",
|
|
108
|
+
desc: Object.keys(polkadotValidatorsFormatters).join("|"),
|
|
109
|
+
type: String,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "status",
|
|
113
|
+
desc: "The status of the validators to fetch (all|elected|waiting)",
|
|
114
|
+
type: String,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "validator",
|
|
118
|
+
type: String,
|
|
119
|
+
multiple: true,
|
|
120
|
+
desc: "address of recipient validator that will receive the delegate",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
job: ({
|
|
124
|
+
format,
|
|
125
|
+
status,
|
|
126
|
+
validator,
|
|
127
|
+
}: Partial<{
|
|
128
|
+
format: string;
|
|
129
|
+
status: SidecarValidatorsParamStatus | SidecarValidatorsParamAddresses;
|
|
130
|
+
validator: string[];
|
|
131
|
+
}>): Observable<string> =>
|
|
132
|
+
from(
|
|
133
|
+
getValidators(validator && validator.length ? validator : status)
|
|
134
|
+
).pipe(
|
|
135
|
+
map((validators) => {
|
|
136
|
+
const f =
|
|
137
|
+
(format && polkadotValidatorsFormatters[format]) ||
|
|
138
|
+
polkadotValidatorsFormatters.default;
|
|
139
|
+
return f(validators);
|
|
140
|
+
})
|
|
141
|
+
),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
function inferTransactions(
|
|
145
|
+
transactions: Array<{
|
|
146
|
+
account: AccountLike;
|
|
147
|
+
transaction: Transaction;
|
|
148
|
+
}>,
|
|
149
|
+
opts: Record<string, any>,
|
|
150
|
+
{ inferAmount }: any
|
|
151
|
+
): Transaction[] {
|
|
152
|
+
return flatMap(transactions, ({ transaction, account }) => {
|
|
153
|
+
invariant(transaction.family === "polkadot", "polkadot family");
|
|
154
|
+
|
|
155
|
+
if (isAccount(account)) {
|
|
156
|
+
invariant(
|
|
157
|
+
(account as PolkadotAccount).polkadotResources,
|
|
158
|
+
"unactivated account"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const validators: string[] = opts["validator"] || [];
|
|
163
|
+
return {
|
|
164
|
+
...transaction,
|
|
165
|
+
family: "polkadot",
|
|
166
|
+
fees: opts.fees ? inferAmount(account, opts.fees) : null,
|
|
167
|
+
mode: opts.mode || "send",
|
|
168
|
+
validators,
|
|
169
|
+
era: opts.era || null,
|
|
170
|
+
rewardDestination: opts.rewardDestination || null,
|
|
171
|
+
numSlashingSpans: isAccount(account)
|
|
172
|
+
? (account as PolkadotAccount).polkadotResources?.numSlashingSpans
|
|
173
|
+
: null,
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default {
|
|
179
|
+
options,
|
|
180
|
+
inferTransactions,
|
|
181
|
+
commands: {
|
|
182
|
+
polkadotValidators,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { AccountLike, Account } from "@ledgerhq/types-live";
|
|
2
|
+
import type { PolkadotAccount, Transaction, TransactionStatus } from "./types";
|
|
3
|
+
import type { CommonDeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";
|
|
4
|
+
import { getMainAccount } from "@ledgerhq/coin-framework/account/index";
|
|
5
|
+
import {
|
|
6
|
+
formatCurrencyUnit,
|
|
7
|
+
getCryptoCurrencyById,
|
|
8
|
+
} from "@ledgerhq/coin-framework/currencies/index";
|
|
9
|
+
import { isStash } from "./logic";
|
|
10
|
+
const currency = getCryptoCurrencyById("polkadot");
|
|
11
|
+
export type ExtraDeviceTransactionField = {
|
|
12
|
+
type: "polkadot.validators";
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
type DeviceTransactionField = CommonDeviceTransactionField &
|
|
16
|
+
ExtraDeviceTransactionField;
|
|
17
|
+
|
|
18
|
+
const getSendFields = ({
|
|
19
|
+
transaction,
|
|
20
|
+
status: { amount },
|
|
21
|
+
}: {
|
|
22
|
+
transaction: Transaction;
|
|
23
|
+
status: TransactionStatus;
|
|
24
|
+
}) => {
|
|
25
|
+
const fields: { type: string; label: string; value: string }[] = [];
|
|
26
|
+
fields.push({
|
|
27
|
+
type: "text",
|
|
28
|
+
label: "Balances",
|
|
29
|
+
value:
|
|
30
|
+
transaction && transaction.useAllAmount
|
|
31
|
+
? "Transfer"
|
|
32
|
+
: "Transfer keep alive",
|
|
33
|
+
});
|
|
34
|
+
fields.push({
|
|
35
|
+
type: "text",
|
|
36
|
+
label: "Amount",
|
|
37
|
+
value: formatCurrencyUnit(currency.units[0], amount, {
|
|
38
|
+
showCode: true,
|
|
39
|
+
disableRounding: true,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
return fields;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function getDeviceTransactionConfig({
|
|
46
|
+
account,
|
|
47
|
+
parentAccount,
|
|
48
|
+
transaction,
|
|
49
|
+
status,
|
|
50
|
+
}: {
|
|
51
|
+
account: AccountLike;
|
|
52
|
+
parentAccount: Account | null | undefined;
|
|
53
|
+
transaction: Transaction;
|
|
54
|
+
status: TransactionStatus;
|
|
55
|
+
}): Array<DeviceTransactionField> {
|
|
56
|
+
const { mode, recipient, rewardDestination } = transaction;
|
|
57
|
+
const { amount } = status;
|
|
58
|
+
const mainAccount = getMainAccount(account, parentAccount) as PolkadotAccount;
|
|
59
|
+
let fields: { type: string; label: string; value?: string }[] = [];
|
|
60
|
+
|
|
61
|
+
switch (mode) {
|
|
62
|
+
case "send":
|
|
63
|
+
fields = getSendFields({
|
|
64
|
+
transaction,
|
|
65
|
+
status,
|
|
66
|
+
});
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case "nominate":
|
|
70
|
+
fields.push({
|
|
71
|
+
type: "text",
|
|
72
|
+
label: "Staking",
|
|
73
|
+
value: "Nominate",
|
|
74
|
+
});
|
|
75
|
+
fields.push({
|
|
76
|
+
type: "polkadot.validators",
|
|
77
|
+
label: "Targets",
|
|
78
|
+
});
|
|
79
|
+
break;
|
|
80
|
+
|
|
81
|
+
case "bond":
|
|
82
|
+
if (!isStash(mainAccount)) {
|
|
83
|
+
fields.push({
|
|
84
|
+
type: "text",
|
|
85
|
+
label: "Staking",
|
|
86
|
+
value: "Bond",
|
|
87
|
+
});
|
|
88
|
+
fields.push({
|
|
89
|
+
type: "text",
|
|
90
|
+
label: "Controller",
|
|
91
|
+
value: recipient,
|
|
92
|
+
});
|
|
93
|
+
fields.push({
|
|
94
|
+
type: "text",
|
|
95
|
+
label: "Amount",
|
|
96
|
+
value: formatCurrencyUnit(currency.units[0], amount, {
|
|
97
|
+
showCode: true,
|
|
98
|
+
disableRounding: true,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
fields.push({
|
|
102
|
+
type: "text",
|
|
103
|
+
label: "Payee",
|
|
104
|
+
value: rewardDestination || "Stash",
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
fields.push({
|
|
108
|
+
type: "text",
|
|
109
|
+
label: "Staking",
|
|
110
|
+
value: "Bond extra",
|
|
111
|
+
});
|
|
112
|
+
fields.push({
|
|
113
|
+
type: "text",
|
|
114
|
+
label: "Amount",
|
|
115
|
+
value: formatCurrencyUnit(currency.units[0], amount, {
|
|
116
|
+
showCode: true,
|
|
117
|
+
disableRounding: true,
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case "rebond":
|
|
125
|
+
fields.push({
|
|
126
|
+
type: "text",
|
|
127
|
+
label: "Staking",
|
|
128
|
+
value: "Rebond",
|
|
129
|
+
});
|
|
130
|
+
fields.push({
|
|
131
|
+
type: "text",
|
|
132
|
+
label: "Amount",
|
|
133
|
+
value: formatCurrencyUnit(currency.units[0], amount, {
|
|
134
|
+
showCode: true,
|
|
135
|
+
disableRounding: true,
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case "unbond":
|
|
141
|
+
fields.push({
|
|
142
|
+
type: "text",
|
|
143
|
+
label: "Staking",
|
|
144
|
+
value: "Unbond",
|
|
145
|
+
});
|
|
146
|
+
fields.push({
|
|
147
|
+
type: "text",
|
|
148
|
+
label: "Amount",
|
|
149
|
+
value: formatCurrencyUnit(currency.units[0], amount, {
|
|
150
|
+
showCode: true,
|
|
151
|
+
disableRounding: true,
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
case "chill":
|
|
157
|
+
fields.push({
|
|
158
|
+
type: "text",
|
|
159
|
+
label: "Staking",
|
|
160
|
+
value: "Chill",
|
|
161
|
+
});
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
case "withdrawUnbonded":
|
|
165
|
+
fields.push({
|
|
166
|
+
type: "text",
|
|
167
|
+
label: "Staking",
|
|
168
|
+
value: "Withdraw Unbonded",
|
|
169
|
+
});
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case "setController":
|
|
173
|
+
fields.push({
|
|
174
|
+
type: "text",
|
|
175
|
+
label: "Staking",
|
|
176
|
+
value: "Set controller",
|
|
177
|
+
});
|
|
178
|
+
fields.push({
|
|
179
|
+
type: "text",
|
|
180
|
+
label: "Controller",
|
|
181
|
+
value:
|
|
182
|
+
// NOTE: I added this here as TokenAccount and ChildAccount
|
|
183
|
+
// both don't have freshAddress
|
|
184
|
+
account.type === "Account"
|
|
185
|
+
? account.freshAddress
|
|
186
|
+
: mainAccount.freshAddress,
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
|
|
190
|
+
case "claimReward":
|
|
191
|
+
fields.push({
|
|
192
|
+
type: "text",
|
|
193
|
+
label: "Staking",
|
|
194
|
+
value: "Payout Stakers",
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
default:
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fields.push({
|
|
203
|
+
type: "fees",
|
|
204
|
+
label: "Fees",
|
|
205
|
+
});
|
|
206
|
+
return fields as DeviceTransactionField[];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default getDeviceTransactionConfig;
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createCustomErrorClass } from "@ledgerhq/errors";
|
|
2
|
+
export const PolkadotUnauthorizedOperation = createCustomErrorClass(
|
|
3
|
+
"PolkadotUnauthorizedOperation"
|
|
4
|
+
);
|
|
5
|
+
export const PolkadotElectionClosed = createCustomErrorClass(
|
|
6
|
+
"PolkadotElectionClosed"
|
|
7
|
+
);
|
|
8
|
+
export const PolkadotNotValidator = createCustomErrorClass(
|
|
9
|
+
"PolkadotNotValidator"
|
|
10
|
+
);
|
|
11
|
+
export const PolkadotLowBondedBalance = createCustomErrorClass(
|
|
12
|
+
"PolkadotLowBondedBalance"
|
|
13
|
+
);
|
|
14
|
+
export const PolkadotNoUnlockedBalance = createCustomErrorClass(
|
|
15
|
+
"PolkadotNoUnlockedBalance"
|
|
16
|
+
);
|
|
17
|
+
export const PolkadotNoNominations = createCustomErrorClass(
|
|
18
|
+
"PolkadotNoNominations"
|
|
19
|
+
);
|
|
20
|
+
export const PolkadotAllFundsWarning = createCustomErrorClass(
|
|
21
|
+
"PolkadotAllFundsWarning"
|
|
22
|
+
);
|
|
23
|
+
export const PolkadotBondMinimumAmount = createCustomErrorClass(
|
|
24
|
+
"PolkadotBondMinimumAmount"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export const PolkadotBondMinimumAmountWarning = createCustomErrorClass(
|
|
28
|
+
"PolkadotBondMinimumAmountWarning"
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export const PolkadotMaxUnbonding = createCustomErrorClass(
|
|
32
|
+
"PolkadotMaxUnbonding"
|
|
33
|
+
);
|
|
34
|
+
export const PolkadotValidatorsRequired = createCustomErrorClass(
|
|
35
|
+
"PolkadotValidatorsRequired"
|
|
36
|
+
);
|
|
37
|
+
export const PolkadotDoMaxSendInstead = createCustomErrorClass(
|
|
38
|
+
"PolkadotDoMaxSendInstead"
|
|
39
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Polkadot from "@ledgerhq/hw-app-polkadot";
|
|
2
|
+
import type { Resolver } from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
|
|
3
|
+
|
|
4
|
+
const resolver: Resolver = async (transport, { path, verify }) => {
|
|
5
|
+
const polkadot = new Polkadot(transport);
|
|
6
|
+
const r = await polkadot.getAddress(path, verify);
|
|
7
|
+
return {
|
|
8
|
+
address: r.address,
|
|
9
|
+
publicKey: r.pubKey,
|
|
10
|
+
path,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default resolver;
|