@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
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@ledgerhq/coin-polkadot",
3
+ "version": "0.0.2",
4
+ "description": "Ledger Polkadot Coin integration",
5
+ "keywords": [
6
+ "Ledger",
7
+ "LedgerWallet",
8
+ "dot",
9
+ "Polkadot",
10
+ "Hardware Wallet"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/LedgerHQ/ledger-live.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/LedgerHQ/ledger-live/issues"
18
+ },
19
+ "homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/coin-polkadot",
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "main": "lib/index.js",
24
+ "module": "lib-es/index.js",
25
+ "types": "lib/index.d.ts",
26
+ "typesVersions": {
27
+ "*": {
28
+ "lib/*": [
29
+ "lib/*"
30
+ ],
31
+ "lib-es/*": [
32
+ "lib-es/*"
33
+ ],
34
+ "*": [
35
+ "lib/*"
36
+ ]
37
+ }
38
+ },
39
+ "exports": {
40
+ "./lib/*": "./lib/*.js",
41
+ "./lib-es/*": "./lib-es/*.js",
42
+ "./*": {
43
+ "require": "./lib/*.js",
44
+ "default": "./lib-es/*.js"
45
+ },
46
+ "./package.json": "./package.json"
47
+ },
48
+ "license": "Apache-2.0",
49
+ "dependencies": {
50
+ "@ledgerhq/live-app-sdk": "^0.8.1",
51
+ "@polkadot/types": "9.8.1",
52
+ "@polkadot/types-known": "9.8.1",
53
+ "@polkadot/util": "10.3.1",
54
+ "@polkadot/util-crypto": "10.3.1",
55
+ "bignumber.js": "^9.1.0",
56
+ "expect": "^27.4.6",
57
+ "invariant": "^2.2.2",
58
+ "lodash": "^4.17.21",
59
+ "rxjs": "^6.6.7",
60
+ "@ledgerhq/coin-framework": "^0.0.2",
61
+ "@ledgerhq/cryptoassets": "^9.0.0",
62
+ "@ledgerhq/devices": "^8.0.0",
63
+ "@ledgerhq/errors": "^6.12.3",
64
+ "@ledgerhq/hw-app-polkadot": "^6.27.12",
65
+ "@ledgerhq/logs": "^6.10.1",
66
+ "@ledgerhq/types-cryptoassets": "^7.0.0",
67
+ "@ledgerhq/types-live": "^6.31.0"
68
+ },
69
+ "devDependencies": {
70
+ "@types/jest": "^29.2.4",
71
+ "@typescript-eslint/eslint-plugin": "^5.46.1",
72
+ "@typescript-eslint/parser": "^5.46.1",
73
+ "eslint": "^7.32.0",
74
+ "eslint-config-prettier": "^8.3.0",
75
+ "eslint-config-typescript": "^3.0.0",
76
+ "eslint-formatter-pretty": "^3.0.1",
77
+ "eslint-plugin-prettier": "^3.4.0",
78
+ "eslint-plugin-typescript": "^0.14.0",
79
+ "jest": "^28.1.1",
80
+ "prettier": "^2.8.1",
81
+ "ts-jest": "^28.0.5",
82
+ "typescript": "^4.9.4"
83
+ },
84
+ "gitHead": "dd0dea64b58e5a9125c8a422dcffd29e5ef6abec",
85
+ "scripts": {
86
+ "clean": "rimraf lib lib-es",
87
+ "build": "tsc && tsc -m ES6 --outDir lib-es",
88
+ "prewatch": "pnpm build",
89
+ "watch": "tsc --watch",
90
+ "doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
91
+ "lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx",
92
+ "lint:fix": "pnpm lint --fix",
93
+ "test": "jest"
94
+ }
95
+ }
package/src/account.ts ADDED
@@ -0,0 +1,170 @@
1
+ import invariant from "invariant";
2
+ import { BigNumber } from "bignumber.js";
3
+ import type { Operation } from "@ledgerhq/types-live";
4
+ import { getAccountUnit } from "@ledgerhq/coin-framework/account/index";
5
+ import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
6
+ import { PolkadotAccount, PolkadotResources } from "./types";
7
+ import type { Unit } from "@ledgerhq/types-cryptoassets";
8
+
9
+ function formatOperationSpecifics(
10
+ op: Operation,
11
+ unit: Unit | null | undefined
12
+ ): string {
13
+ const {
14
+ validators,
15
+ bondedAmount,
16
+ unbondedAmount,
17
+ withdrawUnbondedAmount,
18
+ validatorStash,
19
+ amount,
20
+ } = op.extra;
21
+ let str = (validators || []).map((v) => `\n ${v}`).join("");
22
+ const formatConfig = {
23
+ disableRounding: true,
24
+ alwaysShowSign: false,
25
+ showCode: true,
26
+ };
27
+ str +=
28
+ bondedAmount && !bondedAmount.isNaN()
29
+ ? `\n bondedAmount: ${
30
+ unit
31
+ ? formatCurrencyUnit(unit, bondedAmount, formatConfig)
32
+ : bondedAmount
33
+ }`
34
+ : unbondedAmount && !unbondedAmount.isNaN()
35
+ ? `\n unbondedAmount: ${
36
+ unit
37
+ ? formatCurrencyUnit(unit, unbondedAmount, formatConfig)
38
+ : unbondedAmount
39
+ }`
40
+ : withdrawUnbondedAmount && !withdrawUnbondedAmount.isNaN()
41
+ ? `\n withdrawUnbondedAmount: ${
42
+ unit
43
+ ? formatCurrencyUnit(unit, withdrawUnbondedAmount, formatConfig)
44
+ : withdrawUnbondedAmount
45
+ }`
46
+ : "";
47
+ str += validatorStash ? `\n validatorStash: ${validatorStash}` : "";
48
+ str += amount
49
+ ? `\n amount: ${
50
+ unit ? formatCurrencyUnit(unit, amount, formatConfig) : amount
51
+ }`
52
+ : "";
53
+ return str;
54
+ }
55
+
56
+ function formatAccountSpecifics(account: PolkadotAccount): string {
57
+ const { polkadotResources } = account;
58
+ invariant(polkadotResources, "polkadot account expected");
59
+ const unit = getAccountUnit(account);
60
+ const formatConfig = {
61
+ disableRounding: true,
62
+ alwaysShowSign: false,
63
+ showCode: true,
64
+ };
65
+ let str = " ";
66
+ str +=
67
+ formatCurrencyUnit(unit, account.spendableBalance, formatConfig) +
68
+ " spendable. ";
69
+
70
+ if ((polkadotResources as PolkadotResources).lockedBalance.gt(0)) {
71
+ str +=
72
+ formatCurrencyUnit(
73
+ unit,
74
+ (polkadotResources as PolkadotResources).lockedBalance,
75
+ formatConfig
76
+ ) + " locked. ";
77
+ }
78
+
79
+ if ((polkadotResources as PolkadotResources).unlockedBalance.gt(0)) {
80
+ str +=
81
+ formatCurrencyUnit(
82
+ unit,
83
+ (polkadotResources as PolkadotResources).unlockedBalance,
84
+ formatConfig
85
+ ) + " unlocked. ";
86
+ }
87
+
88
+ if ((polkadotResources as PolkadotResources).stash) {
89
+ str += "\nstash : " + (polkadotResources as PolkadotResources).stash;
90
+ }
91
+
92
+ if ((polkadotResources as PolkadotResources).controller) {
93
+ str +=
94
+ "\ncontroller : " + (polkadotResources as PolkadotResources).controller;
95
+ }
96
+
97
+ if ((polkadotResources as PolkadotResources).nominations?.length) {
98
+ str += "\nNominations\n";
99
+ str += ((polkadotResources as PolkadotResources).nominations as any[])
100
+ .map((v) => ` to ${v.address}`)
101
+ .join("\n");
102
+ }
103
+
104
+ return str;
105
+ }
106
+
107
+ export function fromOperationExtraRaw(
108
+ extra: Record<string, any> | null | undefined
109
+ ) {
110
+ if (extra && extra.transferAmount) {
111
+ extra = { ...extra, transferAmount: extra.transferAmount.toString() };
112
+ }
113
+
114
+ if (extra && extra.bondedAmount) {
115
+ return { ...extra, bondedAmount: new BigNumber(extra.bondedAmount) };
116
+ }
117
+
118
+ if (extra && extra.unbondedAmount) {
119
+ return { ...extra, unbondedAmount: new BigNumber(extra.unbondedAmount) };
120
+ }
121
+
122
+ if (extra && extra.withdrawUnbondedAmount) {
123
+ return {
124
+ ...extra,
125
+ withdrawUnbondedAmount: new BigNumber(extra.withdrawUnbondedAmount),
126
+ };
127
+ }
128
+
129
+ // for subscan reward & slash
130
+ if (extra && extra.amount) {
131
+ return { ...extra, amount: new BigNumber(extra.amount) };
132
+ }
133
+
134
+ return extra;
135
+ }
136
+ export function toOperationExtraRaw(
137
+ extra: Record<string, any> | null | undefined
138
+ ) {
139
+ if (extra && extra.transferAmount) {
140
+ extra = { ...extra, transferAmount: extra.transferAmount.toString() };
141
+ }
142
+
143
+ if (extra && extra.bondedAmount) {
144
+ return { ...extra, bondedAmount: extra.bondedAmount.toString() };
145
+ }
146
+
147
+ if (extra && extra.unbondedAmount) {
148
+ return { ...extra, unbondedAmount: extra.unbondedAmount.toString() };
149
+ }
150
+
151
+ if (extra && extra.withdrawUnbondedAmount) {
152
+ return {
153
+ ...extra,
154
+ withdrawUnbondedAmount: extra.withdrawUnbondedAmount.toString(),
155
+ };
156
+ }
157
+
158
+ // for subscan reward & slash
159
+ if (extra && extra.amount) {
160
+ return { ...extra, amount: extra.amount.toString() };
161
+ }
162
+
163
+ return extra;
164
+ }
165
+ export default {
166
+ formatAccountSpecifics,
167
+ formatOperationSpecifics,
168
+ fromOperationExtraRaw,
169
+ toOperationExtraRaw,
170
+ };
package/src/address.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { encodeAddress, decodeAddress } from "@polkadot/util-crypto";
2
+ import { hexToU8a, isHex } from "@polkadot/util";
3
+
4
+ const POLKADOT_SS58_PREFIX = 0;
5
+
6
+ /**
7
+ * Returns true if address is valid, false if it's invalid (can't parse or wrong checksum)
8
+ *
9
+ * @param {*} address
10
+ */
11
+ // TODO Cache this to improve perf
12
+ export const isValidAddress = (
13
+ address: string,
14
+ ss58Format: number = POLKADOT_SS58_PREFIX
15
+ ): boolean => {
16
+ if (!address) return false;
17
+
18
+ try {
19
+ encodeAddress(
20
+ isHex(address)
21
+ ? hexToU8a(address)
22
+ : decodeAddress(address, false, ss58Format)
23
+ );
24
+ return true;
25
+ } catch (err) {
26
+ return false;
27
+ }
28
+ };
@@ -0,0 +1,343 @@
1
+ import network from "@ledgerhq/coin-framework/network";
2
+ import querystring from "querystring";
3
+ import { BigNumber } from "bignumber.js";
4
+ import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
5
+ import { getEnv } from "@ledgerhq/coin-framework/env";
6
+ import { getOperationType } from "./common";
7
+ import type { OperationType, Operation } from "@ledgerhq/types-live";
8
+ import { isValidAddress } from "../address";
9
+
10
+ const LIMIT = 200;
11
+
12
+ /**
13
+ * Return the url of the indexer
14
+ *
15
+ * @returns {string}
16
+ */
17
+ const getBaseApiUrl = (): string => getEnv("API_POLKADOT_INDEXER");
18
+
19
+ /**
20
+ * Fetch operation lists from indexer
21
+ *
22
+ * @param {string} addr
23
+ * @param {number} offset
24
+ * @param {number} startAt
25
+ * @param {number} limit
26
+ *
27
+ * @returns {string}
28
+ */
29
+ const getAccountOperationUrl = (
30
+ addr: string,
31
+ offset: number,
32
+ startAt: number,
33
+ limit: number = LIMIT
34
+ ): string =>
35
+ `${getBaseApiUrl()}/accounts/${addr}/operations?${querystring.stringify({
36
+ limit,
37
+ offset,
38
+ startAt,
39
+ })}`;
40
+
41
+ const getWithdrawUnbondedAmount = (extrinsic) => {
42
+ return (
43
+ extrinsic?.staking?.eventStaking.reduce((acc, curr) => {
44
+ return curr.method === "Withdrawn"
45
+ ? new BigNumber(acc).plus(curr.value)
46
+ : new BigNumber(0);
47
+ }, new BigNumber(0)) || new BigNumber(0)
48
+ );
49
+ };
50
+
51
+ const getController = (_extrinsic) => {
52
+ // TODO: ask BisonTrails to provide the info
53
+ return "";
54
+ };
55
+
56
+ /**
57
+ * add Extra info for operation details
58
+ *
59
+ * @param {OperationType} type
60
+ * @param {*} extrinsic
61
+ *
62
+ * @returns {Object}
63
+ */
64
+ const getExtra = (type: OperationType, extrinsic: any): Record<string, any> => {
65
+ let extra: {
66
+ transferAmount?: BigNumber;
67
+ palletMethod: string;
68
+ bondedAmount?: BigNumber;
69
+ unbondedAmount?: BigNumber;
70
+ amount?: BigNumber;
71
+ withdrawUnbondedAmount?: any;
72
+ validatorStash?: any;
73
+ validators?: any;
74
+ controller?: any;
75
+ } = {
76
+ palletMethod: `${extrinsic.section}.${extrinsic.method}`,
77
+ };
78
+
79
+ switch (type) {
80
+ case "IN":
81
+ case "OUT":
82
+ if (extrinsic.amount || extrinsic.amount === 0) {
83
+ extra = { ...extra, transferAmount: new BigNumber(extrinsic.amount) };
84
+ }
85
+
86
+ break;
87
+
88
+ case "BOND":
89
+ if (extrinsic.amount || extrinsic.amount === 0) {
90
+ extra = { ...extra, bondedAmount: new BigNumber(extrinsic.amount) };
91
+ }
92
+
93
+ break;
94
+
95
+ case "UNBOND":
96
+ if (extrinsic.amount || extrinsic.amount === 0) {
97
+ extra = { ...extra, unbondedAmount: new BigNumber(extrinsic.amount) };
98
+ }
99
+
100
+ break;
101
+
102
+ case "WITHDRAW_UNBONDED":
103
+ extra = {
104
+ ...extra,
105
+ withdrawUnbondedAmount: getWithdrawUnbondedAmount(extrinsic),
106
+ };
107
+ break;
108
+
109
+ case "SET_CONTROLLER":
110
+ extra = {
111
+ ...extra,
112
+ controller: getController(extrinsic),
113
+ };
114
+ break;
115
+
116
+ case "REWARD_PAYOUT":
117
+ case "SLASH":
118
+ extra = {
119
+ ...extra,
120
+ validatorStash: extrinsic.validatorStash,
121
+ amount: new BigNumber(extrinsic.value),
122
+ };
123
+ break;
124
+
125
+ case "NOMINATE":
126
+ extra = {
127
+ ...extra,
128
+ validators:
129
+ extrinsic.staking?.validators?.reduce((acc, current) => {
130
+ return [...acc, current.address];
131
+ }, []) ?? [],
132
+ };
133
+ break;
134
+ }
135
+
136
+ return extra;
137
+ };
138
+
139
+ /**
140
+ * Returns the operation value (amount if relevant + fees) depending on type
141
+ *
142
+ * @param {*} extrinsic
143
+ * @param {OperationType} type
144
+ *
145
+ * @returns {BigNumber}
146
+ */
147
+ const getValue = (extrinsic, type: OperationType): BigNumber => {
148
+ if (!extrinsic.isSuccess) {
149
+ return type === "IN"
150
+ ? new BigNumber(0)
151
+ : new BigNumber(extrinsic.partialFee || 0);
152
+ }
153
+
154
+ switch (type) {
155
+ case "OUT":
156
+ return extrinsic.signer !== extrinsic.affectedAddress1
157
+ ? new BigNumber(extrinsic.amount).plus(extrinsic.partialFee)
158
+ : new BigNumber(extrinsic.partialFee);
159
+
160
+ case "IN":
161
+ case "SLASH":
162
+ return new BigNumber(extrinsic.amount);
163
+
164
+ default:
165
+ return new BigNumber(extrinsic.partialFee);
166
+ }
167
+ };
168
+
169
+ /**
170
+ * Map extrinsic into the live operation type
171
+ *
172
+ * @param {string} addr
173
+ * @param {string} accountId
174
+ * @param {*} extrinsic
175
+ *
176
+ * @returns {Operation | null}
177
+ */
178
+ const extrinsicToOperation = (
179
+ addr: string,
180
+ accountId: string,
181
+ extrinsic
182
+ ): Partial<Operation> | null => {
183
+ let type = getOperationType(extrinsic.section, extrinsic.method);
184
+
185
+ if (
186
+ type === "OUT" &&
187
+ extrinsic.affectedAddress1 === addr &&
188
+ extrinsic.signer !== addr
189
+ ) {
190
+ type = "IN";
191
+ }
192
+
193
+ if (type !== "IN" && extrinsic.signer !== addr) {
194
+ return null;
195
+ }
196
+
197
+ return {
198
+ id: encodeOperationId(accountId, extrinsic.hash, type),
199
+ accountId,
200
+ fee: new BigNumber(extrinsic.partialFee || 0),
201
+ value: getValue(extrinsic, type),
202
+ type,
203
+ hash: extrinsic.hash,
204
+ blockHeight: extrinsic.blockNumber,
205
+ date: new Date(extrinsic.timestamp),
206
+ extra: getExtra(type, extrinsic),
207
+ senders: [extrinsic.signer],
208
+ recipients: [extrinsic.affectedAddress1, extrinsic.affectedAddress2]
209
+ .filter(Boolean)
210
+ .filter(isValidAddress),
211
+ transactionSequenceNumber:
212
+ extrinsic.signer === addr ? extrinsic.nonce : undefined,
213
+ hasFailed: !extrinsic.isSuccess,
214
+ };
215
+ };
216
+
217
+ /**
218
+ * Map reward to live operation type
219
+ *
220
+ * @param {string} addr
221
+ * @param {string} accountId
222
+ * @param {*} reward
223
+ *
224
+ * @returns {Operation | null}
225
+ */
226
+ const rewardToOperation = (
227
+ addr: string,
228
+ accountId: string,
229
+ reward
230
+ ): Partial<Operation> => {
231
+ const hash = reward.extrinsicHash;
232
+ const type = "REWARD_PAYOUT";
233
+ return {
234
+ id: encodeOperationId(accountId, `${hash}+${reward.index}`, type),
235
+ accountId,
236
+ fee: new BigNumber(0),
237
+ value: new BigNumber(reward.value),
238
+ type: type,
239
+ hash,
240
+ blockHeight: reward.blockNumber,
241
+ date: new Date(reward.timestamp),
242
+ extra: getExtra(type, reward),
243
+ senders: [reward.validatorStash].filter(Boolean),
244
+ recipients: [reward.accountId].filter(Boolean),
245
+ };
246
+ };
247
+
248
+ /**
249
+ * Map slash to live operation type
250
+ *
251
+ * @param {string} addr
252
+ * @param {string} accountId
253
+ * @param {*} slash
254
+ *
255
+ * @returns {Operation | null}
256
+ */
257
+ const slashToOperation = (
258
+ addr: string,
259
+ accountId: string,
260
+ slash
261
+ ): Partial<Operation> => {
262
+ const hash = `${slash.blockNumber}`;
263
+ const type = "SLASH";
264
+ return {
265
+ id: encodeOperationId(accountId, `${hash}+${slash.index}`, type),
266
+ accountId,
267
+ fee: new BigNumber(0),
268
+ value: new BigNumber(slash.value),
269
+ type: type,
270
+ hash: hash,
271
+ blockHeight: slash.blockNumber,
272
+ senders: [slash.validatorStash].filter(Boolean),
273
+ recipients: [slash.accountId].filter(Boolean),
274
+ date: new Date(slash.timestamp),
275
+ extra: getExtra(type, slash),
276
+ };
277
+ };
278
+
279
+ /**
280
+ * Fetch loop with multiple operations per page / offsite / start
281
+ *
282
+ * @param {string} accountId
283
+ * @param {string} addr
284
+ * @param {number} startAt
285
+ * @param {number} offset
286
+ *
287
+ * @param {Operation[]} prevOperations
288
+ */
289
+ const fetchOperationList = async (
290
+ accountId: string,
291
+ addr: string,
292
+ startAt: number,
293
+ offset = 0,
294
+ prevOperations: Operation[] = []
295
+ ) => {
296
+ const { data } = await network({
297
+ method: "GET",
298
+ url: getAccountOperationUrl(addr, offset, startAt),
299
+ });
300
+ const operations = data.extrinsics.map((extrinsic) =>
301
+ extrinsicToOperation(addr, accountId, extrinsic)
302
+ );
303
+ const rewards = data.rewards.map((reward) =>
304
+ rewardToOperation(addr, accountId, reward)
305
+ );
306
+ const slashes = data.slashes.map((slash) =>
307
+ slashToOperation(addr, accountId, slash)
308
+ );
309
+ const mergedOp = [...prevOperations, ...operations, ...rewards, ...slashes];
310
+
311
+ if (
312
+ operations.length < LIMIT &&
313
+ rewards.length < LIMIT &&
314
+ slashes.length < LIMIT
315
+ ) {
316
+ return mergedOp.filter(Boolean).sort((a, b) => b.date - a.date);
317
+ }
318
+
319
+ return await fetchOperationList(
320
+ accountId,
321
+ addr,
322
+ startAt,
323
+ offset + LIMIT,
324
+ mergedOp
325
+ );
326
+ };
327
+
328
+ /**
329
+ * Fetch all operations for a single account from indexer
330
+ *
331
+ * @param {string} accountId
332
+ * @param {string} addr
333
+ * @param {number} startAt - blockHeight after which you fetch this op (included)
334
+ *
335
+ * @return {Operation[]}
336
+ */
337
+ export const getOperations = async (
338
+ accountId: string,
339
+ addr: string,
340
+ startAt = 0
341
+ ) => {
342
+ return await fetchOperationList(accountId, addr, startAt);
343
+ };
@@ -0,0 +1,52 @@
1
+ import { log } from "@ledgerhq/logs";
2
+ import type { OperationType } from "@ledgerhq/types-live";
3
+
4
+ /**
5
+ * Returns the operation type by using his palletMethod
6
+ * the method case depends from which indexer you are using
7
+ *
8
+ * @param {*} pallet
9
+ * @param {*} palletMethod
10
+ *
11
+ * @returns {string} - OperationType
12
+ */
13
+ export const getOperationType = (
14
+ pallet: string,
15
+ palletMethod: string
16
+ ): OperationType => {
17
+ switch (palletMethod) {
18
+ case "transfer":
19
+ case "transferKeepAlive":
20
+ return "OUT";
21
+
22
+ case "bond":
23
+ case "bondExtra":
24
+ case "rebond":
25
+ return "BOND";
26
+
27
+ case "unbond":
28
+ return "UNBOND";
29
+
30
+ case "nominate":
31
+ return "NOMINATE";
32
+
33
+ case "chill":
34
+ return "CHILL";
35
+
36
+ case "withdrawUnbonded":
37
+ return "WITHDRAW_UNBONDED";
38
+
39
+ case "setController":
40
+ return "SET_CONTROLLER";
41
+
42
+ case "payoutStakers":
43
+ return "FEES";
44
+
45
+ default:
46
+ log(
47
+ "polkadot/api",
48
+ `Unknown operation type ${pallet}.${palletMethod} - fallback to FEES`
49
+ );
50
+ return "FEES";
51
+ }
52
+ };