@ledgerhq/coin-tezos 10.2.0 → 10.2.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 (79) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/lib/api/index.d.ts +1 -1
  3. package/lib/api/index.d.ts.map +1 -1
  4. package/lib/api/index.js +1 -1
  5. package/lib/api/index.js.map +1 -1
  6. package/lib/logic/craftRawOperations.js +26 -17
  7. package/lib/logic/craftRawOperations.js.map +1 -1
  8. package/lib/logic/estimateFees.js +4 -4
  9. package/lib/logic/estimateFees.js.map +1 -1
  10. package/lib/logic/listOperations.d.ts.map +1 -1
  11. package/lib/logic/listOperations.js +9 -7
  12. package/lib/logic/listOperations.js.map +1 -1
  13. package/lib/logic/validateIntent.d.ts.map +1 -1
  14. package/lib/logic/validateIntent.js +26 -8
  15. package/lib/logic/validateIntent.js.map +1 -1
  16. package/lib/network/tzkt.js +1 -1
  17. package/lib/network/tzkt.js.map +1 -1
  18. package/lib/test/config.d.ts +2 -22
  19. package/lib/test/config.d.ts.map +1 -1
  20. package/lib/test/config.js.map +1 -1
  21. package/lib/utils.d.ts +0 -3
  22. package/lib/utils.d.ts.map +1 -1
  23. package/lib/utils.js +17 -3
  24. package/lib/utils.js.map +1 -1
  25. package/lib-es/api/index.d.ts +1 -1
  26. package/lib-es/api/index.d.ts.map +1 -1
  27. package/lib-es/api/index.js +1 -1
  28. package/lib-es/api/index.js.map +1 -1
  29. package/lib-es/logic/craftRawOperations.js +26 -17
  30. package/lib-es/logic/craftRawOperations.js.map +1 -1
  31. package/lib-es/logic/estimateFees.js +4 -4
  32. package/lib-es/logic/estimateFees.js.map +1 -1
  33. package/lib-es/logic/listOperations.d.ts.map +1 -1
  34. package/lib-es/logic/listOperations.js +9 -7
  35. package/lib-es/logic/listOperations.js.map +1 -1
  36. package/lib-es/logic/validateIntent.d.ts.map +1 -1
  37. package/lib-es/logic/validateIntent.js +27 -9
  38. package/lib-es/logic/validateIntent.js.map +1 -1
  39. package/lib-es/network/tzkt.js +1 -1
  40. package/lib-es/network/tzkt.js.map +1 -1
  41. package/lib-es/test/config.d.ts +2 -22
  42. package/lib-es/test/config.d.ts.map +1 -1
  43. package/lib-es/test/config.js.map +1 -1
  44. package/lib-es/utils.d.ts +0 -3
  45. package/lib-es/utils.d.ts.map +1 -1
  46. package/lib-es/utils.js +17 -3
  47. package/lib-es/utils.js.map +1 -1
  48. package/package.json +2 -2
  49. package/src/api/index-mainnet.integ.test.ts +40 -50
  50. package/src/api/index.integ.test.ts +60 -45
  51. package/src/api/index.test.ts +229 -180
  52. package/src/api/index.ts +5 -4
  53. package/src/logic/broadcast.integ.test.ts +2 -7
  54. package/src/logic/craftRawOperations.test.ts +11 -10
  55. package/src/logic/craftRawOperations.ts +35 -19
  56. package/src/logic/craftTransaction.integ.test.ts +2 -2
  57. package/src/logic/craftTransaction.test.ts +63 -95
  58. package/src/logic/estimateFees.integ.test.ts +19 -20
  59. package/src/logic/estimateFees.test.ts +30 -63
  60. package/src/logic/estimateFees.ts +4 -4
  61. package/src/logic/getBalance.test.ts +6 -6
  62. package/src/logic/getBlock.integ.test.ts +20 -21
  63. package/src/logic/getBlock.test.ts +44 -51
  64. package/src/logic/getBlockInfo.integ.test.ts +13 -14
  65. package/src/logic/getBlockInfo.test.ts +8 -7
  66. package/src/logic/getStakes.test.ts +17 -17
  67. package/src/logic/listOperations.integ.test.ts +40 -37
  68. package/src/logic/listOperations.test.ts +34 -31
  69. package/src/logic/listOperations.ts +19 -19
  70. package/src/logic/validateIntent.test.ts +145 -20
  71. package/src/logic/validateIntent.ts +32 -8
  72. package/src/network/bakers.integ.test.ts +1 -1
  73. package/src/network/bakers.test.ts +7 -15
  74. package/src/network/tzkt.integ.test.ts +13 -14
  75. package/src/network/tzkt.test.ts +194 -259
  76. package/src/network/tzkt.ts +1 -1
  77. package/src/test/config.ts +3 -1
  78. package/src/utils.ts +18 -3
  79. package/.oxlintrc.json +0 -20
