@ledgerhq/coin-tezos 7.12.0 → 9.0.0

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 (199) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/lib/api/index.d.ts +1 -2
  3. package/lib/api/index.d.ts.map +1 -1
  4. package/lib/api/index.js +45 -44
  5. package/lib/api/index.js.map +1 -1
  6. package/lib/api/types.d.ts +3 -2
  7. package/lib/api/types.d.ts.map +1 -1
  8. package/lib/config.d.ts +3 -1
  9. package/lib/config.d.ts.map +1 -1
  10. package/lib/config.js.map +1 -1
  11. package/lib/logic/broadcast.d.ts +2 -1
  12. package/lib/logic/broadcast.d.ts.map +1 -1
  13. package/lib/logic/broadcast.js +3 -2
  14. package/lib/logic/broadcast.js.map +1 -1
  15. package/lib/logic/combine.d.ts +1 -1
  16. package/lib/logic/combine.d.ts.map +1 -1
  17. package/lib/logic/combine.js +4 -1
  18. package/lib/logic/combine.js.map +1 -1
  19. package/lib/logic/craftRawOperations.d.ts +2 -1
  20. package/lib/logic/craftRawOperations.d.ts.map +1 -1
  21. package/lib/logic/craftRawOperations.js +5 -8
  22. package/lib/logic/craftRawOperations.js.map +1 -1
  23. package/lib/logic/craftTransaction.d.ts +3 -2
  24. package/lib/logic/craftTransaction.d.ts.map +1 -1
  25. package/lib/logic/craftTransaction.js +6 -9
  26. package/lib/logic/craftTransaction.js.map +1 -1
  27. package/lib/logic/estimateFees.d.ts +2 -1
  28. package/lib/logic/estimateFees.d.ts.map +1 -1
  29. package/lib/logic/estimateFees.js +24 -19
  30. package/lib/logic/estimateFees.js.map +1 -1
  31. package/lib/logic/getBalance.d.ts +2 -1
  32. package/lib/logic/getBalance.d.ts.map +1 -1
  33. package/lib/logic/getBalance.js +7 -8
  34. package/lib/logic/getBalance.js.map +1 -1
  35. package/lib/logic/getBlock.d.ts +2 -1
  36. package/lib/logic/getBlock.d.ts.map +1 -1
  37. package/lib/logic/getBlock.js +12 -10
  38. package/lib/logic/getBlock.js.map +1 -1
  39. package/lib/logic/getBlockInfo.d.ts +2 -1
  40. package/lib/logic/getBlockInfo.d.ts.map +1 -1
  41. package/lib/logic/getBlockInfo.js +5 -3
  42. package/lib/logic/getBlockInfo.js.map +1 -1
  43. package/lib/logic/getStakes.d.ts +3 -2
  44. package/lib/logic/getStakes.d.ts.map +1 -1
  45. package/lib/logic/getStakes.js +9 -9
  46. package/lib/logic/getStakes.js.map +1 -1
  47. package/lib/logic/lastBlock.d.ts +2 -1
  48. package/lib/logic/lastBlock.d.ts.map +1 -1
  49. package/lib/logic/lastBlock.js +4 -6
  50. package/lib/logic/lastBlock.js.map +1 -1
  51. package/lib/logic/listOperations.d.ts +2 -1
  52. package/lib/logic/listOperations.d.ts.map +1 -1
  53. package/lib/logic/listOperations.js +8 -6
  54. package/lib/logic/listOperations.js.map +1 -1
  55. package/lib/logic/tezosToolkit.d.ts +2 -1
  56. package/lib/logic/tezosToolkit.d.ts.map +1 -1
  57. package/lib/logic/tezosToolkit.js +2 -6
  58. package/lib/logic/tezosToolkit.js.map +1 -1
  59. package/lib/logic/validateIntent.d.ts +2 -1
  60. package/lib/logic/validateIntent.d.ts.map +1 -1
  61. package/lib/logic/validateIntent.js +12 -13
  62. package/lib/logic/validateIntent.js.map +1 -1
  63. package/lib/network/bakers.d.ts +5 -4
  64. package/lib/network/bakers.d.ts.map +1 -1
  65. package/lib/network/bakers.js +9 -10
  66. package/lib/network/bakers.js.map +1 -1
  67. package/lib/network/index.d.ts +1 -1
  68. package/lib/network/index.d.ts.map +1 -1
  69. package/lib/network/index.js +2 -5
  70. package/lib/network/index.js.map +1 -1
  71. package/lib/network/tzkt.d.ts +16 -9
  72. package/lib/network/tzkt.d.ts.map +1 -1
  73. package/lib/network/tzkt.js +341 -333
  74. package/lib/network/tzkt.js.map +1 -1
  75. package/lib/utils.d.ts +2 -1
  76. package/lib/utils.d.ts.map +1 -1
  77. package/lib/utils.js +1 -6
  78. package/lib/utils.js.map +1 -1
  79. package/lib-es/api/index.d.ts +1 -2
  80. package/lib-es/api/index.d.ts.map +1 -1
  81. package/lib-es/api/index.js +40 -36
  82. package/lib-es/api/index.js.map +1 -1
  83. package/lib-es/api/types.d.ts +3 -2
  84. package/lib-es/api/types.d.ts.map +1 -1
  85. package/lib-es/config.d.ts +3 -1
  86. package/lib-es/config.d.ts.map +1 -1
  87. package/lib-es/config.js.map +1 -1
  88. package/lib-es/logic/broadcast.d.ts +2 -1
  89. package/lib-es/logic/broadcast.d.ts.map +1 -1
  90. package/lib-es/logic/broadcast.js +3 -2
  91. package/lib-es/logic/broadcast.js.map +1 -1
  92. package/lib-es/logic/combine.d.ts +1 -1
  93. package/lib-es/logic/combine.d.ts.map +1 -1
  94. package/lib-es/logic/combine.js +4 -1
  95. package/lib-es/logic/combine.js.map +1 -1
  96. package/lib-es/logic/craftRawOperations.d.ts +2 -1
  97. package/lib-es/logic/craftRawOperations.d.ts.map +1 -1
  98. package/lib-es/logic/craftRawOperations.js +5 -5
  99. package/lib-es/logic/craftRawOperations.js.map +1 -1
  100. package/lib-es/logic/craftTransaction.d.ts +3 -2
  101. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  102. package/lib-es/logic/craftTransaction.js +6 -6
  103. package/lib-es/logic/craftTransaction.js.map +1 -1
  104. package/lib-es/logic/estimateFees.d.ts +2 -1
  105. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  106. package/lib-es/logic/estimateFees.js +24 -16
  107. package/lib-es/logic/estimateFees.js.map +1 -1
  108. package/lib-es/logic/getBalance.d.ts +2 -1
  109. package/lib-es/logic/getBalance.d.ts.map +1 -1
  110. package/lib-es/logic/getBalance.js +5 -3
  111. package/lib-es/logic/getBalance.js.map +1 -1
  112. package/lib-es/logic/getBlock.d.ts +2 -1
  113. package/lib-es/logic/getBlock.d.ts.map +1 -1
  114. package/lib-es/logic/getBlock.js +10 -8
  115. package/lib-es/logic/getBlock.js.map +1 -1
  116. package/lib-es/logic/getBlockInfo.d.ts +2 -1
  117. package/lib-es/logic/getBlockInfo.d.ts.map +1 -1
  118. package/lib-es/logic/getBlockInfo.js +4 -2
  119. package/lib-es/logic/getBlockInfo.js.map +1 -1
  120. package/lib-es/logic/getStakes.d.ts +3 -2
  121. package/lib-es/logic/getStakes.d.ts.map +1 -1
  122. package/lib-es/logic/getStakes.js +9 -6
  123. package/lib-es/logic/getStakes.js.map +1 -1
  124. package/lib-es/logic/lastBlock.d.ts +2 -1
  125. package/lib-es/logic/lastBlock.d.ts.map +1 -1
  126. package/lib-es/logic/lastBlock.js +4 -3
  127. package/lib-es/logic/lastBlock.js.map +1 -1
  128. package/lib-es/logic/listOperations.d.ts +2 -1
  129. package/lib-es/logic/listOperations.d.ts.map +1 -1
  130. package/lib-es/logic/listOperations.js +7 -5
  131. package/lib-es/logic/listOperations.js.map +1 -1
  132. package/lib-es/logic/tezosToolkit.d.ts +2 -1
  133. package/lib-es/logic/tezosToolkit.d.ts.map +1 -1
  134. package/lib-es/logic/tezosToolkit.js +2 -3
  135. package/lib-es/logic/tezosToolkit.js.map +1 -1
  136. package/lib-es/logic/validateIntent.d.ts +2 -1
  137. package/lib-es/logic/validateIntent.d.ts.map +1 -1
  138. package/lib-es/logic/validateIntent.js +10 -8
  139. package/lib-es/logic/validateIntent.js.map +1 -1
  140. package/lib-es/network/bakers.d.ts +5 -4
  141. package/lib-es/network/bakers.d.ts.map +1 -1
  142. package/lib-es/network/bakers.js +9 -10
  143. package/lib-es/network/bakers.js.map +1 -1
  144. package/lib-es/network/index.d.ts +1 -1
  145. package/lib-es/network/index.d.ts.map +1 -1
  146. package/lib-es/network/index.js +1 -1
  147. package/lib-es/network/index.js.map +1 -1
  148. package/lib-es/network/tzkt.d.ts +16 -9
  149. package/lib-es/network/tzkt.d.ts.map +1 -1
  150. package/lib-es/network/tzkt.js +340 -333
  151. package/lib-es/network/tzkt.js.map +1 -1
  152. package/lib-es/utils.d.ts +2 -1
  153. package/lib-es/utils.d.ts.map +1 -1
  154. package/lib-es/utils.js +1 -3
  155. package/lib-es/utils.js.map +1 -1
  156. package/package.json +2 -2
  157. package/src/api/index-mainnet.integ.test.ts +34 -30
  158. package/src/api/index.integ.test.ts +69 -53
  159. package/src/api/index.test.ts +99 -60
  160. package/src/api/index.ts +52 -36
  161. package/src/api/types.ts +3 -2
  162. package/src/config.ts +4 -1
  163. package/src/logic/broadcast.integ.test.ts +12 -13
  164. package/src/logic/broadcast.ts +4 -2
  165. package/src/logic/combine.test.ts +17 -0
  166. package/src/logic/combine.ts +5 -2
  167. package/src/logic/craftRawOperations.test.ts +47 -33
  168. package/src/logic/craftRawOperations.ts +6 -4
  169. package/src/logic/craftTransaction.integ.test.ts +12 -5
  170. package/src/logic/craftTransaction.test.ts +39 -31
  171. package/src/logic/craftTransaction.ts +10 -5
  172. package/src/logic/estimateFees.integ.test.ts +7 -7
  173. package/src/logic/estimateFees.test.ts +52 -49
  174. package/src/logic/estimateFees.ts +34 -22
  175. package/src/logic/getBalance.test.ts +19 -17
  176. package/src/logic/getBalance.ts +6 -3
  177. package/src/logic/getBlock.integ.test.ts +8 -30
  178. package/src/logic/getBlock.test.ts +91 -81
  179. package/src/logic/getBlock.ts +11 -8
  180. package/src/logic/getBlockInfo.integ.test.ts +9 -18
  181. package/src/logic/getBlockInfo.test.ts +19 -9
  182. package/src/logic/getBlockInfo.ts +5 -2
  183. package/src/logic/getStakes.test.ts +46 -49
  184. package/src/logic/getStakes.ts +14 -5
  185. package/src/logic/lastBlock.ts +5 -3
  186. package/src/logic/listOperations.integ.test.ts +40 -67
  187. package/src/logic/listOperations.test.ts +65 -59
  188. package/src/logic/listOperations.ts +8 -3
  189. package/src/logic/tezosToolkit.ts +3 -3
  190. package/src/logic/validateIntent.test.ts +78 -74
  191. package/src/logic/validateIntent.ts +17 -7
  192. package/src/network/bakers.integ.test.ts +2 -5
  193. package/src/network/bakers.test.ts +5 -6
  194. package/src/network/bakers.ts +13 -10
  195. package/src/network/index.ts +2 -1
  196. package/src/network/tzkt.integ.test.ts +3 -14
  197. package/src/network/tzkt.test.ts +128 -96
  198. package/src/network/tzkt.ts +426 -393
  199. package/src/utils.ts +2 -3
