@ledgerhq/coin-ton 0.8.2-nightly.2 → 0.8.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.
@@ -1,16 +1,9 @@
1
1
  import { encodeOperationId } from "@ledgerhq/coin-framework/lib/operation";
2
2
  import BigNumber from "bignumber.js";
3
3
  // eslint-disable-next-line no-restricted-imports
4
- import { Builder, Slice } from "@ton/core";
5
- import flatMap from "lodash/flatMap";
4
+ import { flatMap } from "lodash";
6
5
  import { TonJettonTransfer, TonTransaction } from "../../bridge/bridgeHelpers/api.types";
7
- import {
8
- dataToSlice,
9
- decodeForwardPayload,
10
- loadSnakeBytes,
11
- mapJettonTxToOps,
12
- mapTxToOps,
13
- } from "../../bridge/bridgeHelpers/txn";
6
+ import { mapJettonTxToOps, mapTxToOps } from "../../bridge/bridgeHelpers/txn";
14
7
  import {
15
8
  jettonTransferResponse,
16
9
  mockAccountId,
@@ -20,7 +13,7 @@ import {
20
13
 
21
14
  describe("Transaction functions", () => {
22
15
  describe("mapTxToOps", () => {
23
- it("should map an IN failed ton transaction without total_fees to a ledger operation", async () => {
16
+ it.skip("should map an IN ton transaction without total_fees to a ledger operation", async () => {
24
17
  const { now, lt, hash, in_msg, total_fees, mc_block_seqno } =
25
18
  tonTransactionResponse.transactions[0];
26
19
 
@@ -37,21 +30,20 @@ describe("Transaction functions", () => {
37
30
  date: new Date(now * 1000), // now is defined in seconds
38
31
  extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt },
39
32
  fee: BigNumber(total_fees),
40
- hasFailed: true,
33
+ hasFailed: false,
41
34
  hash: in_msg?.hash,
42
35
  id: encodeOperationId(mockAccountId, in_msg?.hash ?? "", "IN"),
43
36
  recipients: [in_msg?.destination],
44
37
  senders: ["EQCVnqqL0OOiZi2BQnjVGm-ZeUYgfUhHgAi-vn9F8-94HwrH"],
45
38
  type: "IN",
46
39
  value: BigNumber(in_msg?.value ?? 0),
47
- subOperations: undefined,
48
40
  },
49
41
  ]);
50
42
  });
51
43
 
52
- it("should map an IN ton transaction with total_fees to a ledger operation", async () => {
44
+ it.skip("should map an IN ton transaction with total_fees to a ledger operation", async () => {
53
45
  const transactions = [{ ...tonTransactionResponse.transactions[0], total_fees: "15" }];
54
- const { now, lt, hash, in_msg, total_fees, mc_block_seqno } = transactions[0];
46
+ const { now, lt, hash, in_msg, total_fees, mc_block_seqno, account } = transactions[0];
55
47
 
56
48
  const finalOperation = flatMap(
57
49
  transactions,
@@ -59,6 +51,21 @@ describe("Transaction functions", () => {
59
51
  );
60
52
 
61
53
  expect(finalOperation).toEqual([
54
+ {
55
+ id: encodeOperationId(mockAccountId, in_msg?.hash ?? "", "NONE"),
56
+ hash: in_msg?.hash,
57
+ type: "NONE",
58
+ value: BigNumber(total_fees),
59
+ fee: BigNumber(0),
60
+ blockHash: null,
61
+ blockHeight: mc_block_seqno,
62
+ hasFailed: false,
63
+ accountId: mockAccountId,
64
+ senders: [account],
65
+ recipients: [],
66
+ date: new Date(now * 1000), // now is defined in seconds
67
+ extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt },
68
+ },
62
69
  {
63
70
  accountId: mockAccountId,
64
71
  blockHash: null,
@@ -66,35 +73,18 @@ describe("Transaction functions", () => {
66
73
  date: new Date(now * 1000), // now is defined in seconds
67
74
  extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt },
68
75
  fee: BigNumber(total_fees),
69
- hasFailed: true,
76
+ hasFailed: false,
70
77
  hash: in_msg?.hash,
71
78
  id: encodeOperationId(mockAccountId, in_msg?.hash ?? "", "IN"),
72
79
  recipients: [in_msg?.destination],
73
80
  senders: ["EQCVnqqL0OOiZi2BQnjVGm-ZeUYgfUhHgAi-vn9F8-94HwrH"],
74
81
  type: "IN",
75
82
  value: BigNumber(in_msg?.value ?? 0),
76
- subOperations: [
77
- {
78
- id: encodeOperationId(mockAccountId, in_msg?.hash ?? "", "NONE"),
79
- hash: in_msg?.hash,
80
- type: "NONE",
81
- value: BigNumber(total_fees),
82
- fee: BigNumber(0),
83
- blockHeight: mc_block_seqno,
84
- blockHash: null,
85
- hasFailed: true,
86
- accountId: mockAccountId,
87
- senders: [mockAddress],
88
- recipients: [],
89
- date: new Date(now * 1000),
90
- extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt },
91
- },
92
- ],
93
83
  },
94
84
  ]);
95
85
  });
96
86
 
97
- it("should map a failed OUT ton transaction to a ledger operation", async () => {
87
+ it.skip("should map an OUT ton transaction to a ledger operation", async () => {
98
88
  // The IN transaction will be used as OUT transaction and it will be adjusted
99
89
  const transactions: TonTransaction[] = [
100
90
  {
@@ -107,7 +97,7 @@ describe("Transaction functions", () => {
107
97
  { ...tonTransactionResponse.transactions[0].in_msg, source: transactions[0].account },
108
98
  ];
109
99
  }
110
- const { now, lt, hash, out_msgs, total_fees, mc_block_seqno } = transactions[0];
100
+ const { now, lt, hash, out_msgs, total_fees, mc_block_seqno, account } = transactions[0];
111
101
 
112
102
  const finalOperation = flatMap(
113
103
  transactions,
@@ -116,16 +106,16 @@ describe("Transaction functions", () => {
116
106
 
117
107
  expect(finalOperation).toEqual([
118
108
  {
119
- id: encodeOperationId(mockAccountId, hash, "OUT"),
109
+ id: encodeOperationId(mockAccountId, hash ?? "", "OUT"),
120
110
  hash: out_msgs?.[0].hash,
121
111
  type: "OUT",
122
- value: BigNumber(out_msgs?.[0].value ?? 0),
112
+ value: BigNumber(out_msgs[0].value ?? 0).plus(BigNumber(total_fees)),
123
113
  fee: BigNumber(total_fees),
124
114
  blockHeight: mc_block_seqno,
125
115
  blockHash: null,
126
- hasFailed: true,
116
+ hasFailed: false,
127
117
  accountId: mockAccountId,
128
- senders: [transactions[0].account],
118
+ senders: [account],
129
119
  recipients: ["EQDzd8aeBOU-jqYw_ZSuZjceI5p-F4b7HMprAsUJAtRPbJfg"],
130
120
  date: new Date(now * 1000), // now is defined in seconds
131
121
  extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt },
@@ -210,171 +200,3 @@ describe("Transaction functions", () => {
210
200
  });
211
201
  });
212
202
  });
213
-
214
- describe("TON Payload Processing Functions", () => {
215
- describe("dataToSlice", () => {
216
- it("should convert base64 string to Slice when it's a valid BOC", () => {
217
- // Create a Cell from a string and convert to BOC
218
- const cell = new Builder().storeUint(123, 32).endCell();
219
- const bocBase64 = cell.toBoc().toString("base64");
220
-
221
- const result = dataToSlice(bocBase64);
222
-
223
- expect(result).toBeInstanceOf(Slice);
224
- expect(result?.loadUint(32)).toBe(123);
225
- });
226
-
227
- it("should fallback to BitString when the data is not a valid BOC", () => {
228
- const invalidBocBase64 = "aW52YWxpZCB0b24gZGF0YQ=="; // "invalid ton data"
229
-
230
- const result = dataToSlice(invalidBocBase64);
231
-
232
- expect(result).toBeInstanceOf(Slice);
233
- });
234
-
235
- it("should return undefined for non-string input", () => {
236
- // @ts-expect-error - Testing invalid input
237
- const result = dataToSlice(null);
238
-
239
- expect(result).toBeUndefined();
240
- });
241
- });
242
-
243
- describe("loadSnakeBytes", () => {
244
- it("should load bytes from a simple slice without refs", () => {
245
- const cell = new Builder().storeBuffer(Buffer.from("Slice", "utf-8")).endCell();
246
- const slice = cell.beginParse();
247
-
248
- const result = loadSnakeBytes(slice);
249
-
250
- expect(result.toString("utf-8")).toBe("Slice");
251
- });
252
-
253
- it("should load bytes from a slice with refs (snake structure)", () => {
254
- // Create a chain of cells (snake structure)
255
- const cell2 = new Builder().storeBuffer(Buffer.from(" Data", "utf-8")).endCell();
256
- const cell1 = new Builder()
257
- .storeBuffer(Buffer.from("Slice", "utf-8"))
258
- .storeRef(cell2)
259
- .endCell();
260
-
261
- const slice = cell1.beginParse();
262
-
263
- const result = loadSnakeBytes(slice);
264
-
265
- expect(result.toString("utf-8")).toBe("Slice Data");
266
- });
267
-
268
- it("should handle empty slice", () => {
269
- const cell = new Builder().endCell();
270
- const slice = cell.beginParse();
271
-
272
- const result = loadSnakeBytes(slice);
273
-
274
- expect(result.length).toBe(0);
275
- });
276
-
277
- it("should handle slice with multiple refs in chain", () => {
278
- // Create a longer chain of cells (snake structure)
279
- const cell3 = new Builder().storeBuffer(Buffer.from("Part3", "utf-8")).endCell();
280
- const cell2 = new Builder()
281
- .storeBuffer(Buffer.from("Part2", "utf-8"))
282
- .storeRef(cell3)
283
- .endCell();
284
- const cell1 = new Builder()
285
- .storeBuffer(Buffer.from("Part1", "utf-8"))
286
- .storeRef(cell2)
287
- .endCell();
288
-
289
- const slice = cell1.beginParse();
290
-
291
- const result = loadSnakeBytes(slice);
292
-
293
- expect(result.toString("utf-8")).toBe("Part1Part2Part3");
294
- });
295
- });
296
-
297
- describe("decodeForwardPayload", () => {
298
- it("should return empty string for null payload", () => {
299
- const result = decodeForwardPayload(null);
300
-
301
- expect(result).toBe("");
302
- });
303
-
304
- it("should decode a valid payload with opcode 0 containing text", () => {
305
- // Create a cell with opcode 0 followed by a text string
306
- const cell = new Builder()
307
- .storeUint(0, 32) // opcode 0
308
- .storeBuffer(Buffer.from("This is the comment", "utf-8"))
309
- .endCell();
310
- const bocBase64 = cell.toBoc().toString("base64");
311
-
312
- const result = decodeForwardPayload(bocBase64);
313
-
314
- expect(result).toBe("This is the comment");
315
- });
316
-
317
- it("should return empty string for payloads with non-zero opcode", () => {
318
- // Create a cell with opcode 1 followed by some data
319
- const cell = new Builder()
320
- .storeUint(1, 32) // non-zero opcode
321
- .storeBuffer(Buffer.from("Should be ignored", "utf-8"))
322
- .endCell();
323
- const bocBase64 = cell.toBoc().toString("base64");
324
-
325
- const result = decodeForwardPayload(bocBase64);
326
-
327
- expect(result).toBe("");
328
- });
329
-
330
- it("should handle payload with unicode characters", () => {
331
- // Create a cell with opcode 0 followed by a text with unicode
332
- const cell = new Builder()
333
- .storeUint(0, 32) // opcode 0
334
- .storeBuffer(Buffer.from("Unicode: 你好, мир, 🚀", "utf-8"))
335
- .endCell();
336
- const bocBase64 = cell.toBoc().toString("base64");
337
-
338
- const result = decodeForwardPayload(bocBase64);
339
-
340
- expect(result).toBe("Unicode: 你好, мир, 🚀");
341
- });
342
-
343
- it("should handle snake format payloads correctly", () => {
344
- // Create a chain of cells with opcode 0 followed by a long message
345
- const cell2 = new Builder()
346
- .storeBuffer(Buffer.from(" would need multiple cells to store.", "utf-8"))
347
- .endCell();
348
- const cell1 = new Builder()
349
- .storeUint(0, 32) // opcode 0
350
- .storeBuffer(Buffer.from("This is a very long message that", "utf-8"))
351
- .storeRef(cell2)
352
- .endCell();
353
-
354
- const bocBase64 = cell1.toBoc().toString("base64");
355
-
356
- const result = decodeForwardPayload(bocBase64);
357
-
358
- expect(result).toBe("This is a very long message that would need multiple cells to store.");
359
- });
360
-
361
- it("should handle invalid payloads gracefully by returning empty string", () => {
362
- // Create an invalid base64 string
363
- const invalidBase64 = "!@#$%^&*()";
364
-
365
- const result = decodeForwardPayload(invalidBase64);
366
-
367
- expect(result).toBe("");
368
- });
369
-
370
- it("should handle valid base64 but invalid BOC payloads", () => {
371
- // Valid base64 but not a valid BOC
372
- const validBase64NotBoc = "aW52YWxpZCB0b24gZGF0YQ=="; // "invalid ton data" in base64
373
-
374
- const result = decodeForwardPayload(validBase64NotBoc);
375
-
376
- // Should return empty string as it's not a valid Cell
377
- expect(result).toBe("");
378
- });
379
- });
380
- });
@@ -2,7 +2,7 @@ import { decodeAccountId, encodeTokenAccountId } from "@ledgerhq/coin-framework/
2
2
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
3
3
  import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets/tokens";
4
4
  import { Operation } from "@ledgerhq/types-live";
5
- import { Address, BitReader, BitString, Cell, Slice } from "@ton/core";
5
+ import { Address, Cell } from "@ton/core";
6
6
  import BigNumber from "bignumber.js";
7
7
  import { TonOperation } from "../../types";
8
8
  import { addressesAreEqual, isAddressValid } from "../../utils";
@@ -281,61 +281,25 @@ export function mapJettonTxToOps(
281
281
  },
282
282
  });
283
283
  }
284
+
284
285
  return ops;
285
286
  };
286
287
  }
287
288
 
288
- // Export these functions for testing
289
- export function dataToSlice(data: string): Slice | undefined {
290
- let buffer: Buffer;
291
- if (typeof data === "string") {
292
- buffer = Buffer.from(data, "base64");
293
-
294
- try {
295
- return Cell.fromBoc(buffer)[0].beginParse();
296
- } catch (err) {
297
- return new Slice(new BitReader(new BitString(buffer, 0, buffer.length * 8)), []);
298
- }
299
- }
300
-
301
- return undefined;
302
- }
303
-
304
- export function loadSnakeBytes(slice: Slice): Buffer {
305
- let buffer = Buffer.alloc(0);
306
-
307
- while (slice.remainingBits >= 8) {
308
- buffer = Buffer.concat([buffer, slice.loadBuffer(slice.remainingBits / 8)]);
309
- if (slice.remainingRefs) {
310
- slice = slice.loadRef().beginParse();
311
- } else {
312
- break;
313
- }
314
- }
315
-
316
- return buffer;
317
- }
318
-
319
- export function decodeForwardPayload(payload: string | null): string {
289
+ function decodeForwardPayload(payload: string | null): string {
320
290
  if (!payload) return "";
321
291
 
322
- try {
323
- const slice = dataToSlice(payload);
324
-
325
- if (!slice) return "";
292
+ const decodedBuffer = Buffer.from(payload, "base64");
293
+ const cell = Cell.fromBoc(decodedBuffer)[0];
294
+ const slice = cell.beginParse();
326
295
 
327
- const opcode = slice.loadUint(32);
328
-
329
- // Format with opcode 0 followed by text
330
- if (opcode !== 0) {
331
- return "";
332
- }
333
- const buffer = loadSnakeBytes(slice);
334
- const comment = buffer.toString("utf-8");
335
-
336
- return comment;
337
- } catch (error) {
338
- // Silent failure, returning empty string
296
+ // Read the opcode
297
+ const opcode = slice.loadUint(32);
298
+ if (opcode !== 0) {
339
299
  return "";
340
300
  }
301
+
302
+ // Read the comment
303
+ const comment = slice.loadStringTail();
304
+ return comment;
341
305
  }