package/src/api/index.ts CHANGED
@@ -11,13 +11,14 @@ import type {
11
11
  ListOperationsOptions,
12
12
  Page,
13
13
  } from '@ledgerhq/coin-module-framework/api/index'
14
+ import type { Operation } from '@ledgerhq/coin-module-framework/api/index'
14
15
  import type {
15
16
  BalanceOptions,
16
17
  FeeEstimation,
17
18
  TransactionIntent,
18
19
  } from '@ledgerhq/coin-module-framework/api/types'
19
20
  import { rejectBalanceOptions } from '@ledgerhq/coin-module-framework/api/getBalance/rejectBalanceOptions'
20
- import { IncorrectTypeError, type Operation } from '@ledgerhq/coin-module-framework/api/index'
21
+ import { IncorrectTypeError } from '@ledgerhq/coin-module-framework/api/index'
21
22
  import { craftTransactionData } from '@ledgerhq/coin-module-framework/logic/craftTransactionData'
22
23
  import { log } from '@ledgerhq/logs'
23
24
  import { getRevealFee } from '@taquito/taquito'
@@ -128,7 +129,7 @@ async function craft(
128
129
 
129
130
  const tezosMode = resolveTezosOperationMode(transactionIntent.type, transactionIntent.asset)
130
131
  const mappedType: TezosOperationMode =
131
- tezosMode === 'send_token' ? 'send_token' : (transactionIntent.type as TezosOperationMode)
132
+ tezosMode === 'send_token' ? 'send_token' : transactionIntent.type
132
133
  const tokenCraftInfo =
133
134
  tezosMode === 'send_token' ? parseTezosTokenAsset(transactionIntent.asset)! : undefined
134
135
 
@@ -329,9 +330,9 @@ async function estimate(
329
330
  txFee: estimation.fees,
330
331
  },
331
332
  }
