@ledgerhq/coin-tezos 6.6.0-nightly.3 → 6.7.0-nightly.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +26 -17
  3. package/lib/api/index.d.ts.map +1 -1
  4. package/lib/api/index.integ.test.js +6 -0
  5. package/lib/api/index.integ.test.js.map +1 -1
  6. package/lib/api/index.js +38 -5
  7. package/lib/api/index.js.map +1 -1
  8. package/lib/api/index.test.js +5 -0
  9. package/lib/api/index.test.js.map +1 -1
  10. package/lib/api/types.d.ts +1 -2
  11. package/lib/api/types.d.ts.map +1 -1
  12. package/lib/logic/validateIntent.test.js +12 -0
  13. package/lib/logic/validateIntent.test.js.map +1 -1
  14. package/lib/utils.d.ts +2 -0
  15. package/lib/utils.d.ts.map +1 -1
  16. package/lib/utils.js +13 -14
  17. package/lib/utils.js.map +1 -1
  18. package/lib-es/api/index.d.ts.map +1 -1
  19. package/lib-es/api/index.integ.test.js +6 -0
  20. package/lib-es/api/index.integ.test.js.map +1 -1
  21. package/lib-es/api/index.js +39 -6
  22. package/lib-es/api/index.js.map +1 -1
  23. package/lib-es/api/index.test.js +5 -0
  24. package/lib-es/api/index.test.js.map +1 -1
  25. package/lib-es/api/types.d.ts +1 -2
  26. package/lib-es/api/types.d.ts.map +1 -1
  27. package/lib-es/logic/validateIntent.test.js +12 -0
  28. package/lib-es/logic/validateIntent.test.js.map +1 -1
  29. package/lib-es/utils.d.ts +2 -0
  30. package/lib-es/utils.d.ts.map +1 -1
  31. package/lib-es/utils.js +9 -14
  32. package/lib-es/utils.js.map +1 -1
  33. package/package.json +8 -8
  34. package/src/api/index.integ.test.ts +8 -1
  35. package/src/api/index.test.ts +11 -6
  36. package/src/api/index.ts +49 -5
  37. package/src/api/types.ts +1 -2
  38. package/src/logic/validateIntent.test.ts +12 -0
  39. package/src/utils.ts +11 -15
@@ -2,7 +2,7 @@ import type { Operation } from "@ledgerhq/coin-framework/api/types";
2
2
  import type { APIAccount } from "../network/types";
3
3
  import networkApi from "../network/tzkt";
4
4
  import { createApi } from "./index";
5
- import type { TezosTransactionIntent } from "./types";
5
+ import { TransactionIntent } from "@ledgerhq/coin-framework/api/types";
6
6
 
7
7
  const DEFAULT_ESTIMATED_FEES = 300n;
8
8
  const DEFAULT_GAS_LIMIT = 30n;
@@ -107,11 +107,12 @@ describe("Testing craftTransaction function", () => {
107
107
  storageLimit: DEFAULT_STORAGE_LIMIT,
108
108
  });
109
109
  await api.craftTransaction({
110
+ intentType: "transaction",
110
111
  type: "send",
111
112
  sender: "tz1test",
112
113
  recipient: "tz1recipient",
113
114
  amount: 1000n,
114
- } as TezosTransactionIntent);
115
+ } as TransactionIntent);
115
116
  expect(logicEstimateFees).toHaveBeenCalledTimes(1);
116
117
  expect(logicCraftTransactionMock).toHaveBeenCalledWith(
117
118
  expect.objectContaining({ address: "tz1test" }),
@@ -139,11 +140,12 @@ describe("Testing craftTransaction function", () => {
139
140
  });
140
141
  await api.craftTransaction(
141
142
  {
143
+ intentType: "transaction",
142
144
  type: "send",
143
145
  sender: "tz1test",
144
146
  recipient: "tz1recipient",
145
147
  amount: 1000n,
146
- } as TezosTransactionIntent,
148
+ } as TransactionIntent,
147
149
  {
148
150
  value: customFees,
149
151
  },
@@ -173,11 +175,12 @@ describe("Testing estimateFees function", () => {
173
175
  storageLimit: DEFAULT_STORAGE_LIMIT,
174
176
  });
175
177
  const result = await api.estimateFees({
178
+ intentType: "transaction",
176
179
  type: "send",
177
180
  sender: "tz1test",
178
181
  recipient: "tz1recipient",
179
182
  amount: 1000n,
180
- } as TezosTransactionIntent);
183
+ } as TransactionIntent);
181
184
  expect(result).toEqual({
182
185
  value: DEFAULT_ESTIMATED_FEES,
183
186
  parameters: {
@@ -196,11 +199,12 @@ describe("Testing estimateFees function", () => {
196
199
  });
197
200
  await expect(
198
201
  api.estimateFees({
202
+ intentType: "transaction",
199
203
  type: "send",
200
204
  sender: "tz1test",
201
205
  recipient: "tz1recipient",
202
206
  amount: 1000n,
203
- } as TezosTransactionIntent),
207
+ } as TransactionIntent),
204
208
  ).rejects.toThrow("Fees estimation failed: test");
205
209
  });