@@ -3,12 +3,10 @@
3
3
  import URL from 'url';
4
4
  import network from '@ledgerhq/live-network';
5
5
  import { log } from '@ledgerhq/logs';
6
- import coinConfig from '../config';
7
6
  /** TzKT hard-caps `limit` at 10 000; we use a safer page size to stay well under that. */
8
7
  const BLOCK_PAGE_SIZE = 1000;
9
8
  /** Maximum number of IDs per `id.in` request to keep URLs under the Cloudflare proxy limit (~16 KB). */
10
9
  const ID_IN_CHUNK_SIZE = 100;
11
- const getExplorerUrl = () => coinConfig.getCoinConfig().explorer.url;
12
10
  /**
13
11
  * Coalesces concurrent `getAccountByAddress` calls for the same explorer+address into a
14
12
  * single request. During a sync both `getBalance` and the readiness hook hit this endpoint
@@ -29,349 +27,359 @@ function chunk(arr, size) {
29
27
  return result;
30
28
  }
31
29
  /**
32
- * Internal helper shared by `getOperationsTransactions` and `getOperationsOrigination`.
33
- * Both endpoints accept the same query shape; only the URL path differs.
30
+ * Builds the TzKT explorer client bound to the given coin config (ADR-019). The caller resolves the
31
+ * config from its {@link Context} and passes it in, rather than the client reading a module-level
32
+ * singleton.
34
33
  */