332
- } catch (error: any) {
333
+ } catch (error: unknown) {
333
334
  // Handle PublicKeyNotFoundError
334
- if (error?.message?.includes('Public key not found')) {
335
+ if (error instanceof Error && error.message.includes('Public key not found')) {
335
336
  const apiAccount = await api.getAccountByAddress(transactionIntent.recipient)
336
337
  const storageLimit =
337
338
  !hasEmptyBalance(apiAccount) || transactionIntent.type === 'stake' ? 0n : 277n
@@ -4,17 +4,12 @@
4
4
  import type { TezosContext } from '../config'
5
5
  import { InMemorySigner } from '@taquito/signer'
6
6
  import { OpKind, TezosToolkit } from '@taquito/taquito'
7
+ import { mockConfig } from '../test/config'
7
8
  import { broadcast } from './broadcast'
8
9
 
9
10
  describe('Broadcast', () => {
10
11
  const context: TezosContext = {
11
- config: async () =>
12
- ({
13
- status: { type: 'active' },
14
- node: {
15
- url: 'https://xtz-node.api.live.ledger.com',
16
- },
17
- }) as any,
12
+ config: async () => mockConfig,
18
13
  logger: () => undefined,
19
14
  }
20
15
 
@@ -1,9 +1,11 @@
1
1
  // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import type { TezosCoinConfig, TezosContext } from '../config'
4
+ import type { TezosContext } from '../config'
5
+ import type { DeepPartialReturn } from '@ledgerhq/coin-module-framework/test/utils'
5
6
  import { OpKind } from '@taquito/rpc'
6
7
  import { getPkhfromPk } from '@taquito/utils'
8
+ import { mockConfig } from '../test/config'
7
9
  import { UnsupportedOperationKind } from '../types/errors'
8
10
  import { craftRawOperations } from './craftRawOperations'
9
11
  import { getTezosToolkit } from './tezosToolkit'
@@ -45,16 +47,15 @@ describe('craftRawOperations', () => {
45
47
  }
46
48
 
47
49
  const context: TezosContext = {
48
- config: async () =>
49
- ({
50
- fees: { minFees: 100, minRevealGasLimit: 0, minStorageLimit: 0 },
51
- }) as unknown as TezosCoinConfig,
50
+ config: async () => ({ ...mockConfig, fees: { ...mockConfig.fees, minFees: 100 } }),
52
51
  logger: () => undefined,
53
52
  }
54
53
 
55
54
  beforeEach(() => {
56
55
  jest.clearAllMocks()
57
- ;(getTezosToolkit as jest.Mock).mockReturnValue(mockTezosToolkit)
56
+ jest
57
+ .mocked<DeepPartialReturn<typeof getTezosToolkit>>(getTezosToolkit)
58
+ .mockReturnValue(mockTezosToolkit)
58
59
  mockTezosToolkit.rpc.getBlock.mockResolvedValue({ hash: 'BLockHash' })
59
60
  mockTezosToolkit.rpc.forgeOperations.mockResolvedValue('deadbeef')
60
61
  // Default: account already revealed (no leading reveal op).
@@ -162,7 +163,7 @@ describe('craftRawOperations', () => {
162
163
 
163
164
  it('uses the trailing op estimate when a reveal estimate is prepended for an unrevealed source', async () => {
164
165
  mockTezosToolkit.rpc.getManagerKey.mockResolvedValue(null)
165
- ;(getPkhfromPk as jest.Mock).mockReturnValue(SENDER)
166
+ jest.mocked(getPkhfromPk).mockReturnValue(SENDER)
166
167
  mockTezosToolkit.estimate.reveal.mockResolvedValue({
167
168
  suggestedFeeMutez: 374,
168
169
  gasLimit: 1000,
@@ -224,7 +225,7 @@ describe('craftRawOperations', () => {
224
225
 
225
226
  it('prepends a reveal when the sender is not revealed', async () => {
226
227
  mockTezosToolkit.rpc.getManagerKey.mockResolvedValue(null)
227
- ;(getPkhfromPk as jest.Mock).mockReturnValue(SENDER)
228
+ jest.mocked(getPkhfromPk).mockReturnValue(SENDER)
228
229
  mockTezosToolkit.estimate.reveal.mockResolvedValue({
229
230
  suggestedFeeMutez: 374,
230
231
  gasLimit: 1000,
@@ -248,7 +249,7 @@ describe('craftRawOperations', () => {
248
249
 
249
250
  it('falls back to SDK reveal helpers when estimate.reveal throws', async () => {
250
251
  mockTezosToolkit.rpc.getManagerKey.mockResolvedValue(null)
251
- ;(getPkhfromPk as jest.Mock).mockReturnValue(SENDER)
252
+ jest.mocked(getPkhfromPk).mockReturnValue(SENDER)
252
253
  mockTezosToolkit.estimate.reveal.mockRejectedValue(new Error('inconsistent_hash'))
253
254
 
254
255
  const ops = [{ kind: 'transaction', destination: RECIPIENT, amount: '1000' }]
@@ -264,7 +265,7 @@ describe('craftRawOperations', () => {
264
265
 
265
266
  it('rejects a reveal whose public key does not match the sender', async () => {
266
267
  mockTezosToolkit.rpc.getManagerKey.mockResolvedValue(null)
267
- ;(getPkhfromPk as jest.Mock).mockReturnValue('tz1someoneElse')
268
+ jest.mocked(getPkhfromPk).mockReturnValue('tz1someoneElse')
268
269
 
269
270
  const ops = [{ kind: 'transaction', destination: RECIPIENT, amount: '1000' }]
270
271
 
@@ -133,31 +133,46 @@ function parseOperations(rawTransaction: string): PartialTezosOperation[] {
133
133
  return parsed.map(validateOperation)
134
134
  }
135
135
 
136
+ function isRecord(v: unknown): v is Record<string, unknown> {
137
+ return !!v && typeof v === 'object' && !Array.isArray(v)
138
+ }
139
+
140
+ function assertTransaction(
141
+ op: Record<string, unknown>
142
+ ): asserts op is PartialTezosTransactionOperation {
143
+ if (!isValidAddress(op.destination)) {
144
+ throw new Error('craftRawOperations: a transaction requires a valid `destination` address')
145
+ }
146
+ assertMutezAmount(op.amount)
147
+ }
148
+
149
+ function assertDelegation(
150
+ op: Record<string, unknown>
151
+ ): asserts op is PartialTezosDelegationOperation {
152
+ // A delegation with no `delegate` is an undelegation; when present it must be a valid
153
+ // implicit account (an empty/invalid string must not be silently treated as absent).
154
+ if (op.delegate !== undefined && !isImplicitAddress(op.delegate)) {
155
+ throw new Error(
156
+ 'craftRawOperations: `delegate` must be a valid implicit account address when present'
157
+ )
158
+ }
159
+ }
160
+
136
161
  function validateOperation(op: unknown): PartialTezosOperation {
137
- if (!op || typeof op !== 'object') {
162
+ if (!isRecord(op)) {
138
163
  throw new Error('craftRawOperations: invalid operation')
139
164
  }
140
- const candidate = op as Record<string, unknown>
141
- assertOptionalLimits(candidate)
142
- switch (candidate.kind) {
165
+ assertOptionalLimits(op)
166
+ switch (op.kind) {
143
167
  case 'transaction':
144
- if (!isValidAddress(candidate.destination)) {
145
- throw new Error('craftRawOperations: a transaction requires a valid `destination` address')
146
- }
147
- assertMutezAmount(candidate.amount)
148
- return candidate as PartialTezosTransactionOperation
168
+ assertTransaction(op)
169
+ return op
149
170
  case 'delegation':
150
- // A delegation with no `delegate` is an undelegation; when present it must be a valid
151
- // implicit account (an empty/invalid string must not be silently treated as absent).
152
- if (candidate.delegate !== undefined && !isImplicitAddress(candidate.delegate)) {
153
- throw new Error(
154
- 'craftRawOperations: `delegate` must be a valid implicit account address when present'
155
- )
156
- }
157
- return candidate as unknown as PartialTezosDelegationOperation
171
+ assertDelegation(op)
172
+ return op
158
173
  default:
159
174
  throw new UnsupportedOperationKind('unsupported operation kind', {
160
- kind: String(candidate.kind),
175
+ kind: String(op.kind),
161
176
  })
162
177
  }
163
178
  }
@@ -284,8 +299,9 @@ function buildOperationContent(
284
299
  // Compile-time exhaustiveness guard: a new PartialTezosOperation kind becomes a type error
285
300
  // here. validateOperation already rejects unknown kinds at runtime before crafting.
286
301
  function assertNever(op: never): never {
302
+ const r: Record<string, unknown> = op
287
303
  throw new UnsupportedOperationKind('unsupported operation kind', {
288
- kind: String((op as { kind?: unknown }).kind),
304
+ kind: String(r.kind),
289
305
  })
290
306
  }
291
307
 
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import type { TezosCoinConfig, TezosContext } from '../config'
4
+ import type { TezosContext } from '../config'
5
5
  import { OpKind } from '@taquito/rpc'
6
6
  import { mockConfig } from '../test/config'
7
7
  import { craftTransaction } from '.'
@@ -16,7 +16,7 @@ import { craftTransaction } from '.'
16
16
  describe('Tezos Api', () => {
17
17
  const address = 'tz1heMGVHQnx7ALDcDKqez8fan64Eyicw4DJ'
18
18
  const context: TezosContext = {
19
- config: async () => mockConfig as TezosCoinConfig,
19
+ config: async () => mockConfig,
20
20
  logger: () => undefined,
21
21
  }
22
22
  it('should craft a send transaction', async () => {
@@ -2,19 +2,14 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import type { TezosCoinConfig, TezosContext } from '../config'
5
+ import type { DeepPartialReturn } from '@ledgerhq/coin-module-framework/test/utils'
5
6
  import { OpKind } from '@taquito/rpc'
6
7
  import { getRevealFee } from '@taquito/taquito'
8
+ import { mockConfig } from '../test/config'
7
9
  import { craftTransaction, rawEncode } from './craftTransaction'
8
10
  import { getTezosToolkit } from './tezosToolkit'
9
11
 
10
- type TransactionType =
11
- | 'send'
12
- | 'delegate'
13
- | 'undelegate'
14
- | 'send_token'
15
- | 'stake'
16
- | 'unstake'
17
- | 'finalize_unstake'
12
+ type TransactionInput = Parameters<typeof craftTransaction>[2]
18
13
 
19
14
  jest.mock('./tezosToolkit')
20
15
 
@@ -52,16 +47,18 @@ describe('craftTransaction', () => {
52
47
 
53
48
  beforeEach(() => {
54
49
  jest.clearAllMocks()
55
- ;(getTezosToolkit as jest.Mock).mockReturnValue(mockTezosToolkit)
56
- currentConfig = {} as unknown as TezosCoinConfig
50
+ jest
51
+ .mocked<DeepPartialReturn<typeof getTezosToolkit>>(getTezosToolkit)
52
+ .mockReturnValue(mockTezosToolkit)
53
+ currentConfig = mockConfig
57
54
  })
58
55
 
59
56
  it('should craft a send transaction', async () => {
60
57
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
61
58
 
62
59
  const account = { address: 'tz1...' }
63
- const transaction = {
64
- type: 'send' as TransactionType,
60
+ const transaction: TransactionInput = {
61
+ type: 'send',
65
62
  recipient: 'tz2...',
66
63
  amount: BigInt(1000),
67
64
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -88,8 +85,8 @@ describe('craftTransaction', () => {
88
85
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
89
86
 
90
87
  const account = { address: 'tz1...' }
91
- const transaction = {
92
- type: 'delegate' as TransactionType,
88
+ const transaction: TransactionInput = {
89
+ type: 'delegate',
93
90
  recipient: 'tz2...',
94
91
  amount: BigInt(0),
95
92
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -115,8 +112,8 @@ describe('craftTransaction', () => {
115
112
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
116
113
 
117
114
  const account = { address: 'tz1...' }
118
- const transaction = {
119
- type: 'undelegate' as TransactionType,
115
+ const transaction: TransactionInput = {
116
+ type: 'undelegate',
120
117
  recipient: '',
121
118
  amount: BigInt(0),
122
119
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -144,17 +141,11 @@ describe('craftTransaction', () => {
144
141
  storageLimit: 0,
145
142
  suggestedFeeMutez: getRevealFee('tz1...'),
146
143
  })
147
- currentConfig = {
148
- fees: {
149
- minRevealGasLimit: 100,
150
- minFees: 0,
151
- minStorageLimit: 0,
152
- },
153
- } as unknown as TezosCoinConfig
144
+ currentConfig = { ...mockConfig, fees: { ...mockConfig.fees, minRevealGasLimit: 100 } }
154
145
 
155
146
  const account = { address: 'tz1...' }
156
- const transaction = {
157
- type: 'send' as TransactionType,
147
+ const transaction: TransactionInput = {
148
+ type: 'send',
158
149
  recipient: 'tz2...',
159
150
  amount: BigInt(1000),
160
151
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -197,17 +188,14 @@ describe('craftTransaction', () => {
197
188
  })
198
189
  const minFees = 500
199
190
  currentConfig = {
200
- fees: {
201
- minRevealGasLimit: 300,
202
- minFees,
203
- minStorageLimit: 0,
204
- },
205
- } as unknown as TezosCoinConfig
191
+ ...mockConfig,
192
+ fees: { ...mockConfig.fees, minRevealGasLimit: 300, minFees },
193
+ }
206
194
 
207
195
  const account = { address: 'tz2...' }
208
196
  const mainOpFee = 600
209
- const transaction = {
210
- type: 'delegate' as TransactionType,
197
+ const transaction: TransactionInput = {
198
+ type: 'delegate',
211
199
  recipient: 'tz3...',
212
200
  amount: BigInt(0),
213
201
  fee: { fees: String(mainOpFee), gasLimit: '10000', storageLimit: '0' },
@@ -216,11 +204,11 @@ describe('craftTransaction', () => {
216
204
 
217
205
  const result = await craftTransaction(context, account, transaction, publicKey)
218
206
 
219
- const revealOp = result.contents[0]
220
- expect(revealOp.kind).toBe(OpKind.REVEAL)
221
- expect(Number(revealOp.fee ?? 0)).toBe(suggestedRevealFee)
222
- const delegationOp = result.contents[1]
223
- expect(Number(delegationOp.fee ?? 0)).toBe(mainOpFee)
207
+ expect(result.contents[0]).toMatchObject({
208
+ kind: OpKind.REVEAL,
209
+ fee: String(suggestedRevealFee),
210
+ })
211
+ expect(result.contents[1]).toMatchObject({ fee: String(mainOpFee) })
224
212
  })
225
213
 
226
214
  it('craft transaction when default estimation is lesser than minFees', async () => {
@@ -232,16 +220,13 @@ describe('craftTransaction', () => {
232
220
  })
233
221
  const minFees = 500
234
222
  currentConfig = {
235
- fees: {
236
- minRevealGasLimit: 300,
237
- minFees,
238
- minStorageLimit: 25,
239
- },
240
- } as unknown as TezosCoinConfig
223
+ ...mockConfig,
224
+ fees: { ...mockConfig.fees, minRevealGasLimit: 300, minStorageLimit: 25, minFees },
225
+ }
241
226
 
242
227
  const account = { address: 'tz2...' }
243
- const transaction = {
244
- type: 'delegate' as TransactionType,
228
+ const transaction: TransactionInput = {
229
+ type: 'delegate',
245
230
  recipient: 'tz3...',
246
231
  amount: BigInt(0),
247
232
  fee: { fees: '200', gasLimit: '1000', storageLimit: '0' },
@@ -250,11 +235,12 @@ describe('craftTransaction', () => {
250
235
 
251
236
  const result = await craftTransaction(context, account, transaction, publicKey)
252
237
 
253
- const revealOp = result.contents[0]
254
- expect(revealOp.kind).toBe(OpKind.REVEAL)
255
- expect(revealOp.fee).toBe(String(minFees))
256
- expect(revealOp.gas_limit).toBe('300')
257
- expect(revealOp.storage_limit).toBe('25')
238
+ expect(result.contents[0]).toMatchObject({
239
+ kind: OpKind.REVEAL,
240
+ fee: String(minFees),
241
+ gas_limit: '300',
242
+ storage_limit: '25',
243
+ })
258
244
  })
259
245
 
260
246
  it('craft transaction with customFee splits total so reveal and main op respect minFees', async () => {
@@ -266,17 +252,14 @@ describe('craftTransaction', () => {
266
252
  })
267
253
  const minFees = 1000
268
254
  currentConfig = {
269
- fees: {
270
- minRevealGasLimit: 300,
271
- minFees,
272
- minStorageLimit: 0,
273
- },
274
- } as unknown as TezosCoinConfig
255
+ ...mockConfig,
256
+ fees: { ...mockConfig.fees, minRevealGasLimit: 300, minFees },
257
+ }
275
258
 
276
259
  const account = { address: 'tz2TaTpo31sAiX2HBJUTLLdUnqVJR4QjLy1V' }
277
260
  const mainOpFee = 1000
278
- const transaction = {
279
- type: 'delegate' as TransactionType,
261
+ const transaction: TransactionInput = {
262
+ type: 'delegate',
280
263
  recipient: 'tz3Vq38qYD3GEbWcXHMLt5PaASZrkDtEiA8D',
281
264
  amount: BigInt(0),
282
265
  fee: { fees: String(mainOpFee), gasLimit: '10000', storageLimit: '0' },
@@ -286,25 +269,21 @@ describe('craftTransaction', () => {
286
269
  const result = await craftTransaction(context, account, transaction, publicKey)
287
270
 
288
271
  expect(result.contents).toHaveLength(2)
289
- const revealFee = Number(result.contents[0].fee ?? 0)
290
- const delegationFee = Number(result.contents[1].fee ?? 0)
291
- const total = revealFee + delegationFee
292
- expect(revealFee).toBe(minFees)
293
- expect(delegationFee).toBe(mainOpFee)
294
- expect(total).toBe(minFees + mainOpFee)
272
+ expect(result.contents[0]).toMatchObject({ fee: String(minFees) })
273
+ expect(result.contents[1]).toMatchObject({ fee: String(mainOpFee) })
295
274
  })
296
275
  })
297
276
 
298
277
  it('should craft an FA2 send_token transaction', async () => {
299
- const fa2Param = { entrypoint: 'transfer', value: { prim: 'Pair' as const } }
278
+ const fa2Param = { entrypoint: 'transfer', value: { prim: 'Pair' } }
300
279
  const { transfer } = setupFa2ContractMock(fa2Param)
301
280
  mockTezosToolkit.contract.at.mockResolvedValue({ methods: { transfer } })
302
281
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
303
282
 
304
283
  const contractAddress = 'KT1CpeSQKdkhWi4pinYcseCFKmDhs5M74BkU'
305
284
  const account = { address: 'tz1VSUr8wwNhLAzempoch5d6nLRSNtxK8LBr' }
306
- const transaction = {
307
- type: 'send_token' as TransactionType,
285
+ const transaction: TransactionInput = {
286
+ type: 'send_token',
308
287
  recipient: 'tz1VSUr8wwNhLAzempoch5d6nLRSNtxK8LBr',
309
288
  amount: BigInt(5),
310
289
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -347,18 +326,12 @@ describe('craftTransaction', () => {
347
326
  storageLimit: 0,
348
327
  suggestedFeeMutez: getRevealFee('tz1...'),
349
328
  })
350
- currentConfig = {
351
- fees: {
352
- minRevealGasLimit: 100,
353
- minFees: 0,
354
- minStorageLimit: 0,
355
- },
356
- } as unknown as TezosCoinConfig
329
+ currentConfig = { ...mockConfig, fees: { ...mockConfig.fees, minRevealGasLimit: 100 } }
357
330
 
358
331
  const contractAddress = 'KT1CpeSQKdkhWi4pinYcseCFKmDhs5M74BkU'
359
332
  const account = { address: 'tz1...' }
360
- const transaction = {
361
- type: 'send_token' as TransactionType,
333
+ const transaction: TransactionInput = {
334
+ type: 'send_token',
362
335
  recipient: 'tz2...',
363
336
  amount: BigInt(1),
364
337
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -388,7 +361,7 @@ describe('craftTransaction', () => {
388
361
  context,
389
362
  { address: 'tz1...' },
390
363
  {
391
- type: 'send_token' as TransactionType,
364
+ type: 'send_token',
392
365
  recipient: 'tz2...',
393
366
  amount: 1n,
394
367
  fee: {},
@@ -401,7 +374,7 @@ describe('craftTransaction', () => {
401
374
  context,
402
375
  { address: 'tz1...' },
403
376
  {
404
- type: 'send_token' as TransactionType,
377
+ type: 'send_token',
405
378
  recipient: 'tz2...',
406
379
  amount: 1n,
407
380
  fee: {},
@@ -415,8 +388,8 @@ describe('craftTransaction', () => {
415
388
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
416
389
 
417
390
  const account = { address: 'tz1...' }
418
- const transaction = {
419
- type: 'stake' as TransactionType,
391
+ const transaction: TransactionInput = {
392
+ type: 'stake',
420
393
  recipient: '',
421
394
  amount: BigInt(1234),
422
395
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -444,8 +417,8 @@ describe('craftTransaction', () => {
444
417
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
445
418
 
446
419
  const account = { address: 'tz1...' }
447
- const transaction = {
448
- type: 'unstake' as TransactionType,
420
+ const transaction: TransactionInput = {
421
+ type: 'unstake',
449
422
  recipient: '',
450
423
  amount: BigInt(4321),
451
424
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -473,8 +446,8 @@ describe('craftTransaction', () => {
473
446
  mockTezosToolkit.rpc.getContract.mockResolvedValue({ counter: '1' })
474
447
 
475
448
  const account = { address: 'tz1...' }
476
- const transaction = {
477
- type: 'finalize_unstake' as TransactionType,
449
+ const transaction: TransactionInput = {
450
+ type: 'finalize_unstake',
478
451
  recipient: '',
479
452
  // even with a non-zero amount the operation must serialize amount: "0"
480
453
  amount: BigInt(999),
@@ -506,17 +479,11 @@ describe('craftTransaction', () => {
506
479
  storageLimit: 0,
507
480
  suggestedFeeMutez: getRevealFee('tz1...'),
508
481
  })
509
- currentConfig = {
510
- fees: {
511
- minRevealGasLimit: 100,
512
- minFees: 0,
513
- minStorageLimit: 0,
514
- },
515
- } as unknown as TezosCoinConfig
482
+ currentConfig = { ...mockConfig, fees: { ...mockConfig.fees, minRevealGasLimit: 100 } }
516
483
 
517
484
  const account = { address: 'tz1...' }
518
- const transaction = {
519
- type: 'stake' as TransactionType,
485
+ const transaction: TransactionInput = {
486
+ type: 'stake',
520
487
  recipient: '',
521
488
  amount: BigInt(500),
522
489
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
@@ -553,12 +520,13 @@ describe('craftTransaction', () => {
553
520
  it('should throw an error for unsupported transaction type', async () => {
554
521
  const account = { address: 'tz1...' }
555
522
  const transaction = {
556
- type: 'invalid' as any,
523
+ type: 'invalid',
557
524
  recipient: 'tz2...',
558
525
  amount: BigInt(1000),
559
526
  fee: { fees: '100', gasLimit: '200', storageLimit: '300' },
560
527
  }
561
528
 
529
+ // @ts-expect-error -- intentionally invalid type to test the unsupported-type error path
562
530
  await expect(craftTransaction(context, account, transaction)).rejects.toThrow(
563
531
  'unsupported mode'
564
532
  )
@@ -1,14 +1,13 @@
1
1
  // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import type { TezosCoinConfig, TezosContext } from '../config'
5
- import type { CoreTransactionInfo } from './estimateFees'
4
+ import type { TezosContext } from '../config'
6
5
  import { mockConfig } from '../test/config'
7
6
  import { estimateFees } from './estimateFees'
8
7
 
9
8
  describe('estimateFees', () => {
10
9
  const context: TezosContext = {
11
- config: async () => mockConfig as TezosCoinConfig,
10
+ config: async () => mockConfig,
12
11
  logger: () => undefined,
13
12
  }
14
13
 
@@ -31,15 +30,15 @@ describe('estimateFees', () => {
31
30
  [accounts[0], 'with xpub'],
32
31
  [accounts[1], 'without xpub'],
33
32
  ])('returns correct value %s', async (account, _description) => {
34
- // Given
35
- const transaction = {
36
- mode: 'send',
37
- recipient: 'tz1PWFt4Ym6HedY78MgUP2kVDtSampGwprs5',
38
- amount: BigInt(1_000_000),
39
- } satisfies CoreTransactionInfo
40
-
41
33
  // When
42
- const result = await estimateFees(context, { account, transaction })
34
+ const result = await estimateFees(context, {
35
+ account,
36
+ transaction: {
37
+ mode: 'send',
38
+ recipient: 'tz1PWFt4Ym6HedY78MgUP2kVDtSampGwprs5',
39
+ amount: BigInt(1_000_000),
40
+ },
41
+ })
43
42
  // Then
44
43
  expect(result).toEqual({
45
44
  estimatedFees: BigInt('825'),
@@ -54,16 +53,16 @@ describe('estimateFees', () => {
54
53
  [accounts[0], 'with xpub'],
55
54
  [accounts[1], 'without xpub'],
56
55
  ])('returns correct value when useAllAmount %s', async (account, _description) => {
57
- // Given
58
- const transaction = {
59
- mode: 'send',
60
- recipient: 'tz1PWFt4Ym6HedY78MgUP2kVDtSampGwprs5',
61
- amount: BigInt(1_000_000),
62
- useAllAmount: true,
63
- } satisfies CoreTransactionInfo
64
-
65
56
  // When
66
- const result = await estimateFees(context, { account, transaction })
57
+ const result = await estimateFees(context, {
58
+ account,
59
+ transaction: {
60
+ mode: 'send',
61
+ recipient: 'tz1PWFt4Ym6HedY78MgUP2kVDtSampGwprs5',
62
+ amount: BigInt(1_000_000),
63
+ useAllAmount: true,
64
+ },
65
+ })
67
66
 
68
67
  // Then
69
68
  expect(result).toEqual({