@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.
- package/CHANGELOG.md +25 -0
- package/lib/api/index.d.ts +1 -1
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +1 -1
- package/lib/api/index.js.map +1 -1
- package/lib/logic/craftRawOperations.js +26 -17
- package/lib/logic/craftRawOperations.js.map +1 -1
- package/lib/logic/estimateFees.js +4 -4
- package/lib/logic/estimateFees.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +9 -7
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/validateIntent.d.ts.map +1 -1
- package/lib/logic/validateIntent.js +26 -8
- package/lib/logic/validateIntent.js.map +1 -1
- package/lib/network/tzkt.js +1 -1
- package/lib/network/tzkt.js.map +1 -1
- package/lib/test/config.d.ts +2 -22
- package/lib/test/config.d.ts.map +1 -1
- package/lib/test/config.js.map +1 -1
- package/lib/utils.d.ts +0 -3
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +17 -3
- package/lib/utils.js.map +1 -1
- package/lib-es/api/index.d.ts +1 -1
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +1 -1
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/logic/craftRawOperations.js +26 -17
- package/lib-es/logic/craftRawOperations.js.map +1 -1
- package/lib-es/logic/estimateFees.js +4 -4
- package/lib-es/logic/estimateFees.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +9 -7
- 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 +27 -9
- package/lib-es/logic/validateIntent.js.map +1 -1
- package/lib-es/network/tzkt.js +1 -1
- package/lib-es/network/tzkt.js.map +1 -1
- package/lib-es/test/config.d.ts +2 -22
- package/lib-es/test/config.d.ts.map +1 -1
- package/lib-es/test/config.js.map +1 -1
- package/lib-es/utils.d.ts +0 -3
- package/lib-es/utils.d.ts.map +1 -1
- package/lib-es/utils.js +17 -3
- package/lib-es/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/api/index-mainnet.integ.test.ts +40 -50
- package/src/api/index.integ.test.ts +60 -45
- package/src/api/index.test.ts +229 -180
- package/src/api/index.ts +5 -4
- package/src/logic/broadcast.integ.test.ts +2 -7
- package/src/logic/craftRawOperations.test.ts +11 -10
- package/src/logic/craftRawOperations.ts +35 -19
- package/src/logic/craftTransaction.integ.test.ts +2 -2
- package/src/logic/craftTransaction.test.ts +63 -95
- package/src/logic/estimateFees.integ.test.ts +19 -20
- package/src/logic/estimateFees.test.ts +30 -63
- package/src/logic/estimateFees.ts +4 -4
- package/src/logic/getBalance.test.ts +6 -6
- package/src/logic/getBlock.integ.test.ts +20 -21
- package/src/logic/getBlock.test.ts +44 -51
- package/src/logic/getBlockInfo.integ.test.ts +13 -14
- package/src/logic/getBlockInfo.test.ts +8 -7
- package/src/logic/getStakes.test.ts +17 -17
- package/src/logic/listOperations.integ.test.ts +40 -37
- package/src/logic/listOperations.test.ts +34 -31
- package/src/logic/listOperations.ts +19 -19
- package/src/logic/validateIntent.test.ts +145 -20
- package/src/logic/validateIntent.ts +32 -8
- package/src/network/bakers.integ.test.ts +1 -1
- package/src/network/bakers.test.ts +7 -15
- package/src/network/tzkt.integ.test.ts +13 -14
- package/src/network/tzkt.test.ts +194 -259
- package/src/network/tzkt.ts +1 -1
- package/src/test/config.ts +3 -1
- package/src/utils.ts +18 -3
- 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
|
|
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' :
|
|
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:
|
|
333
|
+
} catch (error: unknown) {
|
|
333
334
|
// Handle PublicKeyNotFoundError
|
|
334
|
-
if (error
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
162
|
+
if (!isRecord(op)) {
|
|
138
163
|
throw new Error('craftRawOperations: invalid operation')
|
|
139
164
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
switch (candidate.kind) {
|
|
165
|
+
assertOptionalLimits(op)
|
|
166
|
+
switch (op.kind) {
|
|
143
167
|
case 'transaction':
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
assertMutezAmount(candidate.amount)
|
|
148
|
-
return candidate as PartialTezosTransactionOperation
|
|
168
|
+
assertTransaction(op)
|
|
169
|
+
return op
|
|
149
170
|
case 'delegation':
|
|
150
|
-
|
|
151
|
-
|
|
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(
|
|
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(
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
56
|
-
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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'
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
expect(
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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'
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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'
|
|
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
|
-
|
|
290
|
-
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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 {
|
|
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
|
|
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, {
|
|
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, {
|
|
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({
|