@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,206 @@
1
+ import { stringCamelCase } from "@polkadot/util";
2
+ import type { PolkadotAccount, Transaction } from "./types";
3
+ import { getRegistry, getTransactionParams } from "./cache";
4
+ import { isFirstBond, getNonce } from "./logic";
5
+ import { loadPolkadotCrypto } from "./polkadot-crypto";
6
+ const EXTRINSIC_VERSION = 4;
7
+ // Default values for tx parameters, if the user doesn't specify any
8
+ const DEFAULTS = {
9
+ tip: 0,
10
+ eraPeriod: 64,
11
+ };
12
+
13
+ const getExtrinsicParams = (a: PolkadotAccount, t: Transaction) => {
14
+ const validator = t.validators ? t.validators[0] : null;
15
+
16
+ switch (t.mode) {
17
+ case "send":
18
+ // Construct a balance transfer transaction offline.
19
+ return {
20
+ args: {
21
+ dest: t.recipient,
22
+ value: t.amount.toString(),
23
+ },
24
+ name: t.useAllAmount ? "transfer" : "transferKeepAlive",
25
+ pallet: "balances",
26
+ };
27
+
28
+ case "bond":
29
+ if (isFirstBond(a)) {
30
+ return {
31
+ pallet: "staking",
32
+ name: "bond",
33
+ args: {
34
+ // Spec choice: we always set the account as both the stash and its controller
35
+ controller: a.freshAddress,
36
+ value: t.amount.toString(),
37
+ // The rewards destination. Can be "Stash", "Staked", "Controller" or "{ Account: accountId }"".
38
+ payee: t.rewardDestination || "Stash",
39
+ },
40
+ };
41
+ } else {
42
+ // Add some extra amount from the stash's `free_balance` into the staking balance.
43
+ // Can only be called when `EraElectionStatus` is `Closed`.
44
+ return {
45
+ pallet: "staking",
46
+ name: "bondExtra",
47
+ args: {
48
+ maxAdditional: t.amount.toString(),
49
+ },
50
+ };
51
+ }
52
+
53
+ case "unbond":
54
+ // Construct a transaction to unbond funds from a Stash account.
55
+ // Must be signed by the controller, and can be only called when `EraElectionStatus` is `Closed`.
56
+ return {
57
+ pallet: "staking",
58
+ name: "unbond",
59
+ args: {
60
+ value: t.amount.toString(),
61
+ },
62
+ };
63
+
64
+ case "rebond":
65
+ // Rebond a portion of the stash scheduled to be unlocked.
66
+ // Must be signed by the controller, and can be only called when `EraElectionStatus` is `Closed`.
67
+ return {
68
+ pallet: "staking",
69
+ name: "rebond",
70
+ args: {
71
+ value: t.amount.toString(),
72
+ },
73
+ };
74
+
75
+ case "withdrawUnbonded":
76
+ // Remove any unbonded chunks from the `unbonding` queue from our management
77
+ // Must be signed by the controller, and can be only called when `EraElectionStatus` is `Closed`.
78
+ return {
79
+ pallet: "staking",
80
+ name: "withdrawUnbonded",
81
+ args: {
82
+ numSlashingSpans: a.polkadotResources?.numSlashingSpans || 0,
83
+ },
84
+ };
85
+
86
+ case "setController":
87
+ // Set the current account as its own controller
88
+ return {
89
+ pallet: "staking",
90
+ name: "setController",
91
+ args: { controller: a.freshAddress },
92
+ };
93
+
94
+ case "nominate":
95
+ // Construct a transaction to nominate validators.
96
+ // Must be signed by the controller, and can be only called when `EraElectionStatus` is `Closed`.
97
+ return {
98
+ pallet: "staking",
99
+ name: "nominate",
100
+ args: {
101
+ targets: t.validators,
102
+ },
103
+ };
104
+
105
+ case "chill":
106
+ // Declare the desire to cease validating or nominating. Does not unbond funds.
107
+ // Must be signed by the controller, and can be only called when `EraElectionStatus` is `Closed`.
108
+ return {
109
+ pallet: "staking",
110
+ name: "chill",
111
+ args: {},
112
+ };
113
+
114
+ case "claimReward":
115
+ // Pay out all the stakers behind a single validator for a single era.
116
+ // Any account can call this function, even if it is not one of the stakers.
117
+ // Can only be called when `EraElectionStatus` is `Closed`.
118
+ return {
119
+ pallet: "staking",
120
+ name: "payoutStakers",
121
+ args: {
122
+ validatorStash: validator,
123
+ era: t.era,
124
+ },
125
+ };
126
+
127
+ default:
128
+ throw new Error("Unknown mode in transaction");
129
+ }
130
+ };
131
+
132
+ /**
133
+ *
134
+ * @param {Account} a
135
+ * @param {Transaction} t
136
+ * @param {boolean} forceLatestParams - forces the use of latest transaction params
137
+ */
138
+ export const buildTransaction = async (
139
+ a: PolkadotAccount,
140
+ t: Transaction,
141
+ forceLatestParams = false
142
+ ) => {
143
+ await loadPolkadotCrypto();
144
+
145
+ const { extrinsics, registry } = await getRegistry();
146
+ const info = forceLatestParams
147
+ ? await getTransactionParams.force()
148
+ : await getTransactionParams();
149
+ // Get the correct extrinsics params depending on transaction
150
+ const extrinsicParams = getExtrinsicParams(a, t);
151
+ const address = a.freshAddress;
152
+ const { blockHash, genesisHash } = info;
153
+ const blockNumber = registry
154
+ .createType("BlockNumber", info.blockNumber)
155
+ .toHex();
156
+ const era = registry
157
+ .createType("ExtrinsicEra", {
158
+ current: info.blockNumber,
159
+ period: DEFAULTS.eraPeriod,
160
+ })
161
+ .toHex();
162
+ const nonce = registry.createType("Compact<Index>", getNonce(a)).toHex();
163
+ const specVersion = registry.createType("u32", info.specVersion).toHex();
164
+ const tip = registry
165
+ .createType("Compact<Balance>", info.tip || DEFAULTS.tip)
166
+ .toHex();
167
+ const transactionVersion = registry
168
+ .createType("u32", info.transactionVersion)
169
+ .toHex();
170
+ const methodFunction =
171
+ extrinsics[extrinsicParams.pallet][extrinsicParams.name];
172
+ const methodArgs = methodFunction.meta.args;
173
+ const method = methodFunction(
174
+ ...methodArgs.map((arg) => {
175
+ if (
176
+ extrinsicParams.args[stringCamelCase(arg.name.toString())] === undefined
177
+ ) {
178
+ throw new Error(
179
+ `Method ${extrinsicParams.pallet}::${
180
+ extrinsicParams.name
181
+ } expects argument ${arg.toString()}, but got undefined`
182
+ );
183
+ }
184
+
185
+ return extrinsicParams.args[stringCamelCase(arg.name.toString())];
186
+ })
187
+ ).toHex();
188
+ const unsigned = {
189
+ address,
190
+ blockHash,
191
+ blockNumber,
192
+ era,
193
+ genesisHash,
194
+ method,
195
+ nonce,
196
+ signedExtensions: registry.signedExtensions,
197
+ specVersion,
198
+ tip,
199
+ transactionVersion,
200
+ version: EXTRINSIC_VERSION,
201
+ };
202
+ return {
203
+ registry,
204
+ unsigned,
205
+ };
206
+ };
@@ -0,0 +1,22 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import type { Transaction } from "./types";
3
+
4
+ /**
5
+ * Create an empty transaction
6
+ *
7
+ * @returns {Transaction}
8
+ */
9
+ const createTransaction = (): Transaction => ({
10
+ family: "polkadot",
11
+ mode: "send",
12
+ amount: new BigNumber(0),
13
+ recipient: "",
14
+ useAllAmount: false,
15
+ fees: null,
16
+ validators: [],
17
+ era: null,
18
+ rewardDestination: null,
19
+ numSlashingSpans: Number(0),
20
+ });
21
+
22
+ export default createTransaction;
@@ -0,0 +1,37 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import type { AccountLike, Account } from "@ledgerhq/types-live";
3
+ import { getMainAccount } from "@ledgerhq/coin-framework/account/index";
4
+ import type { PolkadotAccount, Transaction } from "./types";
5
+ import { calculateAmount } from "./logic";
6
+ import getEstimatedFees from "./js-getFeesForTransaction";
7
+ import createTransaction from "./js-createTransaction";
8
+ import { loadPolkadotCrypto } from "./polkadot-crypto";
9
+
10
+ /**
11
+ * Returns the maximum possible amount for transaction
12
+ *
13
+ * @param {Object} param - the account, parentAccount and transaction
14
+ */
15
+ const estimateMaxSpendable = async ({
16
+ account,
17
+ parentAccount,
18
+ transaction,
19
+ }: {
20
+ account: AccountLike;
21
+ parentAccount: Account | null | undefined;
22
+ transaction: Transaction | null | undefined;
23
+ }): Promise<BigNumber> => {
24
+ await loadPolkadotCrypto();
25
+ const a = getMainAccount(account, parentAccount) as PolkadotAccount;
26
+ const t = { ...createTransaction(), ...transaction, useAllAmount: true };
27
+ const fees = await getEstimatedFees({
28
+ a,
29
+ t,
30
+ });
31
+ return calculateAmount({
32
+ a,
33
+ t: { ...t, fees },
34
+ });
35
+ };
36
+
37
+ export default estimateMaxSpendable;
@@ -0,0 +1,41 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets";
3
+ import type { PolkadotAccount, Transaction } from "./types";
4
+ import { getPaymentInfo } from "./cache";
5
+ import { calculateAmount } from "./logic";
6
+ import { buildTransaction } from "./js-buildTransaction";
7
+ import { fakeSignExtrinsic } from "./js-signOperation";
8
+
9
+ /**
10
+ * Fetch the transaction fees for a transaction
11
+ *
12
+ * @param {Account} a
13
+ * @param {Transaction} t
14
+ */
15
+ const getEstimatedFees = async ({
16
+ a,
17
+ t,
18
+ }: {
19
+ a: PolkadotAccount;
20
+ t: Transaction;
21
+ }): Promise<BigNumber> => {
22
+ const transaction = {
23
+ ...t,
24
+ recipient: getAbandonSeedAddress(a.currency.id),
25
+ // Always use a fake recipient to estimate fees
26
+ amount: calculateAmount({
27
+ a,
28
+ t: { ...t, fees: new BigNumber(0) },
29
+ }), // Remove fees if present since we are fetching fees
30
+ };
31
+ const { unsigned, registry } = await buildTransaction(a, transaction);
32
+ const fakeSignedTx = await fakeSignExtrinsic(unsigned, registry);
33
+ const payment = await getPaymentInfo({
34
+ a,
35
+ t: transaction,
36
+ signedTx: fakeSignedTx,
37
+ });
38
+ return new BigNumber(payment.partialFee);
39
+ };
40
+
41
+ export default getEstimatedFees;
@@ -0,0 +1,350 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import {
3
+ NotEnoughBalance,
4
+ RecipientRequired,
5
+ InvalidAddress,
6
+ InvalidAddressBecauseDestinationIsAlsoSource,
7
+ AmountRequired,
8
+ NotEnoughBalanceBecauseDestinationNotCreated,
9
+ FeeNotLoaded,
10
+ } from "@ledgerhq/errors";
11
+ import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
12
+ import type { PolkadotAccount, Transaction, TransactionStatus } from "./types";
13
+ import {
14
+ PolkadotUnauthorizedOperation,
15
+ PolkadotElectionClosed,
16
+ PolkadotNotValidator,
17
+ PolkadotNoUnlockedBalance,
18
+ PolkadotNoNominations,
19
+ PolkadotAllFundsWarning,
20
+ PolkadotBondMinimumAmount,
21
+ PolkadotBondMinimumAmountWarning,
22
+ PolkadotMaxUnbonding,
23
+ PolkadotValidatorsRequired,
24
+ PolkadotDoMaxSendInstead,
25
+ } from "./errors";
26
+ import { verifyValidatorAddresses } from "./api";
27
+ import {
28
+ EXISTENTIAL_DEPOSIT,
29
+ FEES_SAFETY_BUFFER,
30
+ isFirstBond,
31
+ isController,
32
+ isStash,
33
+ hasLockedBalance,
34
+ hasMaxUnlockings,
35
+ calculateAmount,
36
+ getMinimumAmountToBond,
37
+ getMinimumBalance,
38
+ EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN,
39
+ } from "./logic";
40
+ import { isValidAddress } from "./address";
41
+ import { getCurrentPolkadotPreloadData } from "./preload";
42
+ import { isControllerAddress, isNewAccount, isElectionClosed } from "./cache";
43
+ import { loadPolkadotCrypto } from "./polkadot-crypto";
44
+
45
+ // Should try to refacto
46
+ const getSendTransactionStatus = async (
47
+ a: PolkadotAccount,
48
+ t: Transaction
49
+ ): Promise<TransactionStatus> => {
50
+ const errors: any = {};
51
+ const warnings: any = {};
52
+
53
+ if (!t.fees) {
54
+ errors.fees = new FeeNotLoaded();
55
+ }
56
+
57
+ if (!t.recipient) {
58
+ errors.recipient = new RecipientRequired("");
59
+ } else if (a.freshAddress === t.recipient) {
60
+ errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
61
+ } else if (!isValidAddress(t.recipient)) {
62
+ errors.recipient = new InvalidAddress("", {
63
+ currencyName: a.currency.name,
64
+ });
65
+ }
66
+
67
+ const estimatedFees = t.fees || new BigNumber(0);
68
+ const amount = calculateAmount({
69
+ a,
70
+ t,
71
+ });
72
+ const totalSpent = amount.plus(estimatedFees);
73
+
74
+ if (amount.lte(0) && !t.useAllAmount) {
75
+ errors.amount = new AmountRequired();
76
+ }
77
+
78
+ const minimumBalanceExistential = getMinimumBalance(a);
79
+ const leftover = a.spendableBalance.minus(totalSpent);
80
+
81
+ if (
82
+ minimumBalanceExistential.gt(0) &&
83
+ leftover.lt(minimumBalanceExistential) &&
84
+ leftover.gt(0)
85
+ ) {
86
+ errors.amount = new PolkadotDoMaxSendInstead("", {
87
+ minimumBalance: formatCurrencyUnit(
88
+ a.currency.units[0],
89
+ EXISTENTIAL_DEPOSIT,
90
+ { showCode: true }
91
+ ),
92
+ });
93
+ } else if (
94
+ !errors.amount &&
95
+ !t.useAllAmount &&
96
+ a.spendableBalance.lte(
97
+ EXISTENTIAL_DEPOSIT.plus(EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN)
98
+ )
99
+ ) {
100
+ errors.amount = new NotEnoughBalance();
101
+ } else if (totalSpent.gt(a.spendableBalance)) {
102
+ errors.amount = new NotEnoughBalance();
103
+ }
104
+
105
+ if (
106
+ !errors.amount &&
107
+ a.polkadotResources?.lockedBalance.gt(0) &&
108
+ (t.useAllAmount ||
109
+ a.spendableBalance.minus(totalSpent).lt(FEES_SAFETY_BUFFER))
110
+ ) {
111
+ warnings.amount = new PolkadotAllFundsWarning();
112
+ }
113
+
114
+ if (
115
+ !errors.recipient &&
116
+ amount.lt(EXISTENTIAL_DEPOSIT) &&
117
+ (await isNewAccount(t.recipient))
118
+ ) {
119
+ errors.amount = new NotEnoughBalanceBecauseDestinationNotCreated("", {
120
+ minimalAmount: formatCurrencyUnit(
121
+ a.currency.units[0],
122
+ EXISTENTIAL_DEPOSIT,
123
+ {
124
+ showCode: true,
125
+ }
126
+ ),
127
+ });
128
+ }
129
+
130
+ return Promise.resolve({
131
+ errors,
132
+ warnings,
133
+ estimatedFees,
134
+ amount: amount.lt(0) ? new BigNumber(0) : amount,
135
+ totalSpent,
136
+ family: t.family,
137
+ });
138
+ };
139
+
140
+ const getTransactionStatus = async (a: PolkadotAccount, t: Transaction) => {
141
+ await loadPolkadotCrypto();
142
+
143
+ const errors: {
144
+ staking?: Error;
145
+ amount?: Error;
146
+ recipient?: Error;
147
+ unbondings?: Error;
148
+ } = {};
149
+ const warnings: {
150
+ amount?: Error;
151
+ } = {};
152
+ const preloaded = getCurrentPolkadotPreloadData();
153
+ const { staking, validators } = preloaded;
154
+ const minimumBondBalance = new BigNumber(preloaded.minimumBondBalance);
155
+
156
+ if (t.mode === "send") {
157
+ return await getSendTransactionStatus(a, t);
158
+ }
159
+
160
+ if (
161
+ (staking && !staking.electionClosed) || // Preloaded
162
+ (!staking && (await !isElectionClosed())) // Fallback
163
+ ) {
164
+ errors.staking = new PolkadotElectionClosed();
165
+ }
166
+
167
+ const amount = calculateAmount({
168
+ a,
169
+ t,
170
+ });
171
+ const unlockingBalance =
172
+ a.polkadotResources?.unlockingBalance || new BigNumber(0);
173
+ const unlockedBalance =
174
+ a.polkadotResources?.unlockedBalance || new BigNumber(0);
175
+ const currentBonded =
176
+ a.polkadotResources?.lockedBalance.minus(unlockingBalance) ||
177
+ new BigNumber(0);
178
+
179
+ const minimumAmountToBond = getMinimumAmountToBond(a, minimumBondBalance);
180
+
181
+ switch (t.mode) {
182
+ case "bond":
183
+ if (amount.lt(minimumAmountToBond)) {
184
+ errors.amount = new PolkadotBondMinimumAmount("", {
185
+ minimumBondAmount: formatCurrencyUnit(
186
+ a.currency.units[0],
187
+ minimumAmountToBond,
188
+ { showCode: true }
189
+ ),
190
+ });
191
+ }
192
+
193
+ if (isFirstBond(a)) {
194
+ // Not a stash yet -> bond method sets the controller
195
+ if (!t.recipient) {
196
+ errors.recipient = new RecipientRequired("");
197
+ } else if (!isValidAddress(t.recipient)) {
198
+ errors.recipient = new InvalidAddress("", {
199
+ currencyName: a.currency.name,
200
+ });
201
+ } else if (await isControllerAddress(t.recipient)) {
202
+ errors.recipient = new PolkadotUnauthorizedOperation(
203
+ "Recipient is already a controller"
204
+ );
205
+ }
206
+ }
207
+
208
+ break;
209
+
210
+ case "unbond":
211
+ if (!isController(a) || !hasLockedBalance(a)) {
212
+ errors.staking = new PolkadotUnauthorizedOperation();
213
+ }
214
+
215
+ if (hasMaxUnlockings(a)) {
216
+ errors.unbondings = new PolkadotMaxUnbonding();
217
+ }
218
+
219
+ if (amount.lte(0)) {
220
+ errors.amount = new AmountRequired();
221
+ } else if (
222
+ amount.gt(currentBonded.minus(minimumBondBalance)) &&
223
+ amount.lt(currentBonded)
224
+ ) {
225
+ warnings.amount = new PolkadotBondMinimumAmountWarning("", {
226
+ minimumBondBalance: formatCurrencyUnit(
227
+ a.currency.units[0],
228
+ minimumBondBalance,
229
+ { showCode: true }
230
+ ),
231
+ });
232
+ } else if (amount.gt(currentBonded)) {
233
+ errors.amount = new NotEnoughBalance();
234
+ }
235
+
236
+ break;
237
+
238
+ case "rebond":
239
+ if (!isController(a)) {
240
+ errors.staking = new PolkadotUnauthorizedOperation();
241
+ }
242
+
243
+ if (amount.lte(0)) {
244
+ errors.amount = new AmountRequired();
245
+ } else if (amount.gt(unlockingBalance)) {
246
+ errors.amount = new NotEnoughBalance();
247
+ } else if (amount.lt(minimumAmountToBond)) {
248
+ warnings.amount = new PolkadotBondMinimumAmountWarning("", {
249
+ minimumBondBalance: formatCurrencyUnit(
250
+ a.currency.units[0],
251
+ minimumBondBalance,
252
+ { showCode: true }
253
+ ),
254
+ });
255
+ }
256
+
257
+ break;
258
+
259
+ case "withdrawUnbonded":
260
+ if (!isController(a)) {
261
+ errors.staking = new PolkadotUnauthorizedOperation();
262
+ }
263
+
264
+ if (unlockedBalance.lte(0)) {
265
+ errors.amount = new PolkadotNoUnlockedBalance();
266
+ }
267
+
268
+ break;
269
+
270
+ case "nominate":
271
+ if (!isController(a)) {
272
+ errors.staking = new PolkadotUnauthorizedOperation();
273
+ } else if (!t.validators || t.validators?.length === 0) {
274
+ errors.staking = new PolkadotValidatorsRequired();
275
+ } else {
276
+ if (validators && validators.length) {
277
+ // Validate directly with preloaded data
278
+ const notValidators = t.validators?.filter(
279
+ (address) => !validators.find((v) => v.address === address)
280
+ );
281
+
282
+ if (notValidators && notValidators.length) {
283
+ errors.staking = new PolkadotNotValidator(undefined, {
284
+ validators: notValidators,
285
+ });
286
+ }
287
+ } else {
288
+ // Fallback with api call
289
+ const notValidators = await verifyValidatorAddresses(
290
+ t.validators || []
291
+ );
292
+
293
+ if (notValidators.length) {
294
+ errors.staking = new PolkadotNotValidator(undefined, {
295
+ validators: notValidators,
296
+ });
297
+ break;
298
+ }
299
+ }
300
+ }
301
+
302
+ break;
303
+
304
+ case "chill":
305
+ if (!isController(a)) {
306
+ errors.staking = new PolkadotUnauthorizedOperation();
307
+ } else if (!a.polkadotResources?.nominations) {
308
+ errors.staking = new PolkadotNoNominations();
309
+ }
310
+
311
+ break;
312
+
313
+ case "setController":
314
+ if (!isStash(a)) {
315
+ errors.staking = new PolkadotUnauthorizedOperation();
316
+ }
317
+ break;
318
+ }
319
+
320
+ const estimatedFees = t.fees || new BigNumber(0);
321
+ const totalSpent =
322
+ t.mode === "bond" ? amount.plus(estimatedFees) : estimatedFees;
323
+
324
+ if (t.mode === "bond" || t.mode === "unbond" || t.mode === "rebond") {
325
+ if (amount.lte(0)) {
326
+ errors.amount = new AmountRequired();
327
+ }
328
+ }
329
+
330
+ if (
331
+ t.mode === "bond" &&
332
+ a.spendableBalance.minus(totalSpent).lt(FEES_SAFETY_BUFFER)
333
+ ) {
334
+ errors.amount = new NotEnoughBalance();
335
+ }
336
+
337
+ if (totalSpent.gt(a.spendableBalance)) {
338
+ errors.amount = new NotEnoughBalance();
339
+ }
340
+
341
+ return Promise.resolve({
342
+ errors,
343
+ warnings,
344
+ estimatedFees,
345
+ amount: amount.lt(0) ? new BigNumber(0) : amount,
346
+ totalSpent,
347
+ });
348
+ };
349
+
350
+ export default getTransactionStatus;
@@ -0,0 +1,27 @@
1
+ import type { PolkadotAccount, Transaction } from "./types";
2
+ import getEstimatedFees from "./js-getFeesForTransaction";
3
+ import { loadPolkadotCrypto } from "./polkadot-crypto";
4
+
5
+ const sameFees = (a, b) => (!a || !b ? a === b : a.eq(b));
6
+
7
+ /**
8
+ * Calculate fees for the current transaction
9
+ * @param {PolkadotAccount} a
10
+ * @param {Transaction} t
11
+ */
12
+ const prepareTransaction = async (a: PolkadotAccount, t: Transaction) => {
13
+ await loadPolkadotCrypto();
14
+ let fees = t.fees;
15
+ fees = await getEstimatedFees({
16
+ a,
17
+ t,
18
+ });
19
+
20
+ if (!sameFees(t.fees, fees)) {
21
+ return { ...t, fees };
22
+ }
23
+
24
+ return t;
25
+ };
26
+
27
+ export default prepareTransaction;