@ledgerhq/coin-tezos 7.12.0 → 8.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 (189) hide show
  1. package/CHANGELOG.md +28 -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/craftRawOperations.d.ts +2 -1
  16. package/lib/logic/craftRawOperations.d.ts.map +1 -1
  17. package/lib/logic/craftRawOperations.js +5 -8
  18. package/lib/logic/craftRawOperations.js.map +1 -1
  19. package/lib/logic/craftTransaction.d.ts +3 -2
  20. package/lib/logic/craftTransaction.d.ts.map +1 -1
  21. package/lib/logic/craftTransaction.js +6 -9
  22. package/lib/logic/craftTransaction.js.map +1 -1
  23. package/lib/logic/estimateFees.d.ts +2 -1
  24. package/lib/logic/estimateFees.d.ts.map +1 -1
  25. package/lib/logic/estimateFees.js +24 -19
  26. package/lib/logic/estimateFees.js.map +1 -1
  27. package/lib/logic/getBalance.d.ts +2 -1
  28. package/lib/logic/getBalance.d.ts.map +1 -1
  29. package/lib/logic/getBalance.js +7 -8
  30. package/lib/logic/getBalance.js.map +1 -1
  31. package/lib/logic/getBlock.d.ts +2 -1
  32. package/lib/logic/getBlock.d.ts.map +1 -1
  33. package/lib/logic/getBlock.js +12 -10
  34. package/lib/logic/getBlock.js.map +1 -1
  35. package/lib/logic/getBlockInfo.d.ts +2 -1
  36. package/lib/logic/getBlockInfo.d.ts.map +1 -1
  37. package/lib/logic/getBlockInfo.js +5 -3
  38. package/lib/logic/getBlockInfo.js.map +1 -1
  39. package/lib/logic/getStakes.d.ts +3 -2
  40. package/lib/logic/getStakes.d.ts.map +1 -1
  41. package/lib/logic/getStakes.js +9 -9
  42. package/lib/logic/getStakes.js.map +1 -1
  43. package/lib/logic/lastBlock.d.ts +2 -1
  44. package/lib/logic/lastBlock.d.ts.map +1 -1
  45. package/lib/logic/lastBlock.js +4 -6
  46. package/lib/logic/lastBlock.js.map +1 -1
  47. package/lib/logic/listOperations.d.ts +2 -1
  48. package/lib/logic/listOperations.d.ts.map +1 -1
  49. package/lib/logic/listOperations.js +8 -6
  50. package/lib/logic/listOperations.js.map +1 -1
  51. package/lib/logic/tezosToolkit.d.ts +2 -1
  52. package/lib/logic/tezosToolkit.d.ts.map +1 -1
  53. package/lib/logic/tezosToolkit.js +2 -6
  54. package/lib/logic/tezosToolkit.js.map +1 -1
  55. package/lib/logic/validateIntent.d.ts +2 -1
  56. package/lib/logic/validateIntent.d.ts.map +1 -1
  57. package/lib/logic/validateIntent.js +12 -13
  58. package/lib/logic/validateIntent.js.map +1 -1
  59. package/lib/network/bakers.d.ts +5 -4
  60. package/lib/network/bakers.d.ts.map +1 -1
  61. package/lib/network/bakers.js +9 -10
  62. package/lib/network/bakers.js.map +1 -1
  63. package/lib/network/index.d.ts +1 -1
  64. package/lib/network/index.d.ts.map +1 -1
  65. package/lib/network/index.js +2 -5
  66. package/lib/network/index.js.map +1 -1
  67. package/lib/network/tzkt.d.ts +16 -9
  68. package/lib/network/tzkt.d.ts.map +1 -1
  69. package/lib/network/tzkt.js +341 -333
  70. package/lib/network/tzkt.js.map +1 -1
  71. package/lib/utils.d.ts +2 -1
  72. package/lib/utils.d.ts.map +1 -1
  73. package/lib/utils.js +1 -6
  74. package/lib/utils.js.map +1 -1
  75. package/lib-es/api/index.d.ts +1 -2
  76. package/lib-es/api/index.d.ts.map +1 -1
  77. package/lib-es/api/index.js +40 -36
  78. package/lib-es/api/index.js.map +1 -1
  79. package/lib-es/api/types.d.ts +3 -2
  80. package/lib-es/api/types.d.ts.map +1 -1
  81. package/lib-es/config.d.ts +3 -1
  82. package/lib-es/config.d.ts.map +1 -1
  83. package/lib-es/config.js.map +1 -1
  84. package/lib-es/logic/broadcast.d.ts +2 -1
  85. package/lib-es/logic/broadcast.d.ts.map +1 -1
  86. package/lib-es/logic/broadcast.js +3 -2
  87. package/lib-es/logic/broadcast.js.map +1 -1
  88. package/lib-es/logic/craftRawOperations.d.ts +2 -1
  89. package/lib-es/logic/craftRawOperations.d.ts.map +1 -1
  90. package/lib-es/logic/craftRawOperations.js +5 -5
  91. package/lib-es/logic/craftRawOperations.js.map +1 -1
  92. package/lib-es/logic/craftTransaction.d.ts +3 -2
  93. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  94. package/lib-es/logic/craftTransaction.js +6 -6
  95. package/lib-es/logic/craftTransaction.js.map +1 -1
  96. package/lib-es/logic/estimateFees.d.ts +2 -1
  97. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  98. package/lib-es/logic/estimateFees.js +24 -16
  99. package/lib-es/logic/estimateFees.js.map +1 -1
  100. package/lib-es/logic/getBalance.d.ts +2 -1
  101. package/lib-es/logic/getBalance.d.ts.map +1 -1
  102. package/lib-es/logic/getBalance.js +5 -3
  103. package/lib-es/logic/getBalance.js.map +1 -1
  104. package/lib-es/logic/getBlock.d.ts +2 -1
  105. package/lib-es/logic/getBlock.d.ts.map +1 -1
  106. package/lib-es/logic/getBlock.js +10 -8
  107. package/lib-es/logic/getBlock.js.map +1 -1
  108. package/lib-es/logic/getBlockInfo.d.ts +2 -1
  109. package/lib-es/logic/getBlockInfo.d.ts.map +1 -1
  110. package/lib-es/logic/getBlockInfo.js +4 -2
  111. package/lib-es/logic/getBlockInfo.js.map +1 -1
  112. package/lib-es/logic/getStakes.d.ts +3 -2
  113. package/lib-es/logic/getStakes.d.ts.map +1 -1
  114. package/lib-es/logic/getStakes.js +9 -6
  115. package/lib-es/logic/getStakes.js.map +1 -1
  116. package/lib-es/logic/lastBlock.d.ts +2 -1
  117. package/lib-es/logic/lastBlock.d.ts.map +1 -1
  118. package/lib-es/logic/lastBlock.js +4 -3
  119. package/lib-es/logic/lastBlock.js.map +1 -1
  120. package/lib-es/logic/listOperations.d.ts +2 -1
  121. package/lib-es/logic/listOperations.d.ts.map +1 -1
  122. package/lib-es/logic/listOperations.js +7 -5
  123. package/lib-es/logic/listOperations.js.map +1 -1
  124. package/lib-es/logic/tezosToolkit.d.ts +2 -1
  125. package/lib-es/logic/tezosToolkit.d.ts.map +1 -1
  126. package/lib-es/logic/tezosToolkit.js +2 -3
  127. package/lib-es/logic/tezosToolkit.js.map +1 -1
  128. package/lib-es/logic/validateIntent.d.ts +2 -1
  129. package/lib-es/logic/validateIntent.d.ts.map +1 -1
  130. package/lib-es/logic/validateIntent.js +10 -8
  131. package/lib-es/logic/validateIntent.js.map +1 -1
  132. package/lib-es/network/bakers.d.ts +5 -4
  133. package/lib-es/network/bakers.d.ts.map +1 -1
  134. package/lib-es/network/bakers.js +9 -10
  135. package/lib-es/network/bakers.js.map +1 -1
  136. package/lib-es/network/index.d.ts +1 -1
  137. package/lib-es/network/index.d.ts.map +1 -1
  138. package/lib-es/network/index.js +1 -1
  139. package/lib-es/network/index.js.map +1 -1
  140. package/lib-es/network/tzkt.d.ts +16 -9
  141. package/lib-es/network/tzkt.d.ts.map +1 -1
  142. package/lib-es/network/tzkt.js +340 -333
  143. package/lib-es/network/tzkt.js.map +1 -1
  144. package/lib-es/utils.d.ts +2 -1
  145. package/lib-es/utils.d.ts.map +1 -1
  146. package/lib-es/utils.js +1 -3
  147. package/lib-es/utils.js.map +1 -1
  148. package/package.json +2 -2
  149. package/src/api/index-mainnet.integ.test.ts +34 -30
  150. package/src/api/index.integ.test.ts +69 -53
  151. package/src/api/index.test.ts +99 -60
  152. package/src/api/index.ts +52 -36
  153. package/src/api/types.ts +3 -2
  154. package/src/config.ts +4 -1
  155. package/src/logic/broadcast.integ.test.ts +12 -13
  156. package/src/logic/broadcast.ts +4 -2
  157. package/src/logic/craftRawOperations.test.ts +47 -33
  158. package/src/logic/craftRawOperations.ts +6 -4
  159. package/src/logic/craftTransaction.integ.test.ts +12 -5
  160. package/src/logic/craftTransaction.test.ts +39 -31
  161. package/src/logic/craftTransaction.ts +10 -5
  162. package/src/logic/estimateFees.integ.test.ts +7 -7
  163. package/src/logic/estimateFees.test.ts +52 -49
  164. package/src/logic/estimateFees.ts +34 -22
  165. package/src/logic/getBalance.test.ts +19 -17
  166. package/src/logic/getBalance.ts +6 -3
  167. package/src/logic/getBlock.integ.test.ts +8 -30
  168. package/src/logic/getBlock.test.ts +91 -81
  169. package/src/logic/getBlock.ts +11 -8
  170. package/src/logic/getBlockInfo.integ.test.ts +9 -18
  171. package/src/logic/getBlockInfo.test.ts +19 -9
  172. package/src/logic/getBlockInfo.ts +5 -2
  173. package/src/logic/getStakes.test.ts +46 -49
  174. package/src/logic/getStakes.ts +14 -5
  175. package/src/logic/lastBlock.ts +5 -3
  176. package/src/logic/listOperations.integ.test.ts +40 -67
  177. package/src/logic/listOperations.test.ts +65 -59
  178. package/src/logic/listOperations.ts +8 -3
  179. package/src/logic/tezosToolkit.ts +3 -3
  180. package/src/logic/validateIntent.test.ts +78 -74
  181. package/src/logic/validateIntent.ts +17 -7
  182. package/src/network/bakers.integ.test.ts +2 -5
  183. package/src/network/bakers.test.ts +5 -6
  184. package/src/network/bakers.ts +13 -10
  185. package/src/network/index.ts +2 -1
  186. package/src/network/tzkt.integ.test.ts +3 -14
  187. package/src/network/tzkt.test.ts +128 -96
  188. package/src/network/tzkt.ts +426 -393
  189. package/src/utils.ts +2 -3