206
210
 
@@ -212,11 +216,12 @@ describe("Testing estimateFees function", () => {
212
216
  taquitoError: "proto.022-PsRiotum.delegate.unchanged",
213
217
  });
214
218
  const result = await api.estimateFees({
219
+ intentType: "staking",
215
220
  type: "delegate",
216
221
  sender: "tz1test",
217
222
  recipient: "tz1validator",
218
223
  amount: 0n,
219
- } as TezosTransactionIntent);
224
+ } as TransactionIntent);
220
225
 
221
226
  expect(result).toEqual({
222
227
  value: DEFAULT_ESTIMATED_FEES,
package/src/api/index.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  validateIntent,
25
25
  getStakes,
26
26
  } from "../logic";
27
+ import { getTezosToolkit } from "../logic/tezosToolkit";
27
28
  import api from "../network/tzkt";
28
29
  import type { TezosApi, TezosFeeEstimation } from "./types";
29
30
  import type { FeeEstimation, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
@@ -32,6 +33,7 @@ import { validatePublicKey, ValidationResult, getPkhfromPk } from "@taquito/util
32
33
  import { getRevealFee } from "@taquito/taquito";
33
34
  import {
34
35
  DUST_MARGIN_MUTEZ,
36
+ hasEmptyBalance,
35
37
  mapIntentTypeToTezosMode,
36
38
  normalizePublicKeyForAddress,
37
39
  } from "../utils";
@@ -43,6 +45,14 @@ export function createApi(config: TezosConfig): TezosApi {
43
45
  broadcast,
44
46
  combine,
45
47
  craftTransaction: craft,
48
+ craftRawTransaction: (
49
+ _transaction: string,
50
+ _sender: string,
51
+ _publicKey: string,
52
+ _sequence: number,
53
+ ): Promise<CraftedTransaction> => {
54
+ throw new Error("craftRawTransaction is not supported");
55
+ },
46
56
  estimateFees: estimate,
47
57
  getBalance: balance,
48
58
  lastBlock,
@@ -151,11 +161,14 @@ async function craft(
151
161
 
152
162
  let txFee: number;
153
163
  if (customFees) {
154
- txFee = totalFee;
164
+ txFee = needsReveal ? Math.max(totalFee - getRevealFee(transactionIntent.sender), 0) : totalFee;
155
165
  } else if (estimation.parameters?.txFee !== undefined) {
156
166
  txFee = Number(estimation.parameters.txFee);
157
167
  } else {
158
- txFee = needsReveal ? Math.max(totalFee - getRevealFee(transactionIntent.sender), 0) : totalFee;
168
+ const calculatedTxFee = needsReveal
169
+ ? Math.max(totalFee - getRevealFee(transactionIntent.sender), 0)
170
+ : totalFee;
171
+ txFee = calculatedTxFee;
159
172
  }
160
173
 
161
174
  const txForCraft = {
@@ -198,6 +211,7 @@ async function craft(
198
211
 
199
212
  async function estimate(transactionIntent: TransactionIntent): Promise<TezosFeeEstimation> {
200
213
  // avoid taquito error when estimating a 0-amount transfer during input
214
+ const config = coinConfig.getCoinConfig();
201
215
  if (
202
216
  transactionIntent.type === "send" &&
203
217
  transactionIntent.amount === 0n &&
@@ -267,13 +281,43 @@ async function estimate(transactionIntent: TransactionIntent): Promise<TezosFeeE
267
281
  } catch (error: any) {
268
282
  // Handle PublicKeyNotFoundError
269
283
  if (error?.message?.includes("Public key not found")) {
284
+ const apiAccount = await api.getAccountByAddress(transactionIntent.recipient);
285
+ const storageLimit =
286
+ !hasEmptyBalance(apiAccount) || transactionIntent.type === "stake" ? 0n : 277n;
287
+
288
+ // Check if account needs reveal for proper fee calculation
289
+ const senderApiAcc = await api.getAccountByAddress(transactionIntent.sender);
290
+ const needsReveal = senderApiAcc.type === "user" && !senderApiAcc.revealed;
291
+
292
+ // Production-calibrated fallback fee when Taquito estimation fails (~388 mutez observed)
293
+ const DEFAULT_TX_FEE_FALLBACK = 388;
294
+ let baseTxFee: bigint;
295
+
296
+ try {
297
+ const toolkit = getTezosToolkit();
298
+ const simpleEstimate = await toolkit.estimate.transfer({
299
+ to: transactionIntent.recipient,
300
+ amount: Number(transactionIntent.amount),
301
+ mutez: true,
302
+ source: transactionIntent.sender,
303
+ });
304
+ // Use Taquito estimation, respecting minFees from config
305
+ baseTxFee = BigInt(Math.max(config.fees.minFees, simpleEstimate.suggestedFeeMutez));
306
+ } catch {
307
+ // Fallback to production-calibrated default if estimation fails
308
+ baseTxFee = BigInt(Math.max(DEFAULT_TX_FEE_FALLBACK, config.fees.minFees));
309
+ }
310
+
311
+ const revealFee = needsReveal ? BigInt(getRevealFee(transactionIntent.sender)) : 0n;
312
+ const totalFee = baseTxFee + revealFee;
313
+
270
314
  return {
271
- value: 1000n, // Safe default with reveal fees (500 + 374 reveal + buffer)
315
+ value: totalFee,
272
316
  parameters: {
273
317
  gasLimit: 10000n,
274
- storageLimit: 300n,
318
+ storageLimit,
275
319
  amount: 0n,
276
- txFee: 1000n,
320
+ txFee: baseTxFee,
277
321
  },
278
322
  };
279
323
  } else {
package/src/api/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Api, FeeEstimation, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
1
+ import type { Api, FeeEstimation } from "@ledgerhq/coin-framework/api/types";
2
2
 
3
3
  export type TezosFeeParameters = {
4
4
  gasLimit: bigint;
@@ -11,6 +11,5 @@ export type TezosFeeEstimation = FeeEstimation & {
11
11
  };
12
12
 
13
13
  export type TezosSender = { address: string; xpub?: string };
14
- export type TezosTransactionIntent = TransactionIntent;
15
14
 
16
15
  export type TezosApi = Api;
@@ -58,6 +58,7 @@ describe("validateIntent", () => {
58
58
  describe("recipient validation", () => {
59
59
  it("should return RecipientRequired error when recipient is missing", async () => {
60
60
  const result = await validateIntent({
61
+ intentType: "transaction",
61
62
  asset: { type: "native" },
62
63
  type: "send",
63
64
  sender: senderAddress,
@@ -70,6 +71,7 @@ describe("validateIntent", () => {
70
71
 
71
72
  it("should return InvalidAddress error for invalid recipient", async () => {
72
73
  const result = await validateIntent({
74
+ intentType: "transaction",
73
75
  asset: { type: "native" },
74
76
  type: "send",
75
77
  sender: senderAddress,
@@ -82,6 +84,7 @@ describe("validateIntent", () => {
82
84
 
83
85
  it("should return InvalidAddressBecauseDestinationIsAlsoSource when sender equals recipient", async () => {
84
86
  const result = await validateIntent({
87
+ intentType: "transaction",
85
88
  asset: { type: "native" },
86
89
  type: "send",
87
90
  sender: senderAddress,
@@ -96,6 +99,7 @@ describe("validateIntent", () => {
96
99
  describe("amount validation", () => {
97
100
  it("should return AmountRequired error when amount is zero and not useAllAmount", async () => {
98
101
  const result = await validateIntent({
102
+ intentType: "transaction",
99
103
  asset: { type: "native" },
100
104
  type: "send",
101
105
  sender: senderAddress,
@@ -109,6 +113,7 @@ describe("validateIntent", () => {
109
113
 
110
114
  it("should not return AmountRequired error when useAllAmount is true", async () => {
111
115
  const result = await validateIntent({
116
+ intentType: "transaction",
112
117
  asset: { type: "native" },
113
118
  type: "send",
114
119
  sender: senderAddress,
@@ -140,6 +145,7 @@ describe("validateIntent", () => {
140
145
  });
141
146
 
142
147
  const result = await validateIntent({
148
+ intentType: "transaction",
143
149
  asset: { type: "native" },
144
150
  type: "send",
145
151
  sender: senderAddress,
@@ -168,6 +174,7 @@ describe("validateIntent", () => {
168
174
  });
169
175
 
170
176
  const result = await validateIntent({
177
+ intentType: "transaction",
171
178
  asset: { type: "native" },
172
179
  type: "send",
173
180
  sender: senderAddress,
@@ -183,6 +190,7 @@ describe("validateIntent", () => {
183
190
  describe("transaction types", () => {
184
191
  it("should pass validation for delegate transaction", async () => {
185
192
  const result = await validateIntent({
193
+ intentType: "staking",
186
194
  asset: { type: "native" },
187
195
  type: "delegate",
188
196
  sender: senderAddress,
@@ -195,6 +203,7 @@ describe("validateIntent", () => {
195
203
 
196
204
  it("should pass validation for undelegate transaction", async () => {
197
205
  const result = await validateIntent({
206
+ intentType: "staking",
198
207
  asset: { type: "native" },
199
208
  type: "undelegate",
200
209
  sender: senderAddress,
@@ -207,6 +216,7 @@ describe("validateIntent", () => {
207
216
 
208
217
  it("should handle stake intent (mapped to delegate)", async () => {
209
218
  const result = await validateIntent({
219
+ intentType: "staking",
210
220
  asset: { type: "native" },
211
221
  type: "stake",
212
222
  sender: senderAddress,
@@ -219,6 +229,7 @@ describe("validateIntent", () => {
219
229
 
220
230
  it("should handle unstake intent (mapped to undelegate)", async () => {
221
231
  const result = await validateIntent({
232
+ intentType: "staking",
222
233
  asset: { type: "native" },
223
234
  type: "unstake",
224
235
  sender: senderAddress,
@@ -243,6 +254,7 @@ describe("validateIntent", () => {
243
254
  });
244
255
 
245
256
  const result = await validateIntent({
257
+ intentType: "transaction",
246
258
  asset: { type: "native" },
247
259
  type: "send",
248
260
  sender: senderAddress,
package/src/utils.ts CHANGED
@@ -1,17 +1,8 @@
1
1
  import { validatePublicKey, ValidationResult, b58Encode, PrefixV2 } from "@taquito/utils";
2
2
  import { DerivationType } from "@taquito/ledger-signer";
3
3
  import { compressPublicKey } from "@taquito/ledger-signer/dist/lib/utils";
4
-
5
- /**
6
- * Default limits and fees for Tezos operations
7
- */
8
- // Safe fallback values aligned with Taquito estimation defaults for a simple transfer
9
- // These are used when network estimation is unavailable (e.g., signer not configured)
10
- const DEFAULT_LIMITS = {
11
- GAS: 2169n,
12
- STORAGE: 277n,
13
- BASE_FEE: 491n,
14
- };
4
+ import type { APIAccount } from "./network/types";
5
+ import coinConfig from "./config";
15
6
 
16
7
  /**
17
8
  * Dust margin in mutez to prevent transaction failures on send max operations
@@ -106,10 +97,15 @@ export function normalizePublicKeyForAddress(
106
97
  * Creates default fallback estimation values
107
98
  */
108
99
  export function createFallbackEstimation() {
100
+ const config = coinConfig.getCoinConfig();
109
101
  return {
110
- fees: DEFAULT_LIMITS.BASE_FEE,
111
- gasLimit: DEFAULT_LIMITS.GAS,
112
- storageLimit: DEFAULT_LIMITS.STORAGE,
113
- estimatedFees: DEFAULT_LIMITS.BASE_FEE,
102
+ fees: BigInt(config.fees.minEstimatedFees),
103
+ gasLimit: BigInt(config.fees.minGasLimit),
104
+ storageLimit: BigInt(config.fees.minStorageLimit),
105
+ estimatedFees: BigInt(config.fees.minEstimatedFees),
114
106
  };
115
107
  }
108
+
109
+ export function hasEmptyBalance(account: APIAccount) {
110
+ return (account.type === "user" && account.balance === 0) || account.type === "empty";
111
+ }