35
- async function getOperationsByType(type, level, cursor, apiQueryParams = {}) {
36
- // "sort.asc": "id" guarantees forward progress for cursor-based paging (offset.cr).
37
- // Without an explicit sort the API default may be descending, which would cause the
38
- // cursor to go backwards and produce duplicates or an infinite loop.
39
- const params = {
40
- 'level.gte': level,
41
- limit: BLOCK_PAGE_SIZE,
42
- 'sort.asc': 'id',
43
- ...clearUndefined(apiQueryParams),
44
- };
45
- if (cursor !== undefined)
46
- params['offset.cr'] = cursor;
47
- const { data } = await network({
48
- url: `${getExplorerUrl()}/v1/operations/${type}`,
49
- params,
50
- });
51
- return data;
52
- }
53
- const api = {
54
- async getBlockCount() {
55
- const { data } = await network({
56
- url: `${getExplorerUrl()}/v1/blocks/count`,
57
- });
58
- return data;
59
- },
60
- async getLastBlock() {
61
- const { data } = await network({
62
- url: `${getExplorerUrl()}/v1/blocks`,
63
- params: {
64
- 'sort.desc': 'level',
65
- },
66
- });
67
- return {
68
- hash: data[0].hash,
69
- level: data[0].level,
70
- date: new Date(data[0].timestamp),
71
- };
72
- },
73
- async getAccountByAddress(address) {
74
- const key = `${getExplorerUrl()}|${address}`;
75
- const existing = accountByAddressInflight.get(key);
76
- if (existing)
77
- return existing;
78
- const request = network({
79
- url: `${getExplorerUrl()}/v1/accounts/${address}`,
80
- })
81
- .then(({ data }) => data)
82
- .finally(() => accountByAddressInflight.delete(key));
83
- accountByAddressInflight.set(key, request);
84
- return request;
85
- },
86
- /**
87
- * Returns the total `actualAmount` (mutez) summed over the account's `finalizable`
88
- * unstake requests — the portion of `unstakedBalance` whose unlock cycle has been
89
- * reached and that can be reclaimed via `finalize_unstake`. The complementary
90
- * `unstakedBalance - finalizable` portion is still in the deactivation delay window.
91
- *
92
- * TzKT's account endpoint does not expose this split, so we sum from
93
- * `/v1/staking/unstake_requests`. https://api.tzkt.io/#operation/Staking_GetUnstakeRequests
94
- *
95
- * `limit=1000` covers any realistic number of concurrent finalizable requests for
96
- * a single staker — we do not paginate.
97
- */
98
- async getUnstakeRequestsFinalizable(address) {
99
- const { data } = await network({
100
- url: `${getExplorerUrl()}/v1/staking/unstake_requests`,
101
- params: {
102
- 'staker.eq': address,
103
- status: 'finalizable',
104
- 'select.values': 'actualAmount',
105
- limit: 1000,
106
- },
107
- });
108
- return data.reduce((sum, n) => sum + BigInt(n), 0n);
109
- },
110
- /**
111
- * Pending + finalizable unstake requests, by id ascending. Uses `status.ne=finalized` because
112
- * TzKT ignores `status.in=pending,finalizable` on this endpoint and returns finalized requests.
113
- * https://api.tzkt.io/#operation/Staking_GetUnstakeRequests
114
- */
115
- async getUnstakeRequests(address) {
116
- const { data } = await network({
117
- url: `${getExplorerUrl()}/v1/staking/unstake_requests`,
118
- params: {
119
- 'staker.eq': address,
120
- 'status.ne': 'finalized',
121
- 'sort.asc': 'id',
122
- limit: 1000,
123
- },
124
- });
125
- return data;
126
- },
127
- // https://api.tzkt.io/#operation/Accounts_GetOperations
128
- async getAccountOperations(address, query) {
129
- // Remove undefined from query
130
- Object.entries(query).forEach(([key, value]) => value === undefined && delete query[key]);
131
- const { data } = await network({
132
- url: URL.format({
133
- pathname: `${getExplorerUrl()}/v1/accounts/${address}/operations`,
134
- query,
135
- }),
136
- });
137
- return data;
138
- },
139
- // https://api.tzkt.io/#operation/Blocks_GetByLevel
140
- async getBlockByLevel(level) {
141
- const { data } = await network({
142
- url: `${getExplorerUrl()}/v1/blocks/${level}`,
143
- });
144
- return data;
145
- },
146
- /**
147
- * Resolves block hashes for the given levels in a single request.
148
- * Uses `/v1/blocks?level.in=...&select.values=level,hash`, which TzKT honours
149
- * (unlike `/v1/blocks/{level}?select=...`, where `select` is ignored). Used
150
- * for cheap backfill of `block.hash` on operations whose level is known but
151
- * whose response omits the block field (e.g. `/accounts/{addr}/operations`
152
- * for staking ops). Levels that don't resolve are absent from the result map.
153
- */
154
- async getBlockHashesByLevels(levels) {
155
- if (levels.length === 0)
156
- return new Map();
157
- const { data } = await network({
158
- url: `${getExplorerUrl()}/v1/blocks`,
159
- params: {
160
- 'level.in': levels.join(','),
161
- 'select.values': 'level,hash',
162
- limit: levels.length,
163
- },
164
- });
165
- return new Map(data);
166
- },
34
+ export function createTzktApi(config) {
35
+ const explorerUrl = config.explorer.url;
167
36
  /**
168
- * Fetches a single page of `transaction` operations at the given block level.
169
- * Internal used by `fetchBlockTransactions` which handles pagination.
170
- * https://api.tzkt.io/#operation/Operations_GetTransactions
37
+ * Internal helper shared by `getOperationsTransactions` and `getOperationsOrigination`.
38
+ * Both endpoints accept the same query shape; only the URL path differs.
171
39
  */
172
- async getBlockTransactionsPage(level, cursor) {
40
+ async function getOperationsByType(type, level, cursor, apiQueryParams = {}) {
173
41
  // "sort.asc": "id" guarantees forward progress for cursor-based paging (offset.cr).
174
42
  // Without an explicit sort the API default may be descending, which would cause the
175
43
  // cursor to go backwards and produce duplicates or an infinite loop.
176
- const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
177
- if (cursor !== undefined)
178
- params['offset.cr'] = cursor;
179
- const { data } = await network({
180
- url: `${getExplorerUrl()}/v1/operations/transactions`,
181
- params,
182
- });
183
- return data;
184
- },
185
- /**
186
- * Fetches a list of `transaction` operations after the given level.
187
- * https://api.tzkt.io/#operation/Operations_GetTransactions
188
- */
189
- async getOperationsTransactions(level, cursor, apiQueryParams = {}) {
190
- return getOperationsByType('transactions', level, cursor, apiQueryParams);
191
- },
192
- /**
193
- * Fetches a list of `originations` operations after the given level.
194
- * https://api.tzkt.io/#operation/Operations_GetOriginations
195
- */
196
- async getOperationsOrigination(level, cursor, apiQueryParams = {}) {
197
- return getOperationsByType('originations', level, cursor, apiQueryParams);
198
- },
199
- /**
200
- * Fetches a single page of FA token transfers at the given block level.
201
- * Internal — used by `fetchBlockTokenTransfers` which handles pagination.
202
- * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
203
- */
204
- async getBlockTokenTransfersPage(level, cursor) {
205
- // Same rationale as getBlockTransactionsPage: explicit ascending sort keeps the
206
- // offset.cr cursor advancing forward regardless of the API's default ordering.
207
44
  const params = {
208
- level,
45
+ 'level.gte': level,
209
46
  limit: BLOCK_PAGE_SIZE,
210
47
  'sort.asc': 'id',
211
- 'token.standard': 'fa2',
212
- };
213
- if (cursor !== undefined)
214
- params['offset.cr'] = cursor;
215
- const { data } = await network({
216
- url: `${getExplorerUrl()}/v1/tokens/transfers`,
217
- params,
218
- });
219
- return data;
220
- },
221
- /**
222
- * Fetches the latest FA token transfers since the given level.
223
- * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
224
- */
225
- async getTokenTransfers(apiQueryParams = {}) {
226
- const params = {
227
48
  ...clearUndefined(apiQueryParams),
228
49
  };
229
- const { data } = await network({
230
- url: `${getExplorerUrl()}/v1/tokens/transfers`,
231
- params,
232
- });
233
- return data;
234
- },
235
- /**
236
- * Fetches a single page of `origination` operations at the given block level.
237
- * Internal — used by `fetchBlockOriginations` which handles pagination.
238
- * https://api.tzkt.io/#operation/Operations_GetOriginations
239
- */
240
- async getBlockOriginationsPage(level, cursor) {
241
- const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
242
- if (cursor !== undefined)
243
- params['offset.cr'] = cursor;
244
- const { data } = await network({
245
- url: `${getExplorerUrl()}/v1/operations/originations`,
246
- params,
247
- });
248
- return data;
249
- },
250
- /**
251
- * Fetches a single page of `reveal` operations at the given block level.
252
- * Internal — used by `fetchBlockReveals` which handles pagination.
253
- * https://api.tzkt.io/#operation/Operations_GetReveals
254
- */
255
- async getBlockRevealsPage(level, cursor) {
256
- const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
257
- if (cursor !== undefined)
258
- params['offset.cr'] = cursor;
259
- const { data } = await network({
260
- url: `${getExplorerUrl()}/v1/operations/reveals`,
261
- params,
262
- });
263
- return data;
264
- },
265
- /**
266
- * Fetches a single page of `delegation` operations at the given block level.
267
- * Internal — used by `fetchBlockDelegations` which handles pagination.
268
- * https://api.tzkt.io/#operation/Operations_GetDelegations
269
- */
270
- async getBlockDelegationsPage(level, cursor) {
271
- const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
272
- if (cursor !== undefined)
273
- params['offset.cr'] = cursor;
274
- const { data } = await network({
275
- url: `${getExplorerUrl()}/v1/operations/delegations`,
276
- params,
277
- });
278
- return data;
279
- },
280
- /**
281
- * Fetches a single page of `staking` operations at the given block level.
282
- * Internal — used by `fetchBlockStaking` which handles pagination.
283
- * https://api.tzkt.io/#operation/Operations_GetStaking
284
- */
285
- async getBlockStakingPage(level, cursor) {
286
- const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
287
50
  if (cursor !== undefined)
288
51
  params['offset.cr'] = cursor;
289
52
  const { data } = await network({
290
- url: `${getExplorerUrl()}/v1/operations/staking`,
53
+ url: `${explorerUrl}/v1/operations/${type}`,
291
54
  params,
292
55
  });
293
56
  return data;
294
- },
295
- /**
296
- * Fetches FA2 token transfers for a given account.
297
- * Translates `query.sort` to TzKT's `sort.asc=id` / `sort.desc=id`.
298
- * The lower-level `getTokenTransfers` helper is a generic pass-through and does not pin the sort.
299
- * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
300
- */
301
- async getAccountTokenTransfers(address, query) {
302
- const sortKey = query.sort === 'Descending' ? 'sort.desc' : 'sort.asc';
303
- const params = {
304
- 'anyof.from.to': address,
305
- 'token.standard': 'fa2',
306
- [sortKey]: 'id',
307
- limit: query.limit,
308
- 'level.ge': query['level.ge'],
309
- 'level.lt': query['level.lt'],
310
- 'level.gt': query['level.gt'],
311
- 'id.lt': query['id.lt'],
312
- 'id.gt': query['id.gt'],
313
- };
314
- const data = await api.getTokenTransfers(clearUndefined(params));
315
- const transactionIds = data
316
- .map((t) => t.transactionId)
317
- .filter((id) => typeof id === 'number');
318
- const originationIds = data
319
- .map((t) => t.originationId)
320
- .filter((id) => typeof id === 'number');
321
- if (transactionIds.length === 0 && originationIds.length === 0) {
322
- return [];
323
- }
324
- const transactions = transactionIds.length
325
- ? (await Promise.all(chunk(transactionIds, ID_IN_CHUNK_SIZE).map((ids) => api.getOperationsTransactions(query['level.ge'] || 0, undefined, {
326
- 'id.in': ids.join(','),
327
- })))).flat()
328
- : [];
329
- const originations = originationIds.length
330
- ? (await Promise.all(chunk(originationIds, ID_IN_CHUNK_SIZE).map((ids) => api.getOperationsOrigination(query['level.ge'] || 0, undefined, {
331
- 'id.in': ids.join(','),
332
- })))).flat()
333
- : [];
334
- // Build id -> operation maps once so per-transfer lookups are O(1) instead of O(n).
335
- // Keys are widened to `number | undefined` so lookups with a missing id naturally
336
- // return `undefined` (no entry is ever stored under the `undefined` key).
337
- const transactionsById = new Map(transactions.map((t) => [t.id, t]));
338
- const originationsById = new Map(originations.map((o) => [o.id, o]));
339
- return data.map((token) => {
340
- const transaction = transactionsById.get(token.transactionId);
341
- const origination = originationsById.get(token.originationId);
57
+ }
58
+ const api = {
59
+ async getBlockCount() {
60
+ const { data } = await network({
61
+ url: `${explorerUrl}/v1/blocks/count`,
62
+ });
63
+ return data;
64
+ },
65
+ async getLastBlock() {
66
+ const { data } = await network({
67
+ url: `${explorerUrl}/v1/blocks`,
68
+ params: {
69
+ 'sort.desc': 'level',
70
+ },
71
+ });
342
72
  return {
343
- ...token,
344
- hash: transaction?.hash ?? origination?.hash ?? '',
345
- block: transaction?.block ?? origination?.block ?? '',
73
+ hash: data[0].hash,
74
+ level: data[0].level,
75
+ date: new Date(data[0].timestamp),
346
76
  };
347
- });
348
- },
349
- /**
350
- * Fetches FA2 token balances for a given account.
351
- * When `tokenFilter` is omitted, all FA2 token balances are returned.
352
- * Pass `tokenFilter` to query a specific FA2 contract + token id (e.g. send-max for FA2).
353
- * https://api.tzkt.io/#operation/Tokens_GetTokenBalances
354
- */
355
- async getTokensBalances(address, tokenFilter) {
356
- const params = {
357
- account: address,
358
- 'token.standard': 'fa2',
359
- };
360
- if (tokenFilter) {
361
- params['token.contract'] = tokenFilter.contractAddress;
362
- params['token.tokenId'] = String(tokenFilter.tokenId);
363
- }
364
- const { data } = await network({
365
- url: `${getExplorerUrl()}/v1/tokens/balances`,
366
- params,
367
- });
368
- return data;
369
- },
370
- };
77
+ },
78
+ async getAccountByAddress(address) {
79
+ const key = `${explorerUrl}|${address}`;
80
+ const existing = accountByAddressInflight.get(key);
81
+ if (existing)
82
+ return existing;
83
+ const request = network({
84
+ url: `${explorerUrl}/v1/accounts/${address}`,
85
+ })
86
+ .then(({ data }) => data)
87
+ .finally(() => accountByAddressInflight.delete(key));
88
+ accountByAddressInflight.set(key, request);
89
+ return request;
90
+ },
91
+ /**
92
+ * Returns the total `actualAmount` (mutez) summed over the account's `finalizable`
93
+ * unstake requests — the portion of `unstakedBalance` whose unlock cycle has been
94
+ * reached and that can be reclaimed via `finalize_unstake`. The complementary
95
+ * `unstakedBalance - finalizable` portion is still in the deactivation delay window.
96
+ *
97
+ * TzKT's account endpoint does not expose this split, so we sum from
98
+ * `/v1/staking/unstake_requests`. https://api.tzkt.io/#operation/Staking_GetUnstakeRequests
99
+ *
100
+ * `limit=1000` covers any realistic number of concurrent finalizable requests for
101
+ * a single staker — we do not paginate.
102
+ */
103
+ async getUnstakeRequestsFinalizable(address) {
104
+ const { data } = await network({
105
+ url: `${explorerUrl}/v1/staking/unstake_requests`,
106
+ params: {
107
+ 'staker.eq': address,
108
+ status: 'finalizable',
109
+ 'select.values': 'actualAmount',
110
+ limit: 1000,
111
+ },
112
+ });
113
+ return data.reduce((sum, n) => sum + BigInt(n), 0n);
114
+ },
115
+ /**
116
+ * Pending + finalizable unstake requests, by id ascending. Uses `status.ne=finalized` because
117
+ * TzKT ignores `status.in=pending,finalizable` on this endpoint and returns finalized requests.
118
+ * https://api.tzkt.io/#operation/Staking_GetUnstakeRequests
119
+ */
120
+ async getUnstakeRequests(address) {
121
+ const { data } = await network({
122
+ url: `${explorerUrl}/v1/staking/unstake_requests`,
123
+ params: {
124
+ 'staker.eq': address,
125
+ 'status.ne': 'finalized',
126
+ 'sort.asc': 'id',
127
+ limit: 1000,
128
+ },
129
+ });
130
+ return data;
131
+ },
132
+ // https://api.tzkt.io/#operation/Accounts_GetOperations
133
+ async getAccountOperations(address, query) {
134
+ // Remove undefined from query
135
+ Object.entries(query).forEach(([key, value]) => value === undefined && delete query[key]);
136
+ const { data } = await network({
137
+ url: URL.format({
138
+ pathname: `${explorerUrl}/v1/accounts/${address}/operations`,
139
+ query,
140
+ }),
141
+ });
142
+ return data;
143
+ },
144
+ // https://api.tzkt.io/#operation/Blocks_GetByLevel
145
+ async getBlockByLevel(level) {
146
+ const { data } = await network({
147
+ url: `${explorerUrl}/v1/blocks/${level}`,
148
+ });
149
+ return data;
150
+ },
151
+ /**
152
+ * Resolves block hashes for the given levels in a single request.
153
+ * Uses `/v1/blocks?level.in=...&select.values=level,hash`, which TzKT honours
154
+ * (unlike `/v1/blocks/{level}?select=...`, where `select` is ignored). Used
155
+ * for cheap backfill of `block.hash` on operations whose level is known but
156
+ * whose response omits the block field (e.g. `/accounts/{addr}/operations`
157
+ * for staking ops). Levels that don't resolve are absent from the result map.
158
+ */
159
+ async getBlockHashesByLevels(levels) {
160
+ if (levels.length === 0)
161
+ return new Map();
162
+ const { data } = await network({
163
+ url: `${explorerUrl}/v1/blocks`,
164
+ params: {
165
+ 'level.in': levels.join(','),
166
+ 'select.values': 'level,hash',
167
+ limit: levels.length,
168
+ },
169
+ });
170
+ return new Map(data);
171
+ },
172
+ /**
173
+ * Fetches a single page of `transaction` operations at the given block level.
174
+ * Internal — used by `fetchBlockTransactions` which handles pagination.
175
+ * https://api.tzkt.io/#operation/Operations_GetTransactions
176
+ */
177
+ async getBlockTransactionsPage(level, cursor) {
178
+ // "sort.asc": "id" guarantees forward progress for cursor-based paging (offset.cr).
179
+ // Without an explicit sort the API default may be descending, which would cause the
180
+ // cursor to go backwards and produce duplicates or an infinite loop.
181
+ const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
182
+ if (cursor !== undefined)
183
+ params['offset.cr'] = cursor;
184
+ const { data } = await network({
185
+ url: `${explorerUrl}/v1/operations/transactions`,
186
+ params,
187
+ });
188
+ return data;
189
+ },
190
+ /**
191
+ * Fetches a list of `transaction` operations after the given level.
192
+ * https://api.tzkt.io/#operation/Operations_GetTransactions
193
+ */
194
+ async getOperationsTransactions(level, cursor, apiQueryParams = {}) {
195
+ return getOperationsByType('transactions', level, cursor, apiQueryParams);
196
+ },
197
+ /**
198
+ * Fetches a list of `originations` operations after the given level.
199
+ * https://api.tzkt.io/#operation/Operations_GetOriginations
200
+ */
201
+ async getOperationsOrigination(level, cursor, apiQueryParams = {}) {
202
+ return getOperationsByType('originations', level, cursor, apiQueryParams);
203
+ },
204
+ /**
205
+ * Fetches a single page of FA token transfers at the given block level.
206
+ * Internal — used by `fetchBlockTokenTransfers` which handles pagination.
207
+ * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
208
+ */
209
+ async getBlockTokenTransfersPage(level, cursor) {
210
+ // Same rationale as getBlockTransactionsPage: explicit ascending sort keeps the
211
+ // offset.cr cursor advancing forward regardless of the API's default ordering.
212
+ const params = {
213
+ level,
214
+ limit: BLOCK_PAGE_SIZE,
215
+ 'sort.asc': 'id',
216
+ 'token.standard': 'fa2',
217
+ };
218
+ if (cursor !== undefined)
219
+ params['offset.cr'] = cursor;
220
+ const { data } = await network({
221
+ url: `${explorerUrl}/v1/tokens/transfers`,
222
+ params,
223
+ });
224
+ return data;
225
+ },
226
+ /**
227
+ * Fetches the latest FA token transfers since the given level.
228
+ * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
229
+ */
230
+ async getTokenTransfers(apiQueryParams = {}) {
231
+ const params = {
232
+ ...clearUndefined(apiQueryParams),
233
+ };
234
+ const { data } = await network({
235
+ url: `${explorerUrl}/v1/tokens/transfers`,
236
+ params,
237
+ });
238
+ return data;
239
+ },
240
+ /**
241
+ * Fetches a single page of `origination` operations at the given block level.
242
+ * Internal — used by `fetchBlockOriginations` which handles pagination.
243
+ * https://api.tzkt.io/#operation/Operations_GetOriginations
244
+ */
245
+ async getBlockOriginationsPage(level, cursor) {
246
+ const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
247
+ if (cursor !== undefined)
248
+ params['offset.cr'] = cursor;
249
+ const { data } = await network({
250
+ url: `${explorerUrl}/v1/operations/originations`,
251
+ params,
252
+ });
253
+ return data;
254
+ },
255
+ /**
256
+ * Fetches a single page of `reveal` operations at the given block level.
257
+ * Internal — used by `fetchBlockReveals` which handles pagination.
258
+ * https://api.tzkt.io/#operation/Operations_GetReveals
259
+ */
260
+ async getBlockRevealsPage(level, cursor) {
261
+ const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
262
+ if (cursor !== undefined)
263
+ params['offset.cr'] = cursor;
264
+ const { data } = await network({
265
+ url: `${explorerUrl}/v1/operations/reveals`,
266
+ params,
267
+ });
268
+ return data;
269
+ },
270
+ /**
271
+ * Fetches a single page of `delegation` operations at the given block level.
272
+ * Internal — used by `fetchBlockDelegations` which handles pagination.
273
+ * https://api.tzkt.io/#operation/Operations_GetDelegations
274
+ */
275
+ async getBlockDelegationsPage(level, cursor) {
276
+ const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
277
+ if (cursor !== undefined)
278
+ params['offset.cr'] = cursor;
279
+ const { data } = await network({
280
+ url: `${explorerUrl}/v1/operations/delegations`,
281
+ params,
282
+ });
283
+ return data;
284
+ },
285
+ /**
286
+ * Fetches a single page of `staking` operations at the given block level.
287
+ * Internal — used by `fetchBlockStaking` which handles pagination.
288
+ * https://api.tzkt.io/#operation/Operations_GetStaking
289
+ */
290
+ async getBlockStakingPage(level, cursor) {
291
+ const params = { level, limit: BLOCK_PAGE_SIZE, 'sort.asc': 'id' };
292
+ if (cursor !== undefined)
293
+ params['offset.cr'] = cursor;
294
+ const { data } = await network({
295
+ url: `${explorerUrl}/v1/operations/staking`,
296
+ params,
297
+ });
298
+ return data;
299
+ },
300
+ /**
301
+ * Fetches FA2 token transfers for a given account.
302
+ * Translates `query.sort` to TzKT's `sort.asc=id` / `sort.desc=id`.
303
+ * The lower-level `getTokenTransfers` helper is a generic pass-through and does not pin the sort.
304
+ * https://api.tzkt.io/#operation/Tokens_GetTokenTransfers
305
+ */
306
+ async getAccountTokenTransfers(address, query) {
307
+ const sortKey = query.sort === 'Descending' ? 'sort.desc' : 'sort.asc';
308
+ const params = {
309
+ 'anyof.from.to': address,
310
+ 'token.standard': 'fa2',
311
+ [sortKey]: 'id',
312
+ limit: query.limit,
313
+ 'level.ge': query['level.ge'],
314
+ 'level.lt': query['level.lt'],
315
+ 'level.gt': query['level.gt'],
316
+ 'id.lt': query['id.lt'],
317
+ 'id.gt': query['id.gt'],
318
+ };
319
+ const data = await api.getTokenTransfers(clearUndefined(params));
320
+ const transactionIds = data
321
+ .map((t) => t.transactionId)
322
+ .filter((id) => typeof id === 'number');
323
+ const originationIds = data
324
+ .map((t) => t.originationId)
325
+ .filter((id) => typeof id === 'number');
326
+ if (transactionIds.length === 0 && originationIds.length === 0) {
327
+ return [];
328
+ }
329
+ const transactions = transactionIds.length
330
+ ? (await Promise.all(chunk(transactionIds, ID_IN_CHUNK_SIZE).map((ids) => api.getOperationsTransactions(query['level.ge'] || 0, undefined, {
331
+ 'id.in': ids.join(','),
332
+ })))).flat()
333
+ : [];
334
+ const originations = originationIds.length
335
+ ? (await Promise.all(chunk(originationIds, ID_IN_CHUNK_SIZE).map((ids) => api.getOperationsOrigination(query['level.ge'] || 0, undefined, {
336
+ 'id.in': ids.join(','),
337
+ })))).flat()
338
+ : [];
339
+ // Build id -> operation maps once so per-transfer lookups are O(1) instead of O(n).
340
+ // Keys are widened to `number | undefined` so lookups with a missing id naturally
341
+ // return `undefined` (no entry is ever stored under the `undefined` key).
342
+ const transactionsById = new Map(transactions.map((t) => [t.id, t]));
343
+ const originationsById = new Map(originations.map((o) => [o.id, o]));
344
+ return data.map((token) => {
345
+ const transaction = transactionsById.get(token.transactionId);
346
+ const origination = originationsById.get(token.originationId);
347
+ return {
348
+ ...token,
349
+ hash: transaction?.hash ?? origination?.hash ?? '',
350
+ block: transaction?.block ?? origination?.block ?? '',
351
+ };
352
+ });
353
+ },
354
+ /**
355
+ * Fetches FA2 token balances for a given account.
356
+ * When `tokenFilter` is omitted, all FA2 token balances are returned.
357
+ * Pass `tokenFilter` to query a specific FA2 contract + token id (e.g. send-max for FA2).
358
+ * https://api.tzkt.io/#operation/Tokens_GetTokenBalances
359
+ */
360
+ async getTokensBalances(address, tokenFilter) {
361
+ const params = {
362
+ account: address,
363
+ 'token.standard': 'fa2',
364
+ };
365
+ if (tokenFilter) {
366
+ params['token.contract'] = tokenFilter.contractAddress;
367
+ params['token.tokenId'] = String(tokenFilter.tokenId);
368
+ }
369
+ const { data } = await network({
370
+ url: `${explorerUrl}/v1/tokens/balances`,
371
+ params,
372
+ });
373
+ return data;
374
+ },
375
+ };
376
+ return api;
377
+ }
371
378
  // TODO this has same purpose as api/listOperations
