@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,232 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import { Observable } from "rxjs";
3
+ import { TypeRegistry } from "@polkadot/types";
4
+ import { u8aConcat } from "@polkadot/util";
5
+ import { FeeNotLoaded } from "@ledgerhq/errors";
6
+ import Polkadot from "@ledgerhq/hw-app-polkadot";
7
+ import type { PolkadotAccount, Transaction } from "./types";
8
+ import type {
9
+ Account,
10
+ Operation,
11
+ OperationType,
12
+ SignOperationEvent,
13
+ SignOperationFnSignature,
14
+ } from "@ledgerhq/types-live";
15
+ import { DeviceCommunication } from "@ledgerhq/coin-framework/bridge/jsHelpers";
16
+ import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
17
+ import { buildTransaction } from "./js-buildTransaction";
18
+ import { calculateAmount, getNonce, isFirstBond } from "./logic";
19
+ const MODE_TO_TYPE = {
20
+ send: "OUT",
21
+ bond: "BOND",
22
+ unbond: "UNBOND",
23
+ rebond: "BOND",
24
+ withdrawUnbonded: "WITHDRAW_UNBONDED",
25
+ nominate: "NOMINATE",
26
+ chill: "CHILL",
27
+ setController: "SET_CONTROLLER",
28
+ claimReward: "REWARD_PAYOUT",
29
+ default: "FEES",
30
+ };
31
+ const MODE_TO_PALLET_METHOD = {
32
+ send: "balances.transferKeepAlive",
33
+ sendMax: "balances.transfer",
34
+ bond: "staking.bond",
35
+ bondExtra: "staking.bondExtra",
36
+ unbond: "staking.unbond",
37
+ rebond: "staking.rebond",
38
+ withdrawUnbonded: "staking.withdrawUnbonded",
39
+ nominate: "staking.nominate",
40
+ chill: "staking.chill",
41
+ setController: "staking.setController",
42
+ claimReward: "staking.payoutStakers",
43
+ };
44
+
45
+ const getExtra = (
46
+ type: string,
47
+ account: PolkadotAccount,
48
+ transaction: Transaction
49
+ ) => {
50
+ const extra = {
51
+ palletMethod: MODE_TO_PALLET_METHOD[transaction.mode],
52
+ };
53
+
54
+ if (transaction.mode == "send" && transaction.useAllAmount) {
55
+ extra.palletMethod = MODE_TO_PALLET_METHOD["sendMax"];
56
+ } else if (transaction.mode === "bond" && !isFirstBond(account)) {
57
+ extra.palletMethod = MODE_TO_PALLET_METHOD["bondExtra"];
58
+ }
59
+
60
+ switch (type) {
61
+ case "OUT":
62
+ return { ...extra, transferAmount: new BigNumber(transaction.amount) };
63
+
64
+ case "BOND":
65
+ return { ...extra, bondedAmount: new BigNumber(transaction.amount) };
66
+
67
+ case "UNBOND":
68
+ return { ...extra, unbondedAmount: new BigNumber(transaction.amount) };
69
+
70
+ case "WITHDRAW_UNBONDED":
71
+ return {
72
+ ...extra,
73
+ withdrawUnbondedAmount: new BigNumber(
74
+ account.polkadotResources?.unlockedBalance || 0
75
+ ),
76
+ };
77
+
78
+ case "NOMINATE":
79
+ return { ...extra, validators: transaction.validators };
80
+ }
81
+
82
+ return extra;
83
+ };
84
+
85
+ const buildOptimisticOperation = (
86
+ account: PolkadotAccount,
87
+ transaction: Transaction,
88
+ fee: BigNumber
89
+ ): Operation => {
90
+ const type = (MODE_TO_TYPE[transaction.mode] ??
91
+ MODE_TO_TYPE.default) as OperationType;
92
+ const value =
93
+ type === "OUT"
94
+ ? new BigNumber(transaction.amount).plus(fee)
95
+ : new BigNumber(fee);
96
+ const extra = getExtra(type, account, transaction);
97
+ const operation: Operation = {
98
+ id: encodeOperationId(account.id, "", type),
99
+ hash: "",
100
+ type,
101
+ value,
102
+ fee,
103
+ blockHash: null,
104
+ blockHeight: null,
105
+ senders: [account.freshAddress],
106
+ recipients: [transaction.recipient].filter(Boolean),
107
+ accountId: account.id,
108
+ transactionSequenceNumber: getNonce(account),
109
+ date: new Date(),
110
+ extra,
111
+ };
112
+ return operation;
113
+ };
114
+
115
+ /**
116
+ * Serialize a signed transaction in a format that can be submitted over the
117
+ * Node RPC Interface from the signing payload and signature produced by the
118
+ * remote signer.
119
+ *
120
+ * @param unsigned - The JSON representing the unsigned transaction.
121
+ * @param signature - Signature of the signing payload produced by the remote signer.
122
+ * @param registry - Registry used for constructing the payload.
123
+ */
124
+ export const signExtrinsic = async (
125
+ unsigned: Record<string, any>,
126
+ signature: any,
127
+ registry: TypeRegistry
128
+ ): Promise<string> => {
129
+ const extrinsic = registry.createType("Extrinsic", unsigned, {
130
+ version: unsigned.version,
131
+ });
132
+ extrinsic.addSignature(unsigned.address, signature, unsigned as any);
133
+ return extrinsic.toHex();
134
+ };
135
+
136
+ /**
137
+ * Sign Extrinsic with a fake signature (for fees estimation).
138
+ *
139
+ * @param unsigned - The JSON representing the unsigned transaction.
140
+ * @param registry - Registry used for constructing the payload.
141
+ */
142
+ export const fakeSignExtrinsic = async (
143
+ unsigned: Record<string, any>,
144
+ registry: TypeRegistry
145
+ ): Promise<string> => {
146
+ const fakeSignature = u8aConcat(
147
+ new Uint8Array([1]),
148
+ new Uint8Array(64).fill(0x42)
149
+ );
150
+ return signExtrinsic(unsigned, fakeSignature, registry);
151
+ };
152
+
153
+ /**
154
+ * Sign Transaction with Ledger hardware
155
+ */
156
+ const buildSignOperation =
157
+ (withDevice: DeviceCommunication): SignOperationFnSignature<Transaction> =>
158
+ ({
159
+ account,
160
+ deviceId,
161
+ transaction,
162
+ }: {
163
+ account: Account;
164
+ deviceId: any;
165
+ transaction: Transaction;
166
+ }): Observable<SignOperationEvent> =>
167
+ withDevice(deviceId)(
168
+ (transport) =>
169
+ new Observable((o) => {
170
+ async function main() {
171
+ o.next({
172
+ type: "device-signature-requested",
173
+ });
174
+
175
+ if (!transaction.fees) {
176
+ throw new FeeNotLoaded();
177
+ }
178
+
179
+ // Ensure amount is filled when useAllAmount
180
+ const transactionToSign = {
181
+ ...transaction,
182
+ amount: calculateAmount({
183
+ a: account as PolkadotAccount,
184
+ t: transaction,
185
+ }),
186
+ };
187
+ const { unsigned, registry } = await buildTransaction(
188
+ account as PolkadotAccount,
189
+ transactionToSign,
190
+ true
191
+ );
192
+ const payload = registry
193
+ .createType("ExtrinsicPayload", unsigned, {
194
+ version: unsigned.version,
195
+ })
196
+ .toU8a({
197
+ method: true,
198
+ });
199
+ // Sign by device
200
+ const polkadot = new Polkadot(transport);
201
+ // FIXME: the type of payload Uint8Array is not compatible with the signature of sign which accept a string
202
+ const r = await polkadot.sign(
203
+ account.freshAddressPath,
204
+ payload as any
205
+ );
206
+ const signed = await signExtrinsic(unsigned, r.signature, registry);
207
+ o.next({
208
+ type: "device-signature-granted",
209
+ });
210
+ const operation = buildOptimisticOperation(
211
+ account as PolkadotAccount,
212
+ transactionToSign,
213
+ transactionToSign.fees ?? new BigNumber(0)
214
+ );
215
+ o.next({
216
+ type: "signed",
217
+ signedOperation: {
218
+ operation,
219
+ signature: signed,
220
+ expirationDate: null,
221
+ },
222
+ });
223
+ }
224
+
225
+ main().then(
226
+ () => o.complete(),
227
+ (e) => o.error(e)
228
+ );
229
+ })
230
+ );
231
+
232
+ export default buildSignOperation;
@@ -0,0 +1,57 @@
1
+ import type { Account } from "@ledgerhq/types-live";
2
+ import { encodeAccountId } from "@ledgerhq/coin-framework/account/index";
3
+ import type { GetAccountShape } from "@ledgerhq/coin-framework/bridge/jsHelpers";
4
+ import { mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
5
+ import { getAccount, getOperations } from "./api";
6
+ import { loadPolkadotCrypto } from "./polkadot-crypto";
7
+
8
+ export const getAccountShape: GetAccountShape = async (info) => {
9
+ await loadPolkadotCrypto();
10
+ const { address, initialAccount, currency, derivationMode } = info;
11
+ const oldOperations = initialAccount?.operations || [];
12
+ const startAt = oldOperations.length
13
+ ? (oldOperations[0].blockHeight || 0) + 1
14
+ : 0;
15
+ const {
16
+ blockHeight,
17
+ balance,
18
+ spendableBalance,
19
+ nonce,
20
+ lockedBalance,
21
+ controller,
22
+ stash,
23
+ unlockedBalance,
24
+ unlockingBalance,
25
+ unlockings,
26
+ nominations,
27
+ numSlashingSpans,
28
+ } = await getAccount(address);
29
+ const accountId = encodeAccountId({
30
+ type: "js",
31
+ version: "2",
32
+ currencyId: currency.id,
33
+ xpubOrAddress: address,
34
+ derivationMode,
35
+ });
36
+ const newOperations = await getOperations(accountId, address, startAt);
37
+ const operations = mergeOps(oldOperations, newOperations);
38
+ const shape = {
39
+ id: accountId,
40
+ balance,
41
+ spendableBalance,
42
+ operationsCount: operations.length,
43
+ blockHeight,
44
+ polkadotResources: {
45
+ nonce,
46
+ controller,
47
+ stash,
48
+ lockedBalance,
49
+ unlockedBalance,
50
+ unlockingBalance,
51
+ unlockings,
52
+ nominations,
53
+ numSlashingSpans,
54
+ },
55
+ };
56
+ return { ...shape, operations } as Partial<Account>;
57
+ };
package/src/logic.ts ADDED
@@ -0,0 +1,266 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import type { Account, OperationType } from "@ledgerhq/types-live";
3
+ import type { PolkadotAccount, Transaction } from "./types";
4
+ import { getCurrentPolkadotPreloadData } from "./preload";
5
+
6
+ export const EXISTENTIAL_DEPOSIT = new BigNumber(10000000000);
7
+ export const EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN = new BigNumber(1000000000); // Polkadot recommended Existential Deposit error margin
8
+ export const MAX_NOMINATIONS = 16;
9
+ export const MAX_UNLOCKINGS = 32;
10
+ export const PRELOAD_MAX_AGE = 60 * 1000;
11
+ // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
12
+ export const MAX_AMOUNT_INPUT = 0xffffffffffffffff;
13
+ export const FEES_SAFETY_BUFFER = new BigNumber(1000000000); // Arbitrary buffer for paying fees of next transactions
14
+
15
+ /**
16
+ * Returns true if account is a stash.
17
+ * When the account is a stash, we have the information of which account is the controller
18
+ *
19
+ * @param {PolkadotAccount} a
20
+ */
21
+ export const isStash = (a: PolkadotAccount): boolean => {
22
+ return !!a.polkadotResources?.controller;
23
+ };
24
+
25
+ /**
26
+ * Returns true if account is a controller.
27
+ * when account is a controller, we have the information of which stash it controls
28
+ *
29
+ * @param {PolkadotAccount} a
30
+ */
31
+ export const isController = (a: PolkadotAccount): boolean => {
32
+ return !!a.polkadotResources?.stash;
33
+ };
34
+
35
+ /**
36
+ * Returns true if account is controlled by an external account (not self)
37
+ *
38
+ * @param {PolkadotAccount} a
39
+ */
40
+ export const hasExternalController = (a: PolkadotAccount): boolean => {
41
+ return a.polkadotResources?.controller
42
+ ? a.polkadotResources?.controller !== a.freshAddress
43
+ : false;
44
+ };
45
+
46
+ /**
47
+ * Returns true if account controls an external stash (not self)
48
+ *
49
+ * @param {PolkadotAccount} a
50
+ */
51
+ export const hasExternalStash = (a: PolkadotAccount): boolean => {
52
+ return a.polkadotResources?.stash
53
+ ? a.polkadotResources?.stash !== a.freshAddress
54
+ : false;
55
+ };
56
+
57
+ /**
58
+ * Must have the minimum balance to bond
59
+ *
60
+ * @param {PolkadotAccount} a
61
+ */
62
+ export const canBond = (a: Account): boolean => {
63
+ const { balance } = a;
64
+ return EXISTENTIAL_DEPOSIT.lte(balance);
65
+ };
66
+
67
+ /**
68
+ * Get the minimum bond amount required to bond/rebond
69
+ *
70
+ * @param {PolkadotAccount} a
71
+ */
72
+ export const getMinimumAmountToBond = (
73
+ a: PolkadotAccount,
74
+ minimumBondBalance: BigNumber | undefined
75
+ ): BigNumber => {
76
+ const currentlyBondedBalance = calculateMaxUnbond(a);
77
+
78
+ if (minimumBondBalance && currentlyBondedBalance.lt(minimumBondBalance)) {
79
+ return minimumBondBalance.minus(currentlyBondedBalance);
80
+ }
81
+
82
+ return new BigNumber(0);
83
+ };
84
+
85
+ /**
86
+ * Return true if the account has at least the current minimum bonded balance required by the network
87
+ *
88
+ * @param {PolkadotAccount} a
89
+ */
90
+ export const hasMinimumBondBalance = (a: PolkadotAccount): boolean => {
91
+ const { minimumBondBalance } = getCurrentPolkadotPreloadData();
92
+ return (
93
+ !a.polkadotResources ||
94
+ a.polkadotResources.lockedBalance.gte(new BigNumber(minimumBondBalance))
95
+ );
96
+ };
97
+
98
+ /**
99
+ * Return true if some operation with type is pending and not yet synchronized
100
+ *
101
+ * @param {PolkadotAccount} a
102
+ */
103
+ export const hasPendingOperationType = (
104
+ a: Account,
105
+ type: OperationType
106
+ ): boolean => {
107
+ return a.pendingOperations?.some((op) => op.type === type) ?? false;
108
+ };
109
+
110
+ /**
111
+ * Retyrns true if has reached the maximum of unlocking slots
112
+ *
113
+ * @param {PolkadotAccount} a
114
+ */
115
+ export const hasMaxUnlockings = (a: PolkadotAccount) => {
116
+ const { unlockings = [] } = a.polkadotResources || {};
117
+ return (unlockings?.length || 0) >= MAX_UNLOCKINGS;
118
+ };
119
+
120
+ /**
121
+ * Return true if account has enough Locked Balance to rebond
122
+ *
123
+ * @param {PolkadotAccount} a
124
+ */
125
+ export const hasLockedBalance = (a: PolkadotAccount) => {
126
+ const {
127
+ lockedBalance = new BigNumber(0),
128
+ unlockingBalance = new BigNumber(0),
129
+ } = a.polkadotResources || {};
130
+ return lockedBalance.minus(unlockingBalance).gt(0);
131
+ };
132
+
133
+ /**
134
+ * Must have locked Balance
135
+ *
136
+ * @param {PolkadotAccount} a
137
+ */
138
+ export const canUnbond = (a: PolkadotAccount): boolean => {
139
+ return hasLockedBalance(a) && !hasMaxUnlockings(a);
140
+ };
141
+
142
+ /**
143
+ * Returns true if an account can nominate
144
+ *
145
+ * @param {PolkadotAccount} a
146
+ */
147
+ export const canNominate = (a: PolkadotAccount): boolean => isController(a);
148
+
149
+ /**
150
+ * Returns true if account must do a first bond - false for a bond extra
151
+ *
152
+ * @param {Account} a
153
+ */
154
+ export const isFirstBond = (a: PolkadotAccount): boolean => !isStash(a);
155
+
156
+ /**
157
+ * Returns nonce for an account
158
+ *
159
+ * @param {Account} a
160
+ */
161
+ export const getNonce = (a: PolkadotAccount): number => {
162
+ const lastPendingOp = a.pendingOperations[0];
163
+ const nonce = Math.max(
164
+ a.polkadotResources?.nonce || 0,
165
+ lastPendingOp && typeof lastPendingOp.transactionSequenceNumber === "number"
166
+ ? lastPendingOp.transactionSequenceNumber + 1
167
+ : 0
168
+ );
169
+ return nonce;
170
+ };
171
+
172
+ /**
173
+ * Calculate max bond which is the actual spendable minus a safety buffer,
174
+ * so the user still has funds to pay the fees for next transactions
175
+ *
176
+ * @param {*} a
177
+ * @param {*} t
178
+ */
179
+ const calculateMaxBond = (a: PolkadotAccount, t: Transaction): BigNumber => {
180
+ const amount = a.spendableBalance
181
+ .minus(t.fees || 0)
182
+ .minus(FEES_SAFETY_BUFFER);
183
+ return amount.lt(0) ? new BigNumber(0) : amount;
184
+ };
185
+
186
+ /**
187
+ * Calculates max unbond amount which is the remaining active locked balance (not unlocking)
188
+ *
189
+ * @param {*} account
190
+ */
191
+ const calculateMaxUnbond = (a: PolkadotAccount): BigNumber => {
192
+ return (
193
+ a.polkadotResources?.lockedBalance.minus(
194
+ a.polkadotResources.unlockingBalance
195
+ ) ?? new BigNumber(0)
196
+ );
197
+ };
198
+
199
+ /**
200
+ * Calculates max rebond amount which is the current unlocking balance (including unlocked)
201
+ *
202
+ * @param {*} account
203
+ */
204
+ const calculateMaxRebond = (a: PolkadotAccount): BigNumber => {
205
+ return a.polkadotResources?.unlockingBalance ?? new BigNumber(0);
206
+ };
207
+
208
+ /**
209
+ * Calculate the real spendable
210
+ *
211
+ * @param {*} a
212
+ * @param {*} t
213
+ */
214
+ const calculateMaxSend = (a: Account, t: Transaction): BigNumber => {
215
+ const amount = a.spendableBalance.minus(t.fees || 0);
216
+ return amount.lt(0) ? new BigNumber(0) : amount;
217
+ };
218
+
219
+ /**
220
+ * Calculates correct amount if useAllAmount
221
+ *
222
+ * @param {*} param
223
+ */
224
+ export const calculateAmount = ({
225
+ a,
226
+ t,
227
+ }: {
228
+ a: PolkadotAccount;
229
+ t: Transaction;
230
+ }): BigNumber => {
231
+ let amount = t.amount;
232
+
233
+ if (t.useAllAmount) {
234
+ switch (t.mode) {
235
+ case "send":
236
+ amount = calculateMaxSend(a, t);
237
+ break;
238
+
239
+ case "bond":
240
+ amount = calculateMaxBond(a, t);
241
+ break;
242
+
243
+ case "unbond":
244
+ amount = calculateMaxUnbond(a);
245
+ break;
246
+
247
+ case "rebond":
248
+ amount = calculateMaxRebond(a);
249
+ break;
250
+
251
+ default:
252
+ amount = a.spendableBalance.minus(t.fees || 0);
253
+ break;
254
+ }
255
+ } else if (t.amount.gt(MAX_AMOUNT_INPUT)) {
256
+ return new BigNumber(MAX_AMOUNT_INPUT);
257
+ }
258
+
259
+ return amount.lt(0) ? new BigNumber(0) : amount;
260
+ };
261
+ export const getMinimumBalance = (a: Account): BigNumber => {
262
+ const lockedBalance = a.balance.minus(a.spendableBalance);
263
+ return lockedBalance.lte(EXISTENTIAL_DEPOSIT)
264
+ ? EXISTENTIAL_DEPOSIT.minus(lockedBalance)
265
+ : new BigNumber(0);
266
+ };
@@ -0,0 +1,61 @@
1
+ import dot from "./platformAdapter";
2
+ import { PolkadotTransaction as PlatformTransaction } from "@ledgerhq/live-app-sdk";
3
+ import { FAMILIES } from "@ledgerhq/live-app-sdk";
4
+ import BigNumber from "bignumber.js";
5
+ import { Transaction } from "./types";
6
+
7
+ describe("getPlatformTransactionSignFlowInfos", () => {
8
+ describe("should properly get infos for DOT platform tx", () => {
9
+ it("with most basic tx", () => {
10
+ const dotPlatformTx: PlatformTransaction = {
11
+ family: FAMILIES.POLKADOT,
12
+ amount: new BigNumber(100000),
13
+ recipient: "0xABCDEF",
14
+ mode: "send",
15
+ };
16
+
17
+ const expectedLiveTx: Partial<Transaction> = {
18
+ family: dotPlatformTx.family,
19
+ amount: dotPlatformTx.amount,
20
+ recipient: dotPlatformTx.recipient,
21
+ mode: dotPlatformTx.mode,
22
+ };
23
+
24
+ const { canEditFees, hasFeesProvided, liveTx } =
25
+ dot.getPlatformTransactionSignFlowInfos(dotPlatformTx);
26
+
27
+ expect(canEditFees).toBe(false);
28
+
29
+ expect(hasFeesProvided).toBe(false);
30
+
31
+ expect(liveTx).toEqual(expectedLiveTx);
32
+ });
33
+
34
+ it("with era provided", () => {
35
+ const dotPlatformTx: PlatformTransaction = {
36
+ family: FAMILIES.POLKADOT,
37
+ amount: new BigNumber(100000),
38
+ recipient: "0xABCDEF",
39
+ mode: "send",
40
+ era: 1,
41
+ };
42
+
43
+ const expectedLiveTx: Partial<Transaction> = {
44
+ family: dotPlatformTx.family,
45
+ amount: dotPlatformTx.amount,
46
+ recipient: dotPlatformTx.recipient,
47
+ mode: dotPlatformTx.mode,
48
+ era: `${dotPlatformTx.era}`,
49
+ };
50
+
51
+ const { canEditFees, hasFeesProvided, liveTx } =
52
+ dot.getPlatformTransactionSignFlowInfos(dotPlatformTx);
53
+
54
+ expect(canEditFees).toBe(false);
55
+
56
+ expect(hasFeesProvided).toBe(false);
57
+
58
+ expect(liveTx).toEqual(expectedLiveTx);
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,29 @@
1
+ // TODO: to remove once live-app-sdk is depreciated and removed from LL
2
+
3
+ import { PolkadotTransaction as PlatformTransaction } from "@ledgerhq/live-app-sdk";
4
+ import { Transaction } from "./types";
5
+
6
+ const CAN_EDIT_FEES = false;
7
+
8
+ const convertToLiveTransaction = (
9
+ tx: PlatformTransaction
10
+ ): Partial<Transaction> => ({
11
+ ...tx,
12
+ era: tx.era ? `${tx.era}` : undefined,
13
+ });
14
+
15
+ const getPlatformTransactionSignFlowInfos = (
16
+ tx: PlatformTransaction
17
+ ): {
18
+ canEditFees: boolean;
19
+ hasFeesProvided: boolean;
20
+ liveTx: Partial<Transaction>;
21
+ } => {
22
+ return {
23
+ canEditFees: CAN_EDIT_FEES,
24
+ liveTx: convertToLiveTransaction(tx),
25
+ hasFeesProvided: false,
26
+ };
27
+ };
28
+
29
+ export default { getPlatformTransactionSignFlowInfos };
@@ -0,0 +1,10 @@
1
+ import { cryptoWaitReady } from "@polkadot/util-crypto";
2
+
3
+ let loadingPromise: null | Promise<boolean>;
4
+
5
+ export function loadPolkadotCrypto(): Promise<boolean> {
6
+ if (loadingPromise) return loadingPromise;
7
+ const p = cryptoWaitReady();
8
+ loadingPromise = p;
9
+ return p;
10
+ }