@@ -4,13 +4,16 @@
4
4
  import type { TezosCoinConfig } from '../config'
5
5
  import type { TezosApiBaker } from './bakers'
6
6
  import network from '@ledgerhq/live-network/network'
7
- import coinConfig from '../config'
8
7
  import { mockConfig } from '../test/config'
9
8
  import { asBaker, cache } from './bakers'
10
9
 
11
10
  jest.mock('@ledgerhq/live-network/network')
12
11
  const mockedNetwork = jest.mocked(network)
13
12
 
13
+ // `cache` is keyed by config (its LRU resolver is `config.baker.url`), so it must be called with a
14
+ // config. Previously the config was provided via `coinConfig.setCoinConfig(...)`.
15
+ const config = mockConfig as TezosCoinConfig
16
+
14
17
  const data: TezosApiBaker[] = [
15
18
  {
16
19
  address: 'tz1Kf25fX1VdmYGSEzwFy1wNmkbSEZ2V83sY',
@@ -167,10 +170,6 @@ const data: TezosApiBaker[] = [
167
170
  ]
168
171
 
169
172
  describe('Tezos Baker', () => {
170
- beforeAll(() => {
171
- coinConfig.setCoinConfig((): TezosCoinConfig => mockConfig as TezosCoinConfig)
172
- })
173
-
174
173
  afterEach(() => {
175
174
  jest.clearAllMocks()
176
175
  cache.reset()
@@ -192,7 +191,7 @@ describe('Tezos Baker', () => {
192
191
  })
193
192
 
194
193
  it('Ledger Baker should be first in the list', async () => {
195
- const bakers = await cache()
194
+ const bakers = await cache(config)
196
195
  expect(bakers[0].name).toBe('Ledger by Kiln')
197
196
  })
198
197
  })
@@ -1,11 +1,11 @@
1
1
  // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import type { TezosCoinConfig } from '../config'
4
5
  import type { Baker } from '../types'
5
6
  import { makeLRUCache } from '@ledgerhq/live-network/cache'
6
7
  import network from '@ledgerhq/live-network/network'
7
8
  import { log } from '@ledgerhq/logs'
8
- import coinConfig from '../config'
9
9
  import { ledgerValidatorAddress } from './bakers.whitelist-default'
10
10
 
11
11
  export type TezosApiBaker = {
@@ -40,9 +40,9 @@ export type TezosBakerFeature = {
40
40
  }
41
41
 
42
42
  export const cache = makeLRUCache(
43
- async (): Promise<Baker[]> => {
43
+ async (config: TezosCoinConfig): Promise<Baker[]> => {
44
44
  const bakers: Baker[] = []
45
- const TEZOS_API_BASE_URL = coinConfig.getCoinConfig().baker.url
45
+ const TEZOS_API_BASE_URL = config.baker.url
46
46
  const { data } = await network<TezosApiBaker[]>({
47
47
  url: `${TEZOS_API_BASE_URL}/v3/bakers`,
48
48
  })
@@ -67,13 +67,13 @@ export const cache = makeLRUCache(
67
67
  log('tezos/bakers', 'loaded ' + bakers.length + ' bakers')
68
68
  return [ledgerBaker, ...bakers]
69
69
  },
70
- () => ''
70
+ (config: TezosCoinConfig) => config.baker.url
71
71
  )
72
72
 
73
73
  let _lastBakers: Baker[] = []
74
74
 
75
- export const fetchAllBakers = async (): Promise<Baker[]> => {
76
- const allBakers = await cache.force()
75
+ export const fetchAllBakers = async (config: TezosCoinConfig): Promise<Baker[]> => {
76
+ const allBakers = await cache.force(config)
77
77
  _lastBakers = allBakers
78
78
  return allBakers
79
79
  }
@@ -85,8 +85,11 @@ export const listBakersWithDefault = (whitelistAddresses: string[]): Baker[] =>
85
85
  return whitelist(_lastBakers, whitelistAddresses)
86
86
  }
87
87
 
88
- export const listBakers = async (whitelistAddresses: string[]): Promise<Baker[]> => {
89
- _lastBakers = await cache()
88
+ export const listBakers = async (
89
+ config: TezosCoinConfig,
90
+ whitelistAddresses: string[]
91
+ ): Promise<Baker[]> => {
92
+ _lastBakers = await cache(config)
90
93
  return whitelist(_lastBakers, whitelistAddresses)
91
94
  }
92
95
 
@@ -94,10 +97,10 @@ export function getBakerSync(addr: string): Baker | undefined {
94
97
  return _lastBakers.find((baker) => baker.address === addr)
95
98
  }
96
99
 
97
- export async function loadBaker(addr: string): Promise<Baker | undefined> {
100
+ export async function loadBaker(config: TezosCoinConfig, addr: string): Promise<Baker | undefined> {
98
101
  const cacheBaker = getBakerSync(addr)
99
102
  if (cacheBaker) return cacheBaker
100
- const bakers = await cache()
103
+ const bakers = await cache(config)
101
104
  const baker = bakers.find((baker) => baker.address === addr)
102
105
  return baker
103
106
  }
@@ -5,7 +5,8 @@ import * as bakers from './bakers'
5
5
 
6
6
  export { bakers }
7
7
  export {
8
- default as tzkt,
8
+ createTzktApi,
9
+ type TzktApi,
9
10
  fetchAllTransactions,
10
11
  fetchBlockDelegations,
11
12
  fetchBlockOriginations,
@@ -2,8 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import type { TezosCoinConfig } from '../config'
5
- import coinConfig from '../config'
6
- import api from './tzkt'
5
+ import { createTzktApi } from './tzkt'
7
6
 
8
7
  /**
9
8
  * Mainnet integration tests for multi-asset FA2 support (LIVE-29210).
@@ -33,18 +32,8 @@ const mainnetConfig = (): TezosCoinConfig =>
33
32
  }) as TezosCoinConfig
34
33
 
35
34
  describe('TzKT multi-asset FA2 — mainnet', () => {
36
- let originalGetCoinConfig: () => TezosCoinConfig
37
-
38
- beforeAll(() => {
39
- originalGetCoinConfig = coinConfig.getCoinConfig
40
- coinConfig.setCoinConfig(mainnetConfig)
41
- })
42
-
43
- afterAll(() => {
44
- if (originalGetCoinConfig) {
45
- coinConfig.setCoinConfig(originalGetCoinConfig)
46
- }
47
- })
35
+ const config: TezosCoinConfig = mainnetConfig()
36
+ const api = createTzktApi(config)
48
37
 
49
38
  describe('getTokensBalances', () => {
50
39
  it('returns balances for multiple tokenIds on the same contract', async () => {
@@ -13,15 +13,13 @@ import type {
13
13
  APITokenBalance,
14
14
  APITokenTransfer,
15
15
  APITransactionType,
16
- AccountsGetOperationsOptions,
17
16
  } from './types'
18
17
  // tzkt.ts imports `network` from "@ledgerhq/live-network" (the package root, not the /network
19
18
  // subpath). We mock the same path so Jest's module registry intercepts those calls.
20
19
  import network from '@ledgerhq/live-network'
21
20
  import { log } from '@ledgerhq/logs'
22
- import coinConfig from '../config'
23
21
  import { mockConfig } from '../test/config'
24
- import api from './tzkt'
22
+ import { createTzktApi } from './tzkt'
25
23
  import {
26
24
  fetchAllTransactions,
27
25
  fetchBlockDelegations,
@@ -35,6 +33,18 @@ jest.mock('@ledgerhq/logs')
35
33
  const mockedNetwork = jest.mocked(network)
36
34
  const mockedLog = jest.mocked(log)
37
35
 
36
+ // The tzkt module is NOT mocked here. `createTzktApi(config)` returns a plain object whose methods
37
+ // close over `config.explorer.url` and delegate to the mocked `network`. Each method also calls its
38
+ // sibling methods through this same object (closure, not `this`), so spying on the instance's
39
+ // methods (see `spyOnApi`) intercepts intra-object calls such as `getAccountTokenTransfers` →
40
+ // `getOperationsTransactions`.
41
+ //
42
+ // The paginated helpers (`fetchAllTransactions`, `fetchBlock*`) are top-level functions that build
43
+ // their OWN fresh `createTzktApi(config)` instance internally, so spying on `api` cannot intercept
44
+ // them. Those tests therefore drive the mocked `network` directly.
45
+ const config = mockConfig as TezosCoinConfig
46
+ const api = createTzktApi(config)
47
+
38
48
  // ---------------------------------------------------------------------------
39
49
  // Helpers
40
50
  // ---------------------------------------------------------------------------
@@ -106,10 +116,6 @@ function spyOnApi<K extends keyof typeof api>(method: K) {
106
116
  // ---------------------------------------------------------------------------
107
117
 
108
118
  describe('tzkt network API', () => {
109
- beforeAll(() => {
110
- coinConfig.setCoinConfig((): TezosCoinConfig => mockConfig as TezosCoinConfig)
111
- })
112
-
113
119
  afterEach(() => {
114
120
  localSpies.splice(0).forEach((spy) => spy.mockRestore())
115
121
  jest.clearAllMocks()
@@ -847,59 +853,63 @@ describe('tzkt network API', () => {
847
853
  // fetchAllTransactions
848
854
  // -------------------------------------------------------------------------
849
855
 
856
+ // `fetchAllTransactions` builds its own internal `createTzktApi(config)` instance and calls
857
+ // `getAccountOperations` on it, so a spy on the local `api` cannot intercept it. Instead we drive
858
+ // the mocked `network`: each `getAccountOperations` call maps to exactly one `network(...)` whose
859
+ // `.data` is the page. The page cursor (`lastId`) is serialized into the request URL.
850
860
  describe('fetchAllTransactions', () => {
851
861
  it('returns empty when the first page is empty', async () => {
852
- spyOnApi('getAccountOperations').mockResolvedValue([])
862
+ mockedNetwork.mockReturnValue(networkResponse([]) as ReturnType<typeof network>)
853
863
 
854
- const result = await fetchAllTransactions('tz1acc')
864
+ const result = await fetchAllTransactions(config, 'tz1acc')
855
865
 
856
866
  expect(result).toEqual([])
857
867
  })
858
868
 
859
869
  it('concatenates pages until an empty response', async () => {
860
- const spy = jest
861
- .spyOn(api, 'getAccountOperations')
862
- .mockResolvedValueOnce([makeOperation(1)])
863
- .mockResolvedValueOnce([makeOperation(2)])
864
- .mockResolvedValueOnce([])
870
+ mockedNetwork
871
+ .mockReturnValueOnce(networkResponse([makeOperation(1)]) as ReturnType<typeof network>)
872
+ .mockReturnValueOnce(networkResponse([makeOperation(2)]) as ReturnType<typeof network>)
873
+ .mockReturnValueOnce(networkResponse([]) as ReturnType<typeof network>)
865
874
 
866
- const result = await fetchAllTransactions('tz1acc')
875
+ const result = await fetchAllTransactions(config, 'tz1acc')
867
876
 
868
877
  expect(result).toEqual([makeOperation(1), makeOperation(2)])
869
- expect(spy).toHaveBeenCalledTimes(3)
870
- expect(spy).toHaveBeenNthCalledWith(1, 'tz1acc', {
871
- lastId: undefined,
872
- sort: 'Ascending',
873
- 'level.ge': 0,
874
- })
875
- expect(spy).toHaveBeenNthCalledWith(2, 'tz1acc', {
876
- lastId: 1,
877
- sort: 'Ascending',
878
- 'level.ge': 0,
879
- })
878
+ expect(mockedNetwork).toHaveBeenCalledTimes(3)
879
+ // First page: no cursor (lastId undefined is stripped from the query), sort ascending.
880
+ const firstUrl = (mockedNetwork.mock.calls[0][0] as { url: string }).url
881
+ expect(firstUrl).toContain('/v1/accounts/tz1acc/operations')
882
+ expect(firstUrl).not.toMatch(/lastId=/)
883
+ expect(firstUrl).toContain('sort=Ascending')
884
+ expect(firstUrl).toContain('level.ge=0')
885
+ // Second page: cursor advances to the id of the last op returned by the first page.
886
+ const secondUrl = (mockedNetwork.mock.calls[1][0] as { url: string }).url
887
+ expect(secondUrl).toContain('lastId=1')
888
+ expect(secondUrl).toContain('sort=Ascending')
889
+ expect(secondUrl).toContain('level.ge=0')
880
890
  })
881
891
 
882
892
  it('logs and returns when the last operation has a falsy id', async () => {
883
893
  const opMissingId = { ...makeOperation(1), id: 0 } as APIOperation
884
- spyOnApi('getAccountOperations').mockResolvedValue([opMissingId])
894
+ mockedNetwork.mockReturnValue(networkResponse([opMissingId]) as ReturnType<typeof network>)
885
895
 
886
- const result = await fetchAllTransactions('tz1acc')
896
+ const result = await fetchAllTransactions(config, 'tz1acc')
887
897
 
888
898
  expect(result).toEqual([opMissingId])
889
899
  expect(mockedLog).toHaveBeenCalledWith('tezos', 'id missing!')
890
900
  })
891
901
 
892
902
  it('stops after maxTxQuery iterations', async () => {
893
- const spy = jest
894
- .spyOn(api, 'getAccountOperations')
895
- .mockImplementation(async (_addr: string, opts: AccountsGetOperationsOptions) => {
896
- // Always one op with a truthy id so the loop would continue without the cap
897
- return [makeOperation((opts.lastId ?? 0) + 1)]
898
- })
903
+ // Always return one op with a truthy id so the loop would continue without the cap. The id
904
+ // increases every call so cursors keep advancing (matching the previous spy implementation).
905
+ let nextId = 0
906
+ mockedNetwork.mockImplementation(
907
+ () => networkResponse([makeOperation(++nextId)]) as ReturnType<typeof network>
908
+ )
899
909
 
900
- const result = await fetchAllTransactions('tz1acc')
910
+ const result = await fetchAllTransactions(config, 'tz1acc')
901
911
 
902
- expect(spy).toHaveBeenCalledTimes(mockConfig.explorer.maxTxQuery)
912
+ expect(mockedNetwork).toHaveBeenCalledTimes(mockConfig.explorer.maxTxQuery)
903
913
  expect(result).toHaveLength(mockConfig.explorer.maxTxQuery)
904
914
  })
905
915
  })
@@ -1032,20 +1042,23 @@ describe('tzkt network API', () => {
1032
1042
  // fetchBlockTransactions — pagination logic
1033
1043
  // -------------------------------------------------------------------------
1034
1044
 
1045
+ // `fetchBlockTransactions` builds its own internal `createTzktApi(config)` and calls
1046
+ // `getBlockTransactionsPage` on it, so a spy on the local `api` cannot intercept it. Each page
1047
+ // fetch maps to one `network(...)`; the cursor is passed through as the `offset.cr` param.
1035
1048
  describe('fetchBlockTransactions', () => {
1036
1049
  it('returns an empty array when the first page is empty', async () => {
1037
- spyOnApi('getBlockTransactionsPage').mockResolvedValue([])
1050
+ mockedNetwork.mockReturnValue(networkResponse([]) as ReturnType<typeof network>)
1038
1051
 
1039
- const result = await fetchBlockTransactions(100)
1052
+ const result = await fetchBlockTransactions(config, 100)
1040
1053
 
1041
1054
  expect(result).toEqual([])
1042
1055
  })
1043
1056
 
1044
1057
  it('returns a single page when the page is smaller than the page size', async () => {
1045
1058
  const txs = [makeTxItem(1), makeTxItem(2)]
1046
- spyOnApi('getBlockTransactionsPage').mockResolvedValue(txs)
1059
+ mockedNetwork.mockReturnValue(networkResponse(txs) as ReturnType<typeof network>)
1047
1060
 
1048
- const result = await fetchBlockTransactions(100)
1061
+ const result = await fetchBlockTransactions(config, 100)
1049
1062
 
1050
1063
  expect(result).toEqual(txs)
1051
1064
  })
@@ -1054,28 +1067,32 @@ describe('tzkt network API', () => {
1054
1067
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeTxItem(i + 1))
1055
1068
  const lastPage = [makeTxItem(1001), makeTxItem(1002)]
1056
1069
 
1057
- const spy = jest
1058
- .spyOn(api, 'getBlockTransactionsPage')
1059
- .mockResolvedValueOnce(fullPage)
1060
- .mockResolvedValueOnce(lastPage)
1070
+ mockedNetwork
1071
+ .mockReturnValueOnce(networkResponse(fullPage) as ReturnType<typeof network>)
1072
+ .mockReturnValueOnce(networkResponse(lastPage) as ReturnType<typeof network>)
1061
1073
 
1062
- const result = await fetchBlockTransactions(100)
1074
+ const result = await fetchBlockTransactions(config, 100)
1063
1075
 
1064
1076
  expect(result).toHaveLength(1002)
1065
- expect(spy).toHaveBeenCalledTimes(2)
1066
- expect(spy).toHaveBeenNthCalledWith(1, 100, undefined)
1077
+ expect(mockedNetwork).toHaveBeenCalledTimes(2)
1078
+ const firstParams = (mockedNetwork.mock.calls[0][0] as { params: Record<string, unknown> })
1079
+ .params
1080
+ expect(firstParams).toMatchObject({ level: 100 })
1081
+ expect(firstParams).not.toHaveProperty('offset.cr')
1067
1082
  // cursor = last item id of first page = 1000
1068
- expect(spy).toHaveBeenNthCalledWith(2, 100, 1000)
1083
+ const secondParams = (mockedNetwork.mock.calls[1][0] as { params: Record<string, unknown> })
1084
+ .params
1085
+ expect(secondParams).toMatchObject({ level: 100, 'offset.cr': 1000 })
1069
1086
  })
1070
1087
 
1071
1088
  it('stops after maxTxQuery iterations regardless of page length', async () => {
1072
1089
  // maxTxQuery = 100 (from mockConfig); every call returns a full page to keep the loop going
1073
1090
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeTxItem(i + 1))
1074
- const spy = spyOnApi('getBlockTransactionsPage').mockResolvedValue(fullPage)
1091
+ mockedNetwork.mockReturnValue(networkResponse(fullPage) as ReturnType<typeof network>)
1075
1092
 
1076
- const result = await fetchBlockTransactions(100)
1093
+ const result = await fetchBlockTransactions(config, 100)
1077
1094
 
1078
- expect(spy).toHaveBeenCalledTimes(100)
1095
+ expect(mockedNetwork).toHaveBeenCalledTimes(100)
1079
1096
  expect(result).toHaveLength(100_000)
1080
1097
  expect(mockedLog).toHaveBeenCalledWith(
1081
1098
  'tezos',
@@ -1088,20 +1105,21 @@ describe('tzkt network API', () => {
1088
1105
  // fetchBlockTokenTransfers — pagination logic
1089
1106
  // -------------------------------------------------------------------------
1090
1107
 
1108
+ // Driven through the mocked `network` (see the note on `fetchBlockTransactions`).
1091
1109
  describe('fetchBlockTokenTransfers', () => {
1092
1110
  it('returns an empty array when the first page is empty', async () => {
1093
- spyOnApi('getBlockTokenTransfersPage').mockResolvedValue([])
1111
+ mockedNetwork.mockReturnValue(networkResponse([]) as ReturnType<typeof network>)
1094
1112
 
1095
- const result = await fetchBlockTokenTransfers(100)
1113
+ const result = await fetchBlockTokenTransfers(config, 100)
1096
1114
 
1097
1115
  expect(result).toEqual([])
1098
1116
  })
1099
1117
 
1100
1118
  it('returns a single page when the page is smaller than the page size', async () => {
1101
1119
  const items = [makeTokenItem(1), makeTokenItem(2)]
1102
- spyOnApi('getBlockTokenTransfersPage').mockResolvedValue(items)
1120
+ mockedNetwork.mockReturnValue(networkResponse(items) as ReturnType<typeof network>)
1103
1121
 
1104
- const result = await fetchBlockTokenTransfers(100)
1122
+ const result = await fetchBlockTokenTransfers(config, 100)
1105
1123
 
1106
1124
  expect(result).toEqual(items)
1107
1125
  })
@@ -1110,26 +1128,30 @@ describe('tzkt network API', () => {
1110
1128
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeTokenItem(i + 1))
1111
1129
  const lastPage = [makeTokenItem(1001)]
1112
1130
 
1113
- const spy = jest
1114
- .spyOn(api, 'getBlockTokenTransfersPage')
1115
- .mockResolvedValueOnce(fullPage)
1116
- .mockResolvedValueOnce(lastPage)
1131
+ mockedNetwork
1132
+ .mockReturnValueOnce(networkResponse(fullPage) as ReturnType<typeof network>)
1133
+ .mockReturnValueOnce(networkResponse(lastPage) as ReturnType<typeof network>)
1117
1134
 
1118
- const result = await fetchBlockTokenTransfers(100)
1135
+ const result = await fetchBlockTokenTransfers(config, 100)
1119
1136
 
1120
1137
  expect(result).toHaveLength(1001)
1121
- expect(spy).toHaveBeenCalledTimes(2)
1122
- expect(spy).toHaveBeenNthCalledWith(1, 100, undefined)
1123
- expect(spy).toHaveBeenNthCalledWith(2, 100, 1000)
1138
+ expect(mockedNetwork).toHaveBeenCalledTimes(2)
1139
+ const firstParams = (mockedNetwork.mock.calls[0][0] as { params: Record<string, unknown> })
1140
+ .params
1141
+ expect(firstParams).toMatchObject({ level: 100 })
1142
+ expect(firstParams).not.toHaveProperty('offset.cr')
1143
+ const secondParams = (mockedNetwork.mock.calls[1][0] as { params: Record<string, unknown> })
1144
+ .params
1145
+ expect(secondParams).toMatchObject({ level: 100, 'offset.cr': 1000 })
1124
1146
  })
1125
1147
 
1126
1148
  it('stops after maxTxQuery iterations regardless of page length', async () => {
1127
1149
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeTokenItem(i + 1))
1128
- const spy = spyOnApi('getBlockTokenTransfersPage').mockResolvedValue(fullPage)
1150
+ mockedNetwork.mockReturnValue(networkResponse(fullPage) as ReturnType<typeof network>)
1129
1151
 
1130
- const result = await fetchBlockTokenTransfers(100)
1152
+ const result = await fetchBlockTokenTransfers(config, 100)
1131
1153
 
1132
- expect(spy).toHaveBeenCalledTimes(100)
1154
+ expect(mockedNetwork).toHaveBeenCalledTimes(100)
1133
1155
  expect(result).toHaveLength(100_000)
1134
1156
  expect(mockedLog).toHaveBeenCalledWith(
1135
1157
  'tezos',
@@ -1181,20 +1203,21 @@ describe('tzkt network API', () => {
1181
1203
  // fetchBlockDelegations — pagination logic
1182
1204
  // -------------------------------------------------------------------------
1183
1205
 
1206
+ // Driven through the mocked `network` (see the note on `fetchBlockTransactions`).
1184
1207
  describe('fetchBlockDelegations', () => {
1185
1208
  it('returns an empty array when the first page is empty', async () => {
1186
- spyOnApi('getBlockDelegationsPage').mockResolvedValue([])
1209
+ mockedNetwork.mockReturnValue(networkResponse([]) as ReturnType<typeof network>)
1187
1210
 
1188
- const result = await fetchBlockDelegations(100)
1211
+ const result = await fetchBlockDelegations(config, 100)
1189
1212
 
1190
1213
  expect(result).toEqual([])
1191
1214
  })
1192
1215
 
1193
1216
  it('returns a single page when the page is smaller than the page size', async () => {
1194
1217
  const items = [makeDelegationItem(1), makeDelegationItem(2)]
1195
- spyOnApi('getBlockDelegationsPage').mockResolvedValue(items)
1218
+ mockedNetwork.mockReturnValue(networkResponse(items) as ReturnType<typeof network>)
1196
1219
 
1197
- const result = await fetchBlockDelegations(100)
1220
+ const result = await fetchBlockDelegations(config, 100)
1198
1221
 
1199
1222
  expect(result).toEqual(items)
1200
1223
  })
@@ -1203,26 +1226,30 @@ describe('tzkt network API', () => {
1203
1226
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeDelegationItem(i + 1))
1204
1227
  const lastPage = [makeDelegationItem(1001)]
1205
1228
 
1206
- const spy = jest
1207
- .spyOn(api, 'getBlockDelegationsPage')
1208
- .mockResolvedValueOnce(fullPage)
1209
- .mockResolvedValueOnce(lastPage)
1229
+ mockedNetwork
1230
+ .mockReturnValueOnce(networkResponse(fullPage) as ReturnType<typeof network>)
1231
+ .mockReturnValueOnce(networkResponse(lastPage) as ReturnType<typeof network>)
1210
1232
 
1211
- const result = await fetchBlockDelegations(100)
1233
+ const result = await fetchBlockDelegations(config, 100)
1212
1234
 
1213
1235
  expect(result).toHaveLength(1001)
1214
- expect(spy).toHaveBeenCalledTimes(2)
1215
- expect(spy).toHaveBeenNthCalledWith(1, 100, undefined)
1216
- expect(spy).toHaveBeenNthCalledWith(2, 100, 1000)
1236
+ expect(mockedNetwork).toHaveBeenCalledTimes(2)
1237
+ const firstParams = (mockedNetwork.mock.calls[0][0] as { params: Record<string, unknown> })
1238
+ .params
1239
+ expect(firstParams).toMatchObject({ level: 100 })
1240
+ expect(firstParams).not.toHaveProperty('offset.cr')
1241
+ const secondParams = (mockedNetwork.mock.calls[1][0] as { params: Record<string, unknown> })
1242
+ .params
1243
+ expect(secondParams).toMatchObject({ level: 100, 'offset.cr': 1000 })
1217
1244
  })
1218
1245
 
1219
1246
  it('stops after maxTxQuery iterations regardless of page length', async () => {
1220
1247
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeDelegationItem(i + 1))
1221
- const spy = spyOnApi('getBlockDelegationsPage').mockResolvedValue(fullPage)
1248
+ mockedNetwork.mockReturnValue(networkResponse(fullPage) as ReturnType<typeof network>)
1222
1249
 
1223
- const result = await fetchBlockDelegations(100)
1250
+ const result = await fetchBlockDelegations(config, 100)
1224
1251
 
1225
- expect(spy).toHaveBeenCalledTimes(100)
1252
+ expect(mockedNetwork).toHaveBeenCalledTimes(100)
1226
1253
  expect(result).toHaveLength(100_000)
1227
1254
  expect(mockedLog).toHaveBeenCalledWith(
1228
1255
  'tezos',
@@ -1274,20 +1301,21 @@ describe('tzkt network API', () => {
1274
1301
  // fetchBlockStaking — pagination logic
1275
1302
  // -------------------------------------------------------------------------
1276
1303
 
1304
+ // Driven through the mocked `network` (see the note on `fetchBlockTransactions`).
1277
1305
  describe('fetchBlockStaking', () => {
1278
1306
  it('returns an empty array when the first page is empty', async () => {
1279
- spyOnApi('getBlockStakingPage').mockResolvedValue([])
1307
+ mockedNetwork.mockReturnValue(networkResponse([]) as ReturnType<typeof network>)
1280
1308
 
1281
- const result = await fetchBlockStaking(100)
1309
+ const result = await fetchBlockStaking(config, 100)
1282
1310
 
1283
1311
  expect(result).toEqual([])
1284
1312
  })
1285
1313
 
1286
1314
  it('returns a single page when the page is smaller than the page size', async () => {
1287
1315
  const items = [makeStakingItem(1), makeStakingItem(2)]
1288
- spyOnApi('getBlockStakingPage').mockResolvedValue(items)
1316
+ mockedNetwork.mockReturnValue(networkResponse(items) as ReturnType<typeof network>)
1289
1317
 
1290
- const result = await fetchBlockStaking(100)
1318
+ const result = await fetchBlockStaking(config, 100)
1291
1319
 
1292
1320
  expect(result).toEqual(items)
1293
1321
  })
@@ -1296,26 +1324,30 @@ describe('tzkt network API', () => {
1296
1324
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeStakingItem(i + 1))
1297
1325
  const lastPage = [makeStakingItem(1001)]
1298
1326
 
1299
- const spy = jest
1300
- .spyOn(api, 'getBlockStakingPage')
1301
- .mockResolvedValueOnce(fullPage)
1302
- .mockResolvedValueOnce(lastPage)
1327
+ mockedNetwork
1328
+ .mockReturnValueOnce(networkResponse(fullPage) as ReturnType<typeof network>)
1329
+ .mockReturnValueOnce(networkResponse(lastPage) as ReturnType<typeof network>)
1303
1330
 
1304
- const result = await fetchBlockStaking(100)
1331
+ const result = await fetchBlockStaking(config, 100)
1305
1332
 
1306
1333
  expect(result).toHaveLength(1001)
1307
- expect(spy).toHaveBeenCalledTimes(2)
1308
- expect(spy).toHaveBeenNthCalledWith(1, 100, undefined)
1309
- expect(spy).toHaveBeenNthCalledWith(2, 100, 1000)
1334
+ expect(mockedNetwork).toHaveBeenCalledTimes(2)
1335
+ const firstParams = (mockedNetwork.mock.calls[0][0] as { params: Record<string, unknown> })
1336
+ .params
1337
+ expect(firstParams).toMatchObject({ level: 100 })
1338
+ expect(firstParams).not.toHaveProperty('offset.cr')
1339
+ const secondParams = (mockedNetwork.mock.calls[1][0] as { params: Record<string, unknown> })
1340
+ .params
1341
+ expect(secondParams).toMatchObject({ level: 100, 'offset.cr': 1000 })
1310
1342
  })
1311
1343
 
1312
1344
  it('stops after maxTxQuery iterations regardless of page length', async () => {
1313
1345
  const fullPage = Array.from({ length: 1000 }, (_, i) => makeStakingItem(i + 1))
1314
- const spy = spyOnApi('getBlockStakingPage').mockResolvedValue(fullPage)
1346
+ mockedNetwork.mockReturnValue(networkResponse(fullPage) as ReturnType<typeof network>)
1315
1347
 
1316
- const result = await fetchBlockStaking(100)
1348
+ const result = await fetchBlockStaking(config, 100)
1317
1349
 
1318
- expect(spy).toHaveBeenCalledTimes(100)
1350
+ expect(mockedNetwork).toHaveBeenCalledTimes(100)
1319
1351
  expect(result).toHaveLength(100_000)
1320
1352
  expect(mockedLog).toHaveBeenCalledWith(
1321
1353
  'tezos',