@ledgerhq/coin-tezos 10.2.2 → 10.2.4
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.
- package/CHANGELOG.md +28 -0
- package/lib/api/index.d.ts +1 -3
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +4 -9
- package/lib/api/index.js.map +1 -1
- package/lib/logic/craftRawOperations.js +1 -1
- package/lib/logic/craftRawOperations.js.map +1 -1
- package/lib/logic/craftTransaction.d.ts.map +1 -1
- package/lib/logic/craftTransaction.js +1 -2
- package/lib/logic/craftTransaction.js.map +1 -1
- package/lib/logic/estimateFees.d.ts.map +1 -1
- package/lib/logic/estimateFees.js +51 -6
- package/lib/logic/estimateFees.js.map +1 -1
- package/lib/logic/estimateRevealLimits.d.ts +2 -2
- package/lib/logic/estimateRevealLimits.d.ts.map +1 -1
- package/lib/logic/estimateRevealLimits.js +3 -3
- package/lib/logic/estimateRevealLimits.js.map +1 -1
- package/lib/logic/getBalance.d.ts.map +1 -1
- package/lib/logic/getBalance.js +2 -3
- package/lib/logic/getBalance.js.map +1 -1
- package/lib/logic/getBlock.js +6 -6
- package/lib/logic/getBlock.js.map +1 -1
- package/lib/logic/getStakes.d.ts +2 -1
- package/lib/logic/getStakes.d.ts.map +1 -1
- package/lib/logic/getStakes.js +8 -9
- package/lib/logic/getStakes.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +16 -16
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/validateIntent.d.ts.map +1 -1
- package/lib/logic/validateIntent.js +14 -2
- package/lib/logic/validateIntent.js.map +1 -1
- package/lib/network/bakers.d.ts +6 -5
- package/lib/network/bakers.d.ts.map +1 -1
- package/lib/network/bakers.js +13 -12
- package/lib/network/bakers.js.map +1 -1
- package/lib/network/tzkt.d.ts +8 -8
- package/lib/network/tzkt.d.ts.map +1 -1
- package/lib/network/tzkt.js +13 -11
- package/lib/network/tzkt.js.map +1 -1
- package/lib/types/errors.d.ts +3 -0
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/errors.js +8 -1
- package/lib/types/errors.js.map +1 -1
- package/lib/utils.d.ts +17 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +31 -1
- package/lib/utils.js.map +1 -1
- package/lib-es/api/index.d.ts +1 -3
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +5 -10
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/logic/craftRawOperations.js +1 -1
- package/lib-es/logic/craftRawOperations.js.map +1 -1
- package/lib-es/logic/craftTransaction.d.ts.map +1 -1
- package/lib-es/logic/craftTransaction.js +1 -2
- package/lib-es/logic/craftTransaction.js.map +1 -1
- package/lib-es/logic/estimateFees.d.ts.map +1 -1
- package/lib-es/logic/estimateFees.js +52 -7
- package/lib-es/logic/estimateFees.js.map +1 -1
- package/lib-es/logic/estimateRevealLimits.d.ts +2 -2
- package/lib-es/logic/estimateRevealLimits.d.ts.map +1 -1
- package/lib-es/logic/estimateRevealLimits.js +3 -3
- package/lib-es/logic/estimateRevealLimits.js.map +1 -1
- package/lib-es/logic/getBalance.d.ts.map +1 -1
- package/lib-es/logic/getBalance.js +2 -3
- package/lib-es/logic/getBalance.js.map +1 -1
- package/lib-es/logic/getBlock.js +6 -6
- package/lib-es/logic/getBlock.js.map +1 -1
- package/lib-es/logic/getStakes.d.ts +2 -1
- package/lib-es/logic/getStakes.d.ts.map +1 -1
- package/lib-es/logic/getStakes.js +8 -9
- package/lib-es/logic/getStakes.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +16 -16
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/validateIntent.d.ts.map +1 -1
- package/lib-es/logic/validateIntent.js +16 -4
- package/lib-es/logic/validateIntent.js.map +1 -1
- package/lib-es/network/bakers.d.ts +6 -5
- package/lib-es/network/bakers.d.ts.map +1 -1
- package/lib-es/network/bakers.js +13 -12
- package/lib-es/network/bakers.js.map +1 -1
- package/lib-es/network/tzkt.d.ts +8 -8
- package/lib-es/network/tzkt.d.ts.map +1 -1
- package/lib-es/network/tzkt.js +13 -11
- package/lib-es/network/tzkt.js.map +1 -1
- package/lib-es/types/errors.d.ts +3 -0
- package/lib-es/types/errors.d.ts.map +1 -1
- package/lib-es/types/errors.js +6 -0
- package/lib-es/types/errors.js.map +1 -1
- package/lib-es/utils.d.ts +17 -0
- package/lib-es/utils.d.ts.map +1 -1
- package/lib-es/utils.js +29 -0
- package/lib-es/utils.js.map +1 -1
- package/package.json +3 -4
- package/src/api/index.test.ts +32 -0
- package/src/api/index.ts +5 -13
- package/src/logic/craftRawOperations.ts +1 -1
- package/src/logic/craftTransaction.ts +1 -2
- package/src/logic/estimateFees.test.ts +124 -2
- package/src/logic/estimateFees.ts +59 -6
- package/src/logic/estimateRevealLimits.ts +11 -7
- package/src/logic/getBalance.ts +9 -6
- package/src/logic/getBlock.ts +6 -6
- package/src/logic/getStakes.ts +13 -8
- package/src/logic/listOperations.ts +23 -13
- package/src/logic/validateIntent.test.ts +90 -1
- package/src/logic/validateIntent.ts +18 -2
- package/src/network/bakers.integ.test.ts +3 -2
- package/src/network/bakers.test.ts +2 -1
- package/src/network/bakers.ts +15 -13
- package/src/network/tzkt.test.ts +29 -30
- package/src/network/tzkt.ts +38 -23
- package/src/types/errors.ts +7 -0
- package/src/utils.ts +32 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { TezosContext } from '../config'
|
|
5
5
|
import type { AccountsGetOperationsOptions } from '../network/types'
|
|
6
6
|
import type {
|
|
7
7
|
APIDelegationType,
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
TokenTransfersGetOptions,
|
|
15
15
|
} from '../network/types'
|
|
16
16
|
import type { Operation } from '@ledgerhq/coin-module-framework/api/types'
|
|
17
|
-
import {
|
|
17
|
+
import type { Logger } from '@ledgerhq/coin-module-framework/config'
|
|
18
18
|
import { STAKING_ACTION_TO_OP_TYPE } from '../constants'
|
|
19
19
|
import { createTzktApi } from '../network'
|
|
20
20
|
import {
|
|
@@ -69,6 +69,7 @@ function maxLevel<T extends { level: number }>(items: T[]): number | undefined {
|
|
|
69
69
|
|
|
70
70
|
/** When the API returned a full page, the trailing block level may be incomplete — drop it. */
|
|
71
71
|
function trimPartialLastLevel<T extends { level: number; id: number }>(
|
|
72
|
+
logger: Logger,
|
|
72
73
|
items: T[],
|
|
73
74
|
full: boolean
|
|
74
75
|
): { trimmed: T[]; intraLevelLastId?: number } {
|
|
@@ -78,7 +79,7 @@ function trimPartialLastLevel<T extends { level: number; id: number }>(
|
|
|
78
79
|
const lastLevel = last.level
|
|
79
80
|
const filtered = items.filter((op) => op.level !== lastLevel)
|
|
80
81
|
if (filtered.length === 0) {
|
|
81
|
-
|
|
82
|
+
logger('coin:tezos', 'listOperations: full page single level — keeping all rows', {
|
|
82
83
|
lastLevel,
|
|
83
84
|
count: items.length,
|
|
84
85
|
})
|
|
@@ -251,6 +252,7 @@ function keepNativeOp(op: APIOperation, address: string): op is ConvertibleOpera
|
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
function convertNativeOps(
|
|
255
|
+
logger: Logger,
|
|
254
256
|
ops: APIOperation[],
|
|
255
257
|
address: string,
|
|
256
258
|
stakingBlockHashes: Map<number, string>
|
|
@@ -282,7 +284,7 @@ function convertNativeOps(
|
|
|
282
284
|
return ops
|
|
283
285
|
.filter((op): op is ConvertibleOperation => keepNativeOp(op, address))
|
|
284
286
|
.map((op) => {
|
|
285
|
-
const converted = convertOperation(address, op, stakingBlockHashes)
|
|
287
|
+
const converted = convertOperation(logger, address, op, stakingBlockHashes)
|
|
286
288
|
const extraFee = internalFeesByHash.get(converted.tx.hash)
|
|
287
289
|
if (extraFee) {
|
|
288
290
|
// Add internal sub-tx fees to the parent operation, then clear to avoid
|
|
@@ -368,8 +370,8 @@ export async function listOperations(
|
|
|
368
370
|
const nativePage = clampPage(nativeOpsRaw, limit)
|
|
369
371
|
const tokenPage = clampPage(tokenTransfersRaw, limit)
|
|
370
372
|
|
|
371
|
-
const nativeTrimmed = trimPartialLastLevel(nativePage.sliced, nativePage.full)
|
|
372
|
-
const tokenTrimmed = trimPartialLastLevel(tokenPage.sliced, tokenPage.full)
|
|
373
|
+
const nativeTrimmed = trimPartialLastLevel(context.logger, nativePage.sliced, nativePage.full)
|
|
374
|
+
const tokenTrimmed = trimPartialLastLevel(context.logger, tokenPage.sliced, tokenPage.full)
|
|
373
375
|
|
|
374
376
|
// when both streams are exhausted (neither hit the page limit),
|
|
375
377
|
// we already have every op — alignment would silently discard ops
|
|
@@ -388,7 +390,7 @@ export async function listOperations(
|
|
|
388
390
|
? tokenTrimmed.trimmed
|
|
389
391
|
: alignToBoundary(tokenTrimmed.trimmed, boundary, sort)
|
|
390
392
|
|
|
391
|
-
const stakingBlockHashes = await fetchMissingStakingBlockHashes(
|
|
393
|
+
const stakingBlockHashes = await fetchMissingStakingBlockHashes(context, nativeAligned)
|
|
392
394
|
|
|
393
395
|
const parentMap = buildParentMap(nativeAligned)
|
|
394
396
|
|
|
@@ -401,7 +403,12 @@ export async function listOperations(
|
|
|
401
403
|
tokenTrimmed.intraLevelLastId
|
|
402
404
|
)
|
|
403
405
|
|
|
404
|
-
const filteredNativeOps = convertNativeOps(
|
|
406
|
+
const filteredNativeOps = convertNativeOps(
|
|
407
|
+
context.logger,
|
|
408
|
+
nativeAligned,
|
|
409
|
+
address,
|
|
410
|
+
stakingBlockHashes
|
|
411
|
+
)
|
|
405
412
|
const tokenConverted = convertTokenOps(tokenAligned, parentMap, address)
|
|
406
413
|
|
|
407
414
|
const sortedOperations = [...filteredNativeOps, ...tokenConverted].sort(
|
|
@@ -424,7 +431,7 @@ type ConvertibleOperation =
|
|
|
424
431
|
* failures are swallowed: callers fall back to `""` for levels not in the map.
|
|
425
432
|
*/
|
|
426
433
|
async function fetchMissingStakingBlockHashes(
|
|
427
|
-
|
|
434
|
+
context: TezosContext,
|
|
428
435
|
ops: readonly APIOperation[]
|
|
429
436
|
): Promise<Map<number, string>> {
|
|
430
437
|
const missingLevels = new Set<number>()
|
|
@@ -432,10 +439,11 @@ async function fetchMissingStakingBlockHashes(
|
|
|
432
439
|
if (isAPIStakingType(op) && !op.block) missingLevels.add(op.level)
|
|
433
440
|
}
|
|
434
441
|
if (missingLevels.size === 0) return new Map()
|
|
442
|
+
const config = await context.config()
|
|
435
443
|
try {
|
|
436
444
|
return await createTzktApi(config).getBlockHashesByLevels([...missingLevels])
|
|
437
445
|
} catch (err) {
|
|
438
|
-
|
|
446
|
+
context.logger('coin:tezos', 'fetchMissingStakingBlockHashes: skipped on fetch error', {
|
|
439
447
|
reason: String(err),
|
|
440
448
|
})
|
|
441
449
|
return new Map()
|
|
@@ -487,6 +495,7 @@ function resolveAmount(operation: ConvertibleOperation): bigint {
|
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
function resolveNormalizedType(
|
|
498
|
+
logger: Logger,
|
|
490
499
|
operation: ConvertibleOperation,
|
|
491
500
|
address: string,
|
|
492
501
|
targetAddress: string | undefined,
|
|
@@ -499,7 +508,7 @@ function resolveNormalizedType(
|
|
|
499
508
|
if (isAPIRevealType(operation)) return 'REVEAL'
|
|
500
509
|
if (isAPIOriginationType(operation)) return operation.contractBalance > 0 ? 'OUT' : 'FEES'
|
|
501
510
|
if (!isAPITransactionType(operation)) {
|
|
502
|
-
|
|
511
|
+
logger('coin:tezos', '(logic/operations): Unknown operation type, defaulting to OUT')
|
|
503
512
|
return 'OUT'
|
|
504
513
|
}
|
|
505
514
|
const isOut = operation.sender?.address === address
|
|
@@ -529,6 +538,7 @@ function getLedgerOpType(
|
|
|
529
538
|
}
|
|
530
539
|
|
|
531
540
|
function convertOperation(
|
|
541
|
+
logger: Logger,
|
|
532
542
|
address: string,
|
|
533
543
|
operation: ConvertibleOperation,
|
|
534
544
|
stakingBlockHashes: Map<number, string>
|
|
@@ -544,7 +554,7 @@ function convertOperation(
|
|
|
544
554
|
const targetAddress = resolveTargetAddress(operation)
|
|
545
555
|
// reveal has no meaningful target; staking resolves senders/recipients separately.
|
|
546
556
|
if (!targetAddress && !isAPIRevealType(operation) && !isAPIStakingType(operation)) {
|
|
547
|
-
|
|
557
|
+
logger('coin:tezos', '(logic/operations): No target address found for operation', operation)
|
|
548
558
|
}
|
|
549
559
|
|
|
550
560
|
const { senders, recipients } = isAPIStakingType(operation)
|
|
@@ -561,7 +571,7 @@ function convertOperation(
|
|
|
561
571
|
BigInt(operation.bakerFee ?? 0) +
|
|
562
572
|
BigInt(operation.allocationFee ?? 0)
|
|
563
573
|
|
|
564
|
-
const normalizedType = resolveNormalizedType(operation, address, targetAddress, amount)
|
|
574
|
+
const normalizedType = resolveNormalizedType(logger, operation, address, targetAddress, amount)
|
|
565
575
|
|
|
566
576
|
// Tezos uses "applied" for every success operation (something else=failed )
|
|
567
577
|
const hasFailed = operation.status && operation.status !== 'applied'
|
|
@@ -15,9 +15,10 @@ import {
|
|
|
15
15
|
MustDelegateBeforeStaking,
|
|
16
16
|
NotEnoughBalanceToDelegate,
|
|
17
17
|
TezosNotEnoughStaked,
|
|
18
|
+
TezosStakeAmountTooSmall,
|
|
18
19
|
TezosStakeBlockedByPendingUnstake,
|
|
19
20
|
} from '../types/errors'
|
|
20
|
-
import { STAKE_USE_ALL_RESERVE_MUTEZ } from '../utils'
|
|
21
|
+
import { MAPPED_TAQUITO_ERRORS, STAKE_USE_ALL_RESERVE_MUTEZ } from '../utils'
|
|
21
22
|
import { validateIntent } from './validateIntent'
|
|
22
23
|
|
|
23
24
|
const mockEstimateFees = jest.fn()
|
|
@@ -387,6 +388,35 @@ describe('validateIntent', () => {
|
|
|
387
388
|
expect(result.amount).toBe(0n)
|
|
388
389
|
})
|
|
389
390
|
|
|
391
|
+
it('should return TezosStakeAmountTooSmall when stake useAllAmount resolves max below the probe', async () => {
|
|
392
|
+
mockGetAccountByAddress.mockResolvedValue(
|
|
393
|
+
makeUserAccount({
|
|
394
|
+
delegate: { alias: 'baker', address: validRecipient, active: true },
|
|
395
|
+
delegationLevel: 1,
|
|
396
|
+
})
|
|
397
|
+
)
|
|
398
|
+
mockEstimateFees.mockResolvedValueOnce({
|
|
399
|
+
fees: 1000n,
|
|
400
|
+
gasLimit: 10000n,
|
|
401
|
+
storageLimit: 0n,
|
|
402
|
+
estimatedFees: 1000n,
|
|
403
|
+
amount: 999n,
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
const result = await validateIntent(context, {
|
|
407
|
+
intentType: 'staking',
|
|
408
|
+
asset: { type: 'native' },
|
|
409
|
+
type: 'stake',
|
|
410
|
+
sender: senderAddress,
|
|
411
|
+
recipient: '',
|
|
412
|
+
amount: 0n,
|
|
413
|
+
useAllAmount: true,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
expect(result.errors.amount).toBeInstanceOf(TezosStakeAmountTooSmall)
|
|
417
|
+
expect(result.amount).toBe(999n)
|
|
418
|
+
})
|
|
419
|
+
|
|
390
420
|
it('excludes already-staked funds when stake useAllAmount falls back to balance computation', async () => {
|
|
391
421
|
mockGetAccountByAddress.mockResolvedValue(
|
|
392
422
|
makeUserAccount({
|
|
@@ -987,6 +1017,65 @@ describe('validateIntent', () => {
|
|
|
987
1017
|
expect(result.errors.amount).toBeInstanceOf(MustDelegateBeforeStaking)
|
|
988
1018
|
})
|
|
989
1019
|
|
|
1020
|
+
it('maps stake_amount_too_small to TezosStakeAmountTooSmall', async () => {
|
|
1021
|
+
mockGetAccountByAddress.mockResolvedValue(
|
|
1022
|
+
makeUserAccount({
|
|
1023
|
+
delegate: { alias: 'baker', address: validRecipient, active: true },
|
|
1024
|
+
delegationLevel: 1,
|
|
1025
|
+
})
|
|
1026
|
+
)
|
|
1027
|
+
mockEstimateFees.mockResolvedValue({
|
|
1028
|
+
fees: 0n,
|
|
1029
|
+
gasLimit: 0n,
|
|
1030
|
+
storageLimit: 0n,
|
|
1031
|
+
estimatedFees: 500n,
|
|
1032
|
+
taquitoError: 'proto.025-PsUshuai.block_validation_plugin.stake_amount_too_small',
|
|
1033
|
+
})
|
|
1034
|
+
|
|
1035
|
+
const result = await validateIntent(context, {
|
|
1036
|
+
intentType: 'staking',
|
|
1037
|
+
asset: { type: 'native' },
|
|
1038
|
+
type: 'stake',
|
|
1039
|
+
sender: senderAddress,
|
|
1040
|
+
recipient: '',
|
|
1041
|
+
amount: 1n,
|
|
1042
|
+
})
|
|
1043
|
+
|
|
1044
|
+
expect(result.errors.amount).toBeInstanceOf(TezosStakeAmountTooSmall)
|
|
1045
|
+
})
|
|
1046
|
+
|
|
1047
|
+
it.each(MAPPED_TAQUITO_ERRORS)(
|
|
1048
|
+
'maps %s, which estimation reports instead of throwing, to a dedicated error',
|
|
1049
|
+
async (id) => {
|
|
1050
|
+
mockGetAccountByAddress.mockResolvedValue(
|
|
1051
|
+
makeUserAccount({
|
|
1052
|
+
delegate: { alias: 'baker', address: validRecipient, active: true },
|
|
1053
|
+
delegationLevel: 1,
|
|
1054
|
+
})
|
|
1055
|
+
)
|
|
1056
|
+
mockEstimateFees.mockResolvedValue({
|
|
1057
|
+
fees: 0n,
|
|
1058
|
+
gasLimit: 0n,
|
|
1059
|
+
storageLimit: 0n,
|
|
1060
|
+
estimatedFees: 500n,
|
|
1061
|
+
taquitoError: `proto.025-PsUshuai.${id}`,
|
|
1062
|
+
})
|
|
1063
|
+
|
|
1064
|
+
const result = await validateIntent(context, {
|
|
1065
|
+
intentType: 'staking',
|
|
1066
|
+
asset: { type: 'native' },
|
|
1067
|
+
type: 'stake',
|
|
1068
|
+
sender: senderAddress,
|
|
1069
|
+
recipient: '',
|
|
1070
|
+
amount: 1000n,
|
|
1071
|
+
})
|
|
1072
|
+
|
|
1073
|
+
const error = result.errors.amount ?? result.errors.recipient
|
|
1074
|
+
expect(error).toBeInstanceOf(Error)
|
|
1075
|
+
expect(error?.name).not.toBe('Error')
|
|
1076
|
+
}
|
|
1077
|
+
)
|
|
1078
|
+
|
|
990
1079
|
it('maps cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate to TezosStakeBlockedByPendingUnstake for stake', async () => {
|
|
991
1080
|
mockGetAccountByAddress.mockResolvedValue(
|
|
992
1081
|
makeUserAccount({
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
MustDelegateBeforeStaking,
|
|
25
25
|
NotEnoughBalanceToDelegate,
|
|
26
26
|
TezosNotEnoughStaked,
|
|
27
|
+
TezosStakeAmountTooSmall,
|
|
27
28
|
TezosStakeBlockedByPendingUnstake,
|
|
28
29
|
} from '../types/errors'
|
|
29
30
|
import {
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
parseTezosTokenAsset,
|
|
32
33
|
partitionNativeBalance,
|
|
33
34
|
resolveTezosOperationMode,
|
|
35
|
+
STAKE_ESTIMATION_PROBE_MUTEZ,
|
|
34
36
|
} from '../utils'
|
|
35
37
|
import { estimateFees } from './estimateFees'
|
|
36
38
|
|
|
@@ -156,6 +158,9 @@ function mapTaquitoErrors(taquitoError: string, intentType: string): Record<stri
|
|
|
156
158
|
errors.amount = new TezosNotEnoughStaked()
|
|
157
159
|
} else if (taquitoError.endsWith('contract.must_be_delegated_to_stake')) {
|
|
158
160
|
errors.amount = new MustDelegateBeforeStaking()
|
|
161
|
+
} else if (taquitoError.endsWith('stake_amount_too_small')) {
|
|
162
|
+
// Below one staking pseudotoken the stake mints nothing; see STAKE_ESTIMATION_PROBE_MUTEZ.
|
|
163
|
+
errors.amount = new TezosStakeAmountTooSmall()
|
|
159
164
|
} else if (
|
|
160
165
|
taquitoError.endsWith('cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate')
|
|
161
166
|
) {
|
|
@@ -166,10 +171,10 @@ function mapTaquitoErrors(taquitoError: string, intentType: string): Record<stri
|
|
|
166
171
|
// Re-delegating (or staking) to the current baker leaves the delegate unchanged; the node
|
|
167
172
|
// rejects it. Surfaces for both `delegate` and `stake` intents as "already delegated".
|
|
168
173
|
errors.recipient = new InvalidAddressBecauseAlreadyDelegated()
|
|
169
|
-
} else if (taquitoError.
|
|
174
|
+
} else if (taquitoError.endsWith('empty_implicit_contract')) {
|
|
170
175
|
errors.amount =
|
|
171
176
|
intentType === 'stake' ? new NotEnoughBalance() : new NotEnoughBalanceToDelegate()
|
|
172
|
-
} else if (taquitoError.
|
|
177
|
+
} else if (taquitoError.endsWith('script_rejected')) {
|
|
173
178
|
errors.amount = new NotEnoughBalance()
|
|
174
179
|
} else {
|
|
175
180
|
errors.amount = new Error(taquitoError)
|
|
@@ -447,6 +452,17 @@ export async function validateIntent(
|
|
|
447
452
|
errors.amount = new NotEnoughBalance()
|
|
448
453
|
}
|
|
449
454
|
|
|
455
|
+
// Estimation simulates the probe, never the resolved max; below the probe the stake may still
|
|
456
|
+
// mint no pseudotoken and be refused at injection.
|
|
457
|
+
if (
|
|
458
|
+
intent.type === 'stake' &&
|
|
459
|
+
intent.useAllAmount &&
|
|
460
|
+
amount < STAKE_ESTIMATION_PROBE_MUTEZ &&
|
|
461
|
+
!errors.amount
|
|
462
|
+
) {
|
|
463
|
+
errors.amount = new TezosStakeAmountTooSmall()
|
|
464
|
+
}
|
|
465
|
+
|
|
450
466
|
const { spendable } = partitionNativeBalance(
|
|
451
467
|
BigInt(senderInfo.balance),
|
|
452
468
|
BigInt(senderInfo.stakedBalance ?? 0),
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: © 2026 LEDGER SAS
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import type { TezosCoinConfig } from '../config'
|
|
4
|
+
import type { TezosCoinConfig, TezosContext } from '../config'
|
|
5
5
|
import { listBakers } from '../network/bakers'
|
|
6
6
|
import whitelist from '../network/bakers.whitelist-default'
|
|
7
7
|
import { mockConfig } from '../test/config'
|
|
8
8
|
|
|
9
9
|
describe('tezos bakers', () => {
|
|
10
10
|
const config: TezosCoinConfig = mockConfig
|
|
11
|
+
const context: TezosContext = { config: async () => config, logger: jest.fn() }
|
|
11
12
|
|
|
12
13
|
test('atleast 10 whitelisted bakers are online', async () => {
|
|
13
|
-
const onlineBakersCount = (await listBakers(
|
|
14
|
+
const onlineBakersCount = (await listBakers(context, whitelist))
|
|
14
15
|
.map((baker) => baker.address)
|
|
15
16
|
.filter((address) => whitelist.includes(address)).length
|
|
16
17
|
expect(onlineBakersCount).toBeGreaterThan(10)
|
|
@@ -7,6 +7,7 @@ import { mockConfig } from '../test/config'
|
|
|
7
7
|
import { asBaker, cache } from './bakers'
|
|
8
8
|
|
|
9
9
|
const mockNetworkFn = jest.fn()
|
|
10
|
+
const mockLogger = jest.fn()
|
|
10
11
|
jest.mock('@ledgerhq/live-network/network', () => ({
|
|
11
12
|
__esModule: true,
|
|
12
13
|
default: (...args: unknown[]) => mockNetworkFn(...args),
|
|
@@ -183,7 +184,7 @@ describe('Tezos Baker', () => {
|
|
|
183
184
|
})
|
|
184
185
|
|
|
185
186
|
it('Ledger Baker should be first in the list', async () => {
|
|
186
|
-
const bakers = await cache(config)
|
|
187
|
+
const bakers = await cache(config, mockLogger)
|
|
187
188
|
expect(bakers[0].name).toBe('Ledger by Kiln')
|
|
188
189
|
})
|
|
189
190
|
})
|
package/src/network/bakers.ts
CHANGED
|
@@ -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
|
+
import type { TezosCoinConfig, TezosContext } from '../config'
|
|
5
5
|
import type { Baker } from '../types'
|
|
6
|
+
import type { Logger } from '@ledgerhq/coin-module-framework/config'
|
|
6
7
|
import { makeLRUCache } from '@ledgerhq/live-network/cache'
|
|
7
8
|
import network from '@ledgerhq/live-network/network'
|
|
8
|
-
import { log } from '@ledgerhq/logs'
|
|
9
9
|
import { ledgerValidatorAddress } from './bakers.whitelist-default'
|
|
10
10
|
|
|
11
11
|
export type TezosApiBaker = {
|
|
@@ -40,7 +40,7 @@ export type TezosBakerFeature = {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export const cache = makeLRUCache(
|
|
43
|
-
async (config: TezosCoinConfig): Promise<Baker[]> => {
|
|
43
|
+
async (config: TezosCoinConfig, logger: Logger): Promise<Baker[]> => {
|
|
44
44
|
const bakers: Baker[] = []
|
|
45
45
|
const TEZOS_API_BASE_URL = config.baker.url
|
|
46
46
|
const { data } = await network<TezosApiBaker[]>({
|
|
@@ -48,7 +48,7 @@ export const cache = makeLRUCache(
|
|
|
48
48
|
})
|
|
49
49
|
|
|
50
50
|
if (data && Array.isArray(data)) {
|
|
51
|
-
|
|
51
|
+
logger('tezos/bakers', 'found ' + data.length + ' bakers')
|
|
52
52
|
data
|
|
53
53
|
.filter((raw) => raw.status === 'active' && raw.delegation.enabled)
|
|
54
54
|
.forEach((raw) => {
|
|
@@ -64,7 +64,7 @@ export const cache = makeLRUCache(
|
|
|
64
64
|
const ledgerBakerIndex = bakers.findIndex((baker) => baker.address === ledgerValidatorAddress)
|
|
65
65
|
const [ledgerBaker] = bakers.splice(ledgerBakerIndex, 1)
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
logger('tezos/bakers', 'loaded ' + bakers.length + ' bakers')
|
|
68
68
|
return [ledgerBaker, ...bakers]
|
|
69
69
|
},
|
|
70
70
|
(config: TezosCoinConfig) => config.baker.url
|
|
@@ -72,8 +72,9 @@ export const cache = makeLRUCache(
|
|
|
72
72
|
|
|
73
73
|
let _lastBakers: Baker[] = []
|
|
74
74
|
|
|
75
|
-
export const fetchAllBakers = async (
|
|
76
|
-
const
|
|
75
|
+
export const fetchAllBakers = async (context: TezosContext): Promise<Baker[]> => {
|
|
76
|
+
const config = await context.config()
|
|
77
|
+
const allBakers = await cache.force(config, context.logger)
|
|
77
78
|
_lastBakers = allBakers
|
|
78
79
|
return allBakers
|
|
79
80
|
}
|
|
@@ -86,10 +87,11 @@ export const listBakersWithDefault = (whitelistAddresses: string[]): Baker[] =>
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
export const listBakers = async (
|
|
89
|
-
|
|
90
|
+
context: TezosContext,
|
|
90
91
|
whitelistAddresses: string[]
|
|
91
92
|
): Promise<Baker[]> => {
|
|
92
|
-
|
|
93
|
+
const config = await context.config()
|
|
94
|
+
_lastBakers = await cache(config, context.logger)
|
|
93
95
|
return whitelist(_lastBakers, whitelistAddresses)
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -97,12 +99,12 @@ export function getBakerSync(addr: string): Baker | undefined {
|
|
|
97
99
|
return _lastBakers.find((baker) => baker.address === addr)
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
export async function loadBaker(
|
|
102
|
+
export async function loadBaker(context: TezosContext, addr: string): Promise<Baker | undefined> {
|
|
101
103
|
const cacheBaker = getBakerSync(addr)
|
|
102
104
|
if (cacheBaker) return cacheBaker
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
return baker
|
|
105
|
+
const config = await context.config()
|
|
106
|
+
const bakers = await cache(config, context.logger)
|
|
107
|
+
return bakers.find((baker) => baker.address === addr)
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
export const asBaker = (data: TezosApiBaker): Baker | undefined => {
|
package/src/network/tzkt.test.ts
CHANGED
|
@@ -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 } from '../config'
|
|
4
|
+
import type { TezosCoinConfig, TezosContext } from '../config'
|
|
5
5
|
// Import fetchBlockTransactions / fetchBlockTokenTransfers / fetchBlockDelegations through the index re-export
|
|
6
6
|
// so that network/index.ts line 4 is exercised and its coverage is tracked.
|
|
7
7
|
import type {
|
|
@@ -18,7 +18,6 @@ import type { DeepPartial, DeepPartialReturn } from '@ledgerhq/coin-module-frame
|
|
|
18
18
|
// tzkt.ts imports `network` from "@ledgerhq/live-network" (the package root, not the /network
|
|
19
19
|
// subpath). We mock the same path so Jest's module registry intercepts those calls.
|
|
20
20
|
import network from '@ledgerhq/live-network'
|
|
21
|
-
import { log } from '@ledgerhq/logs'
|
|
22
21
|
import { mockConfig } from '../test/config'
|
|
23
22
|
import { createTzktApi } from './tzkt'
|
|
24
23
|
import {
|
|
@@ -30,9 +29,10 @@ import {
|
|
|
30
29
|
} from '.'
|
|
31
30
|
|
|
32
31
|
jest.mock('@ledgerhq/live-network')
|
|
33
|
-
jest.mock('@ledgerhq/logs')
|
|
34
32
|
const mockedNetwork = jest.mocked<DeepPartialReturn<typeof network>>(network)
|
|
35
|
-
const
|
|
33
|
+
const mockLogger = jest.fn()
|
|
34
|
+
const config: TezosCoinConfig = mockConfig
|
|
35
|
+
const context: TezosContext = { config: async () => config, logger: mockLogger }
|
|
36
36
|
|
|
37
37
|
// The tzkt module is NOT mocked here. `createTzktApi(config)` returns a plain object whose methods
|
|
38
38
|
// close over `config.explorer.url` and delegate to the mocked `network`. Each method also calls its
|
|
@@ -43,7 +43,6 @@ const mockedLog = jest.mocked(log)
|
|
|
43
43
|
// The paginated helpers (`fetchAllTransactions`, `fetchBlock*`) are top-level functions that build
|
|
44
44
|
// their OWN fresh `createTzktApi(config)` instance internally, so spying on `api` cannot intercept
|
|
45
45
|
// them. Those tests therefore drive the mocked `network` directly.
|
|
46
|
-
const config: TezosCoinConfig = mockConfig
|
|
47
46
|
const api = createTzktApi(config)
|
|
48
47
|
|
|
49
48
|
// ---------------------------------------------------------------------------
|
|
@@ -808,7 +807,7 @@ describe('tzkt network API', () => {
|
|
|
808
807
|
it('returns empty when the first page is empty', async () => {
|
|
809
808
|
mockedNetwork.mockReturnValue(networkResponse([]))
|
|
810
809
|
|
|
811
|
-
const result = await fetchAllTransactions(
|
|
810
|
+
const result = await fetchAllTransactions(context, 'tz1acc')
|
|
812
811
|
|
|
813
812
|
expect(result).toEqual([])
|
|
814
813
|
})
|
|
@@ -819,7 +818,7 @@ describe('tzkt network API', () => {
|
|
|
819
818
|
.mockReturnValueOnce(networkResponse([makeOperation(2)]))
|
|
820
819
|
.mockReturnValueOnce(networkResponse([]))
|
|
821
820
|
|
|
822
|
-
const result = await fetchAllTransactions(
|
|
821
|
+
const result = await fetchAllTransactions(context, 'tz1acc')
|
|
823
822
|
|
|
824
823
|
expect(result).toEqual([makeOperation(1), makeOperation(2)])
|
|
825
824
|
expect(mockedNetwork).toHaveBeenCalledTimes(3)
|
|
@@ -840,10 +839,10 @@ describe('tzkt network API', () => {
|
|
|
840
839
|
const opMissingId: DeepPartial<APIOperation> = { ...makeOperation(1), id: 0 }
|
|
841
840
|
mockedNetwork.mockReturnValue(networkResponse([opMissingId]))
|
|
842
841
|
|
|
843
|
-
const result = await fetchAllTransactions(
|
|
842
|
+
const result = await fetchAllTransactions(context, 'tz1acc')
|
|
844
843
|
|
|
845
844
|
expect(result).toEqual([opMissingId])
|
|
846
|
-
expect(
|
|
845
|
+
expect(mockLogger).toHaveBeenCalledWith('tezos', 'id missing!')
|
|
847
846
|
})
|
|
848
847
|
|
|
849
848
|
it('stops after maxTxQuery iterations', async () => {
|
|
@@ -852,7 +851,7 @@ describe('tzkt network API', () => {
|
|
|
852
851
|
let nextId = 0
|
|
853
852
|
mockedNetwork.mockImplementation(() => networkResponse([makeOperation(++nextId)]))
|
|
854
853
|
|
|
855
|
-
const result = await fetchAllTransactions(
|
|
854
|
+
const result = await fetchAllTransactions(context, 'tz1acc')
|
|
856
855
|
|
|
857
856
|
expect(mockedNetwork).toHaveBeenCalledTimes(mockConfig.explorer.maxTxQuery)
|
|
858
857
|
expect(result).toHaveLength(mockConfig.explorer.maxTxQuery)
|
|
@@ -992,7 +991,7 @@ describe('tzkt network API', () => {
|
|
|
992
991
|
it('returns an empty array when the first page is empty', async () => {
|
|
993
992
|
mockedNetwork.mockReturnValue(networkResponse([]))
|
|
994
993
|
|
|
995
|
-
const result = await fetchBlockTransactions(
|
|
994
|
+
const result = await fetchBlockTransactions(context, 100)
|
|
996
995
|
|
|
997
996
|
expect(result).toEqual([])
|
|
998
997
|
})
|
|
@@ -1001,7 +1000,7 @@ describe('tzkt network API', () => {
|
|
|
1001
1000
|
const txs = [makeTxItem(1), makeTxItem(2)]
|
|
1002
1001
|
mockedNetwork.mockReturnValue(networkResponse(txs))
|
|
1003
1002
|
|
|
1004
|
-
const result = await fetchBlockTransactions(
|
|
1003
|
+
const result = await fetchBlockTransactions(context, 100)
|
|
1005
1004
|
|
|
1006
1005
|
expect(result).toEqual(txs)
|
|
1007
1006
|
})
|
|
@@ -1014,7 +1013,7 @@ describe('tzkt network API', () => {
|
|
|
1014
1013
|
.mockReturnValueOnce(networkResponse(fullPage))
|
|
1015
1014
|
.mockReturnValueOnce(networkResponse(lastPage))
|
|
1016
1015
|
|
|
1017
|
-
const result = await fetchBlockTransactions(
|
|
1016
|
+
const result = await fetchBlockTransactions(context, 100)
|
|
1018
1017
|
|
|
1019
1018
|
expect(result).toHaveLength(1002)
|
|
1020
1019
|
expect(mockedNetwork).toHaveBeenCalledTimes(2)
|
|
@@ -1031,11 +1030,11 @@ describe('tzkt network API', () => {
|
|
|
1031
1030
|
const fullPage = Array.from({ length: 1000 }, (_, i) => makeTxItem(i + 1))
|
|
1032
1031
|
mockedNetwork.mockReturnValue(networkResponse(fullPage))
|
|
1033
1032
|
|
|
1034
|
-
const result = await fetchBlockTransactions(
|
|
1033
|
+
const result = await fetchBlockTransactions(context, 100)
|
|
1035
1034
|
|
|
1036
1035
|
expect(mockedNetwork).toHaveBeenCalledTimes(100)
|
|
1037
1036
|
expect(result).toHaveLength(100_000)
|
|
1038
|
-
expect(
|
|
1037
|
+
expect(mockLogger).toHaveBeenCalledWith(
|
|
1039
1038
|
'tezos',
|
|
1040
1039
|
expect.stringContaining('fetchBlockTransactions: maxTxQuery limit reached at level 100')
|
|
1041
1040
|
)
|
|
@@ -1051,7 +1050,7 @@ describe('tzkt network API', () => {
|
|
|
1051
1050
|
it('returns an empty array when the first page is empty', async () => {
|
|
1052
1051
|
mockedNetwork.mockReturnValue(networkResponse([]))
|
|
1053
1052
|
|
|
1054
|
-
const result = await fetchBlockTokenTransfers(
|
|
1053
|
+
const result = await fetchBlockTokenTransfers(context, 100)
|
|
1055
1054
|
|
|
1056
1055
|
expect(result).toEqual([])
|
|
1057
1056
|
})
|
|
@@ -1060,7 +1059,7 @@ describe('tzkt network API', () => {
|
|
|
1060
1059
|
const items = [makeTokenItem(1), makeTokenItem(2)]
|
|
1061
1060
|
mockedNetwork.mockReturnValue(networkResponse(items))
|
|
1062
1061
|
|
|
1063
|
-
const result = await fetchBlockTokenTransfers(
|
|
1062
|
+
const result = await fetchBlockTokenTransfers(context, 100)
|
|
1064
1063
|
|
|
1065
1064
|
expect(result).toEqual(items)
|
|
1066
1065
|
})
|
|
@@ -1073,7 +1072,7 @@ describe('tzkt network API', () => {
|
|
|
1073
1072
|
.mockReturnValueOnce(networkResponse(fullPage))
|
|
1074
1073
|
.mockReturnValueOnce(networkResponse(lastPage))
|
|
1075
1074
|
|
|
1076
|
-
const result = await fetchBlockTokenTransfers(
|
|
1075
|
+
const result = await fetchBlockTokenTransfers(context, 100)
|
|
1077
1076
|
|
|
1078
1077
|
expect(result).toHaveLength(1001)
|
|
1079
1078
|
expect(mockedNetwork).toHaveBeenCalledTimes(2)
|
|
@@ -1088,11 +1087,11 @@ describe('tzkt network API', () => {
|
|
|
1088
1087
|
const fullPage = Array.from({ length: 1000 }, (_, i) => makeTokenItem(i + 1))
|
|
1089
1088
|
mockedNetwork.mockReturnValue(networkResponse(fullPage))
|
|
1090
1089
|
|
|
1091
|
-
const result = await fetchBlockTokenTransfers(
|
|
1090
|
+
const result = await fetchBlockTokenTransfers(context, 100)
|
|
1092
1091
|
|
|
1093
1092
|
expect(mockedNetwork).toHaveBeenCalledTimes(100)
|
|
1094
1093
|
expect(result).toHaveLength(100_000)
|
|
1095
|
-
expect(
|
|
1094
|
+
expect(mockLogger).toHaveBeenCalledWith(
|
|
1096
1095
|
'tezos',
|
|
1097
1096
|
expect.stringContaining('fetchBlockTokenTransfers: maxTxQuery limit reached at level 100')
|
|
1098
1097
|
)
|
|
@@ -1147,7 +1146,7 @@ describe('tzkt network API', () => {
|
|
|
1147
1146
|
it('returns an empty array when the first page is empty', async () => {
|
|
1148
1147
|
mockedNetwork.mockReturnValue(networkResponse([]))
|
|
1149
1148
|
|
|
1150
|
-
const result = await fetchBlockDelegations(
|
|
1149
|
+
const result = await fetchBlockDelegations(context, 100)
|
|
1151
1150
|
|
|
1152
1151
|
expect(result).toEqual([])
|
|
1153
1152
|
})
|
|
@@ -1156,7 +1155,7 @@ describe('tzkt network API', () => {
|
|
|
1156
1155
|
const items = [makeDelegationItem(1), makeDelegationItem(2)]
|
|
1157
1156
|
mockedNetwork.mockReturnValue(networkResponse(items))
|
|
1158
1157
|
|
|
1159
|
-
const result = await fetchBlockDelegations(
|
|
1158
|
+
const result = await fetchBlockDelegations(context, 100)
|
|
1160
1159
|
|
|
1161
1160
|
expect(result).toEqual(items)
|
|
1162
1161
|
})
|
|
@@ -1169,7 +1168,7 @@ describe('tzkt network API', () => {
|
|
|
1169
1168
|
.mockReturnValueOnce(networkResponse(fullPage))
|
|
1170
1169
|
.mockReturnValueOnce(networkResponse(lastPage))
|
|
1171
1170
|
|
|
1172
|
-
const result = await fetchBlockDelegations(
|
|
1171
|
+
const result = await fetchBlockDelegations(context, 100)
|
|
1173
1172
|
|
|
1174
1173
|
expect(result).toHaveLength(1001)
|
|
1175
1174
|
expect(mockedNetwork).toHaveBeenCalledTimes(2)
|
|
@@ -1184,11 +1183,11 @@ describe('tzkt network API', () => {
|
|
|
1184
1183
|
const fullPage = Array.from({ length: 1000 }, (_, i) => makeDelegationItem(i + 1))
|
|
1185
1184
|
mockedNetwork.mockReturnValue(networkResponse(fullPage))
|
|
1186
1185
|
|
|
1187
|
-
const result = await fetchBlockDelegations(
|
|
1186
|
+
const result = await fetchBlockDelegations(context, 100)
|
|
1188
1187
|
|
|
1189
1188
|
expect(mockedNetwork).toHaveBeenCalledTimes(100)
|
|
1190
1189
|
expect(result).toHaveLength(100_000)
|
|
1191
|
-
expect(
|
|
1190
|
+
expect(mockLogger).toHaveBeenCalledWith(
|
|
1192
1191
|
'tezos',
|
|
1193
1192
|
expect.stringContaining('fetchBlockDelegations: maxTxQuery limit reached at level 100')
|
|
1194
1193
|
)
|
|
@@ -1243,7 +1242,7 @@ describe('tzkt network API', () => {
|
|
|
1243
1242
|
it('returns an empty array when the first page is empty', async () => {
|
|
1244
1243
|
mockedNetwork.mockReturnValue(networkResponse([]))
|
|
1245
1244
|
|
|
1246
|
-
const result = await fetchBlockStaking(
|
|
1245
|
+
const result = await fetchBlockStaking(context, 100)
|
|
1247
1246
|
|
|
1248
1247
|
expect(result).toEqual([])
|
|
1249
1248
|
})
|
|
@@ -1252,7 +1251,7 @@ describe('tzkt network API', () => {
|
|
|
1252
1251
|
const items = [makeStakingItem(1), makeStakingItem(2)]
|
|
1253
1252
|
mockedNetwork.mockReturnValue(networkResponse(items))
|
|
1254
1253
|
|
|
1255
|
-
const result = await fetchBlockStaking(
|
|
1254
|
+
const result = await fetchBlockStaking(context, 100)
|
|
1256
1255
|
|
|
1257
1256
|
expect(result).toEqual(items)
|
|
1258
1257
|
})
|
|
@@ -1265,7 +1264,7 @@ describe('tzkt network API', () => {
|
|
|
1265
1264
|
.mockReturnValueOnce(networkResponse(fullPage))
|
|
1266
1265
|
.mockReturnValueOnce(networkResponse(lastPage))
|
|
1267
1266
|
|
|
1268
|
-
const result = await fetchBlockStaking(
|
|
1267
|
+
const result = await fetchBlockStaking(context, 100)
|
|
1269
1268
|
|
|
1270
1269
|
expect(result).toHaveLength(1001)
|
|
1271
1270
|
expect(mockedNetwork).toHaveBeenCalledTimes(2)
|
|
@@ -1280,11 +1279,11 @@ describe('tzkt network API', () => {
|
|
|
1280
1279
|
const fullPage = Array.from({ length: 1000 }, (_, i) => makeStakingItem(i + 1))
|
|
1281
1280
|
mockedNetwork.mockReturnValue(networkResponse(fullPage))
|
|
1282
1281
|
|
|
1283
|
-
const result = await fetchBlockStaking(
|
|
1282
|
+
const result = await fetchBlockStaking(context, 100)
|
|
1284
1283
|
|
|
1285
1284
|
expect(mockedNetwork).toHaveBeenCalledTimes(100)
|
|
1286
1285
|
expect(result).toHaveLength(100_000)
|
|
1287
|
-
expect(
|
|
1286
|
+
expect(mockLogger).toHaveBeenCalledWith(
|
|
1288
1287
|
'tezos',
|
|
1289
1288
|
expect.stringContaining('fetchBlockStaking: maxTxQuery limit reached at level 100')
|
|
1290
1289
|
)
|