@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +26 -17
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.integ.test.js +6 -0
- package/lib/api/index.integ.test.js.map +1 -1
- package/lib/api/index.js +38 -5
- package/lib/api/index.js.map +1 -1
- package/lib/api/index.test.js +5 -0
- package/lib/api/index.test.js.map +1 -1
- package/lib/api/types.d.ts +1 -2
- package/lib/api/types.d.ts.map +1 -1
- package/lib/logic/validateIntent.test.js +12 -0
- package/lib/logic/validateIntent.test.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +13 -14
- package/lib/utils.js.map +1 -1
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.integ.test.js +6 -0
- package/lib-es/api/index.integ.test.js.map +1 -1
- package/lib-es/api/index.js +39 -6
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/api/index.test.js +5 -0
- package/lib-es/api/index.test.js.map +1 -1
- package/lib-es/api/types.d.ts +1 -2
- package/lib-es/api/types.d.ts.map +1 -1
- package/lib-es/logic/validateIntent.test.js +12 -0
- package/lib-es/logic/validateIntent.test.js.map +1 -1
- package/lib-es/utils.d.ts +2 -0
- package/lib-es/utils.d.ts.map +1 -1
- package/lib-es/utils.js +9 -14
- package/lib-es/utils.js.map +1 -1
- package/package.json +8 -8
- package/src/api/index.integ.test.ts +8 -1
- package/src/api/index.test.ts +11 -6
- package/src/api/index.ts +49 -5
- package/src/api/types.ts +1 -2
- package/src/logic/validateIntent.test.ts +12 -0
- package/src/utils.ts +11 -15
package/src/api/index.test.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
315
|
+
value: totalFee,
|
|
272
316
|
parameters: {
|
|
273
317
|
gasLimit: 10000n,
|
|
274
|
-
storageLimit
|
|
318
|
+
storageLimit,
|
|
275
319
|
amount: 0n,
|
|
276
|
-
txFee:
|
|
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
|
|
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:
|
|
111
|
-
gasLimit:
|
|
112
|
-
storageLimit:
|
|
113
|
-
estimatedFees:
|
|
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
|
+
}
|