372
- export const fetchAllTransactions = async (address, lastId) => {
379
+ export const fetchAllTransactions = async (config, address, lastId) => {
380
+ const api = createTzktApi(config);
373
381
  let ops = [];
374
- let maxIteration = coinConfig.getCoinConfig().explorer.maxTxQuery;
382
+ let maxIteration = config.explorer.maxTxQuery;
375
383
  do {
376
384
  const newOps = await api.getAccountOperations(address, {
377
385
  lastId,
@@ -399,10 +407,10 @@ export const fetchAllTransactions = async (address, lastId) => {
399
407
  * requests when needed and is therefore safe for dense blocks.
400
408
  * A safety cap (`maxTxQuery`) prevents infinite loops on pathological responses.
401
409
  */
402
- async function fetchBlockPaginated(pageFn, level, label) {
410
+ async function fetchBlockPaginated(config, pageFn, level, label) {
403
411
  const items = [];
404
412
  let cursor;
405
- let maxIteration = coinConfig.getCoinConfig().explorer.maxTxQuery;
413
+ let maxIteration = config.explorer.maxTxQuery;
406
414
  do {
407
415
  const page = await pageFn(level, cursor);
408
416
  if (page.length === 0)
@@ -417,11 +425,10 @@ async function fetchBlockPaginated(pageFn, level, label) {
417
425
  }
418
426
  return items;
419
427
  }
420
- export const fetchBlockTransactions = (level) => fetchBlockPaginated(api.getBlockTransactionsPage, level, 'fetchBlockTransactions');
421
- export const fetchBlockTokenTransfers = (level) => fetchBlockPaginated(api.getBlockTokenTransfersPage, level, 'fetchBlockTokenTransfers');
422
- export const fetchBlockDelegations = (level) => fetchBlockPaginated(api.getBlockDelegationsPage, level, 'fetchBlockDelegations');
423
- export const fetchBlockStaking = (level) => fetchBlockPaginated(api.getBlockStakingPage, level, 'fetchBlockStaking');
424
- export const fetchBlockOriginations = (level) => fetchBlockPaginated(api.getBlockOriginationsPage, level, 'fetchBlockOriginations');
425
- export const fetchBlockReveals = (level) => fetchBlockPaginated(api.getBlockRevealsPage, level, 'fetchBlockReveals');
426
- export default api;
428
+ export const fetchBlockTransactions = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockTransactionsPage, level, 'fetchBlockTransactions');
429
+ export const fetchBlockTokenTransfers = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockTokenTransfersPage, level, 'fetchBlockTokenTransfers');
430
+ export const fetchBlockDelegations = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockDelegationsPage, level, 'fetchBlockDelegations');
431
+ export const fetchBlockStaking = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockStakingPage, level, 'fetchBlockStaking');
432
+ export const fetchBlockOriginations = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockOriginationsPage, level, 'fetchBlockOriginations');
433
+ export const fetchBlockReveals = (config, level) => fetchBlockPaginated(config, createTzktApi(config).getBlockRevealsPage, level, 'fetchBlockReveals');
427
434
  //# sourceMappingURL=tzkt.js.map