@ledgerhq/coin-aptos 1.10.0-nightly.2 → 1.10.0-nightly.3

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 (72) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +8 -0
  3. package/lib/__tests__/api/craftTransaction.unit.test.js +147 -10
  4. package/lib/__tests__/api/craftTransaction.unit.test.js.map +1 -1
  5. package/lib/__tests__/api/index.integ.test.js +175 -2
  6. package/lib/__tests__/api/index.integ.test.js.map +1 -1
  7. package/lib/__tests__/logic/buildTransaction.test.js +8 -6
  8. package/lib/__tests__/logic/buildTransaction.test.js.map +1 -1
  9. package/lib/__tests__/network/client.test.js +1 -1
  10. package/lib/__tests__/network/client.test.js.map +1 -1
  11. package/lib/constants.d.ts +4 -1
  12. package/lib/constants.d.ts.map +1 -1
  13. package/lib/constants.js +6 -2
  14. package/lib/constants.js.map +1 -1
  15. package/lib/logic/buildTransaction.d.ts +4 -2
  16. package/lib/logic/buildTransaction.d.ts.map +1 -1
  17. package/lib/logic/buildTransaction.js +30 -28
  18. package/lib/logic/buildTransaction.js.map +1 -1
  19. package/lib/logic/craftTransaction.d.ts.map +1 -1
  20. package/lib/logic/craftTransaction.js +51 -6
  21. package/lib/logic/craftTransaction.js.map +1 -1
  22. package/lib/logic/transactionsToOperations.d.ts +1 -1
  23. package/lib/logic/transactionsToOperations.d.ts.map +1 -1
  24. package/lib/logic/transactionsToOperations.js +8 -10
  25. package/lib/logic/transactionsToOperations.js.map +1 -1
  26. package/lib/network/client.js +2 -2
  27. package/lib/network/client.js.map +1 -1
  28. package/lib/types/assets.d.ts +5 -1
  29. package/lib/types/assets.d.ts.map +1 -1
  30. package/lib/types/index.d.ts +1 -1
  31. package/lib/types/index.d.ts.map +1 -1
  32. package/lib-es/__tests__/api/craftTransaction.unit.test.js +147 -10
  33. package/lib-es/__tests__/api/craftTransaction.unit.test.js.map +1 -1
  34. package/lib-es/__tests__/api/index.integ.test.js +175 -2
  35. package/lib-es/__tests__/api/index.integ.test.js.map +1 -1
  36. package/lib-es/__tests__/logic/buildTransaction.test.js +8 -6
  37. package/lib-es/__tests__/logic/buildTransaction.test.js.map +1 -1
  38. package/lib-es/__tests__/network/client.test.js +1 -1
  39. package/lib-es/__tests__/network/client.test.js.map +1 -1
  40. package/lib-es/constants.d.ts +4 -1
  41. package/lib-es/constants.d.ts.map +1 -1
  42. package/lib-es/constants.js +5 -1
  43. package/lib-es/constants.js.map +1 -1
  44. package/lib-es/logic/buildTransaction.d.ts +4 -2
  45. package/lib-es/logic/buildTransaction.d.ts.map +1 -1
  46. package/lib-es/logic/buildTransaction.js +30 -30
  47. package/lib-es/logic/buildTransaction.js.map +1 -1
  48. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  49. package/lib-es/logic/craftTransaction.js +28 -6
  50. package/lib-es/logic/craftTransaction.js.map +1 -1
  51. package/lib-es/logic/transactionsToOperations.d.ts +1 -1
  52. package/lib-es/logic/transactionsToOperations.d.ts.map +1 -1
  53. package/lib-es/logic/transactionsToOperations.js +8 -10
  54. package/lib-es/logic/transactionsToOperations.js.map +1 -1
  55. package/lib-es/network/client.js +2 -2
  56. package/lib-es/network/client.js.map +1 -1
  57. package/lib-es/types/assets.d.ts +5 -1
  58. package/lib-es/types/assets.d.ts.map +1 -1
  59. package/lib-es/types/index.d.ts +1 -1
  60. package/lib-es/types/index.d.ts.map +1 -1
  61. package/package.json +5 -5
  62. package/src/__tests__/api/craftTransaction.unit.test.ts +193 -20
  63. package/src/__tests__/api/index.integ.test.ts +226 -2
  64. package/src/__tests__/logic/buildTransaction.test.ts +8 -6
  65. package/src/__tests__/network/client.test.ts +1 -1
  66. package/src/constants.ts +5 -1
  67. package/src/logic/buildTransaction.ts +45 -37
  68. package/src/logic/craftTransaction.ts +46 -7
  69. package/src/logic/transactionsToOperations.ts +9 -12
  70. package/src/network/client.ts +2 -2
  71. package/src/types/assets.ts +3 -1
  72. package/src/types/index.ts +1 -1
@@ -1,9 +1,9 @@
1
- import { Hex, type InputEntryFunctionData, RawTransaction, Deserializer } from "@aptos-labs/ts-sdk";
1
+ import { Hex, RawTransaction, Deserializer } from "@aptos-labs/ts-sdk";
2
2
  import type { TransactionIntent } from "@ledgerhq/coin-framework/lib/api/types";
3
3
  import { createApi } from "../../api";
4
- import type { TransactionOptions } from "../../types";
5
4
  import type { AptosAsset, AptosExtra, AptosSender } from "../../types/assets";
6
5
  import { AptosAPI } from "../../network";
6
+ import { APTOS_ASSET_ID } from "../../constants";
7
7
 
8
8
  jest.mock("../../network");
9
9
  let mockedAptosApi: jest.Mocked<any>;
@@ -12,7 +12,7 @@ jest.mock("../../config", () => ({
12
12
  setCoinConfig: jest.fn(),
13
13
  }));
14
14
 
15
- describe("returns a valid transaction", () => {
15
+ describe("craftTransaction", () => {
16
16
  beforeEach(() => {
17
17
  mockedAptosApi = jest.mocked(AptosAPI);
18
18
  });
@@ -21,29 +21,113 @@ describe("returns a valid transaction", () => {
21
21
  jest.resetAllMocks();
22
22
  });
23
23
 
24
- it("creates a valid transaction", async () => {
25
- const SENDER_ADDR = "APTOS_1_ADDRESS";
26
- const RECIPIENT_ADDR = "APTOS_2_ADDRESS";
24
+ const SENDER_ADDR = "APTOS_1_ADDRESS";
25
+ const RECIPIENT_ADDR = "APTOS_2_ADDRESS";
27
26
 
28
- const hexRawTx =
29
- "0xfdde1012c0fac1f9a121eb3c8481c90d473df1c4180c070bd4f2549a6d06180400000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002203f5f0fcc8a909f23806e5efbdc1757e653fcd744de516a7de12b99b8417925c1080a00000000000000400d03000000000064000000000000002c721b6800000000b7";
30
- const rawTxn = RawTransaction.deserialize(
31
- new Deserializer(Hex.fromHexString(hexRawTx).toUint8Array()),
27
+ const hexRawTx =
28
+ "0xfdde1012c0fac1f9a121eb3c8481c90d473df1c4180c070bd4f2549a6d06180400000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002203f5f0fcc8a909f23806e5efbdc1757e653fcd744de516a7de12b99b8417925c1080a00000000000000400d03000000000064000000000000002c721b6800000000b7";
29
+
30
+ const rawTxn = RawTransaction.deserialize(
31
+ new Deserializer(Hex.fromHexString(hexRawTx).toUint8Array()),
32
+ );
33
+
34
+ it("creates a coin transaction", async () => {
35
+ const mockGenerateTransaction = jest.fn().mockResolvedValue(rawTxn);
36
+ const mockGetBalances = jest
37
+ .fn()
38
+ .mockResolvedValue([{ contractAddress: APTOS_ASSET_ID, amount: 12345 }]);
39
+ mockedAptosApi.mockImplementation(() => ({
40
+ generateTransaction: mockGenerateTransaction,
41
+ getBalances: mockGetBalances,
42
+ }));
43
+
44
+ const SENDER: AptosSender = {
45
+ xpub: "public-key",
46
+ freshAddress: SENDER_ADDR,
47
+ };
48
+ const api = createApi({
49
+ aptosSettings: {},
50
+ });
51
+
52
+ const txArg: TransactionIntent<AptosAsset, AptosExtra, AptosSender> = {
53
+ type: "send",
54
+ sender: SENDER,
55
+ recipient: RECIPIENT_ADDR,
56
+ amount: 10n,
57
+ asset: { type: "native" },
58
+ };
59
+
60
+ const tx = await api.craftTransaction(txArg);
61
+
62
+ expect(tx).not.toEqual("");
63
+ expect(Hex.isValid(tx).valid).toBeTruthy();
64
+ expect(mockGetBalances).toHaveBeenCalledTimes(0);
65
+ expect(mockGenerateTransaction).toHaveBeenCalledTimes(1);
66
+
67
+ expect(mockGenerateTransaction).toHaveBeenCalledWith(
68
+ SENDER.freshAddress,
69
+ expect.objectContaining({
70
+ function: "0x1::aptos_account::transfer_coins",
71
+ typeArguments: [APTOS_ASSET_ID],
72
+ functionArguments: [RECIPIENT_ADDR, txArg.amount.toString()],
73
+ }),
74
+ expect.anything(),
32
75
  );
76
+ });
33
77
 
34
- const mockGenerateTransaction = jest
78
+ it("creates a native coin transaction using all amount available", async () => {
79
+ const mockGenerateTransaction = jest.fn().mockResolvedValue(rawTxn);
80
+ const mockGetBalances = jest
35
81
  .fn()
36
- .mockImplementation(
37
- (_address: string, _payload: InputEntryFunctionData, _options: TransactionOptions) =>
38
- Promise.resolve(rawTxn),
39
- );
82
+ .mockResolvedValue([{ contractAddress: APTOS_ASSET_ID, amount: 12345 }]);
40
83
  mockedAptosApi.mockImplementation(() => ({
41
84
  generateTransaction: mockGenerateTransaction,
85
+ getBalances: mockGetBalances,
42
86
  }));
43
- const mockGenerateTransactionSpy = jest.spyOn(
44
- { generateTransaction: mockGenerateTransaction },
45
- "generateTransaction",
87
+
88
+ const SENDER: AptosSender = {
89
+ xpub: "public-key",
90
+ freshAddress: SENDER_ADDR,
91
+ };
92
+ const api = createApi({
93
+ aptosSettings: {},
94
+ });
95
+
96
+ const txArg: TransactionIntent<AptosAsset, AptosExtra, AptosSender> = {
97
+ type: "send",
98
+ sender: SENDER,
99
+ recipient: RECIPIENT_ADDR,
100
+ amount: 0n,
101
+ asset: { type: "native" },
102
+ };
103
+
104
+ const tx = await api.craftTransaction(txArg);
105
+
106
+ expect(tx).not.toEqual("");
107
+ expect(Hex.isValid(tx).valid).toBeTruthy();
108
+ expect(mockGetBalances).toHaveBeenCalledTimes(1);
109
+ expect(mockGenerateTransaction).toHaveBeenCalledTimes(1);
110
+
111
+ expect(mockGenerateTransaction).toHaveBeenCalledWith(
112
+ SENDER.freshAddress,
113
+ expect.objectContaining({
114
+ function: "0x1::aptos_account::transfer_coins",
115
+ typeArguments: [APTOS_ASSET_ID],
116
+ functionArguments: [RECIPIENT_ADDR, "12345"],
117
+ }),
118
+ expect.anything(),
46
119
  );
120
+ });
121
+
122
+ it("creates a coin token transaction", async () => {
123
+ const mockGenerateTransaction = jest.fn().mockResolvedValue(rawTxn);
124
+ const mockGetBalances = jest
125
+ .fn()
126
+ .mockResolvedValue([{ contractAddress: "0x42::token::Token", amount: 12345 }]);
127
+ mockedAptosApi.mockImplementation(() => ({
128
+ generateTransaction: mockGenerateTransaction,
129
+ getBalances: mockGetBalances,
130
+ }));
47
131
 
48
132
  const SENDER: AptosSender = {
49
133
  xpub: "public-key",
@@ -58,13 +142,102 @@ describe("returns a valid transaction", () => {
58
142
  sender: SENDER,
59
143
  recipient: RECIPIENT_ADDR,
60
144
  amount: 10n,
61
- asset: { type: "native" },
145
+ asset: { type: "token", standard: "coin", contractAddress: "0x42::token::Token" },
62
146
  };
63
147
 
64
148
  const tx = await api.craftTransaction(txArg);
65
149
 
66
150
  expect(tx).not.toEqual("");
67
151
  expect(Hex.isValid(tx).valid).toBeTruthy();
68
- expect(mockGenerateTransactionSpy).toHaveBeenCalledTimes(1);
152
+ expect(mockGetBalances).toHaveBeenCalledTimes(0);
153
+ expect(mockGenerateTransaction).toHaveBeenCalledTimes(1);
154
+
155
+ expect(mockGenerateTransaction).toHaveBeenCalledWith(
156
+ SENDER.freshAddress,
157
+ expect.objectContaining({
158
+ function: "0x1::aptos_account::transfer_coins",
159
+ typeArguments: ["0x42::token::Token"],
160
+ functionArguments: [RECIPIENT_ADDR, txArg.amount.toString()],
161
+ }),
162
+ expect.anything(),
163
+ );
164
+ });
165
+
166
+ it("creates a fungible_asset token transaction using all amount available", async () => {
167
+ const mockGenerateTransaction = jest.fn().mockResolvedValue(rawTxn);
168
+ const mockGetBalances = jest
169
+ .fn()
170
+ .mockResolvedValue([{ contractAddress: "0x42", amount: 12345 }]);
171
+ mockedAptosApi.mockImplementation(() => ({
172
+ generateTransaction: mockGenerateTransaction,
173
+ getBalances: mockGetBalances,
174
+ }));
175
+
176
+ const SENDER: AptosSender = {
177
+ xpub: "public-key",
178
+ freshAddress: SENDER_ADDR,
179
+ };
180
+ const api = createApi({
181
+ aptosSettings: {},
182
+ });
183
+
184
+ const txArg: TransactionIntent<AptosAsset, AptosExtra, AptosSender> = {
185
+ type: "send",
186
+ sender: SENDER,
187
+ recipient: RECIPIENT_ADDR,
188
+ amount: 0n,
189
+ asset: { type: "token", standard: "fungible_asset", contractAddress: "0x42" },
190
+ };
191
+
192
+ const tx = await api.craftTransaction(txArg);
193
+
194
+ expect(tx).not.toEqual("");
195
+ expect(Hex.isValid(tx).valid).toBeTruthy();
196
+ expect(mockGetBalances).toHaveBeenCalledTimes(1);
197
+ expect(mockGenerateTransaction).toHaveBeenCalledTimes(1);
198
+
199
+ expect(mockGenerateTransaction).toHaveBeenCalledWith(
200
+ SENDER.freshAddress,
201
+ expect.objectContaining({
202
+ function: "0x1::primary_fungible_store::transfer",
203
+ typeArguments: ["0x1::fungible_asset::Metadata"],
204
+ functionArguments: ["0x42", RECIPIENT_ADDR, "12345"],
205
+ }),
206
+ expect.anything(),
207
+ );
208
+ });
209
+
210
+ it("throws an exception for invalid tokenType", async () => {
211
+ const mockGenerateTransaction = jest.fn().mockResolvedValue(rawTxn);
212
+ const mockGetBalances = jest
213
+ .fn()
214
+ .mockResolvedValue([{ contractAddress: "0x42::token::Token", amount: 12345 }]);
215
+ mockedAptosApi.mockImplementation(() => ({
216
+ generateTransaction: mockGenerateTransaction,
217
+ getBalances: mockGetBalances,
218
+ }));
219
+
220
+ const SENDER: AptosSender = {
221
+ xpub: "public-key",
222
+ freshAddress: SENDER_ADDR,
223
+ };
224
+ const api = createApi({
225
+ aptosSettings: {},
226
+ });
227
+
228
+ const txArg: TransactionIntent<AptosAsset, AptosExtra, AptosSender> = {
229
+ type: "send",
230
+ sender: SENDER,
231
+ recipient: RECIPIENT_ADDR,
232
+ amount: 10n,
233
+ asset: { type: "token", standard: "asset", contractAddress: "0x42::token::Token" },
234
+ };
235
+
236
+ expect(async () => await api.craftTransaction(txArg)).rejects.toThrow(
237
+ "Token type asset not supported",
238
+ );
239
+
240
+ expect(mockGetBalances).toHaveBeenCalledTimes(0);
241
+ expect(mockGenerateTransaction).toHaveBeenCalledTimes(0);
69
242
  });
70
243
  });
@@ -22,6 +22,11 @@ describe("createApi", () => {
22
22
  freshAddress: "0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb",
23
23
  };
24
24
 
25
+ const tokenAccount: AptosSender = {
26
+ xpub: "eacada8192f15185637e475d7783e14486e232d8b9978ffa127383847ffc5318",
27
+ freshAddress: "0xb8922507317d85197d70c2bc1afc949c759fd0a62c8841a4300d1e2b63649bf6",
28
+ };
29
+
25
30
  describe("lastBlock", () => {
26
31
  it("returns the last block information", async () => {
27
32
  const lastBlock = await api.lastBlock();
@@ -33,6 +38,7 @@ describe("createApi", () => {
33
38
  expect(lastBlock.height).toBeGreaterThan(0);
34
39
 
35
40
  expect(lastBlock).toHaveProperty("time");
41
+
36
42
  expect(lastBlock.time).not.toBeUndefined();
37
43
  expect(lastBlock.time?.getFullYear()).toBeGreaterThan(0);
38
44
  expect(lastBlock.time?.getMonth()).toBeGreaterThan(0);
@@ -59,7 +65,7 @@ describe("createApi", () => {
59
65
  });
60
66
 
61
67
  describe("craftTransaction", () => {
62
- it("returns a RawTransaction serialized into an hexadecimal string", async () => {
68
+ it("returns a native coin RawTransaction serialized into an hexadecimal string", async () => {
63
69
  const hex = await api.craftTransaction(
64
70
  {
65
71
  amount: 1n,
@@ -79,6 +85,85 @@ describe("createApi", () => {
79
85
  expect(rawTx.gas_unit_price.toString()).toBe(DEFAULT_GAS_PRICE.toString());
80
86
  expect(rawTx.max_gas_amount.toString()).toBe(DEFAULT_GAS.toString());
81
87
  });
88
+
89
+ it("returns a coin token RawTransaction serialized into an hexadecimal string", async () => {
90
+ const hex = await api.craftTransaction(
91
+ {
92
+ amount: 1n,
93
+ sender: sender,
94
+ recipient: recipient.freshAddress,
95
+ type: "send",
96
+ asset: {
97
+ type: "token",
98
+ standard: "coin",
99
+ contractAddress:
100
+ "0x50788befc1107c0cc4473848a92e5c783c635866ce3c98de71d2eeb7d2a34f85::aptos_coin::AptosCoin",
101
+ },
102
+ },
103
+ 0n,
104
+ );
105
+
106
+ const rawTx = RawTransaction.deserialize(
107
+ new Deserializer(Hex.fromHexString(hex).toUint8Array()),
108
+ );
109
+
110
+ expect(rawTx.sender.toString()).toBe(sender.freshAddress);
111
+ expect(rawTx.gas_unit_price.toString()).toBe(DEFAULT_GAS_PRICE.toString());
112
+ expect(rawTx.max_gas_amount.toString()).toBe(DEFAULT_GAS.toString());
113
+ });
114
+
115
+ it("returns a use-all-amount coin token RawTransaction serialized into an hexadecimal string", async () => {
116
+ const hex = await api.craftTransaction(
117
+ {
118
+ amount: 0n,
119
+ sender: sender,
120
+ recipient: recipient.freshAddress,
121
+ type: "send",
122
+ asset: {
123
+ type: "token",
124
+ standard: "coin",
125
+ contractAddress:
126
+ "0x50788befc1107c0cc4473848a92e5c783c635866ce3c98de71d2eeb7d2a34f85::aptos_coin::AptosCoin",
127
+ },
128
+ },
129
+ 0n,
130
+ );
131
+
132
+ const rawTx = RawTransaction.deserialize(
133
+ new Deserializer(Hex.fromHexString(hex).toUint8Array()),
134
+ );
135
+
136
+ expect(rawTx.sender.toString()).toBe(sender.freshAddress);
137
+ expect(rawTx.gas_unit_price.toString()).toBe(DEFAULT_GAS_PRICE.toString());
138
+ expect(rawTx.max_gas_amount.toString()).toBe(DEFAULT_GAS.toString());
139
+ });
140
+
141
+ it("returns a use-all-amount fungible_asset token RawTransaction serialized into an hexadecimal string", async () => {
142
+ const r = sender;
143
+ const s = recipient; // recipient contains fungible_assets balances
144
+ const hex = await api.craftTransaction(
145
+ {
146
+ amount: 0n,
147
+ sender: s,
148
+ recipient: r.freshAddress,
149
+ type: "send",
150
+ asset: {
151
+ type: "token",
152
+ standard: "fungible_asset",
153
+ contractAddress: "0x2ebb2ccac5e027a87fa0e2e5f656a3a4238d6a48d93ec9b610d570fc0aa0df12",
154
+ },
155
+ },
156
+ 0n,
157
+ );
158
+
159
+ const rawTx = RawTransaction.deserialize(
160
+ new Deserializer(Hex.fromHexString(hex).toUint8Array()),
161
+ );
162
+
163
+ expect(rawTx.sender.toString()).toBe(s.freshAddress);
164
+ expect(rawTx.gas_unit_price.toString()).toBe(DEFAULT_GAS_PRICE.toString());
165
+ expect(rawTx.max_gas_amount.toString()).toBe(DEFAULT_GAS.toString());
166
+ });
82
167
  });
83
168
 
84
169
  describe("getBalance", () => {
@@ -86,7 +171,7 @@ describe("createApi", () => {
86
171
  const balances = await api.getBalance(sender.freshAddress);
87
172
 
88
173
  expect(balances.length).toBeGreaterThan(0);
89
- expect(balances[0].value).toBeGreaterThan(0);
174
+ expect(balances[0].value).toBeGreaterThanOrEqual(0);
90
175
  });
91
176
  });
92
177
 
@@ -136,4 +221,143 @@ describe("createApi", () => {
136
221
  });
137
222
  });
138
223
  });
224
+
225
+ describe("listOperationsTokens", () => {
226
+ it("returns operations from account", async () => {
227
+ const block = await api.lastBlock();
228
+
229
+ const [operations] = await api.listOperations(tokenAccount.freshAddress, {
230
+ minHeight: block.height,
231
+ });
232
+
233
+ expect(operations).toBeInstanceOf(Array);
234
+ expect(operations.length).toBeGreaterThanOrEqual(1);
235
+
236
+ const txINNativeHash = "0xd841b502ce333e5b9ac394db81e6597d9bbbe475e5b12966b98b843d91cc0a09";
237
+ const txOUTNativeHash = "0xb1114396c5b6f2f5ba955fa8e4102d0e3983d5ccdb0717ff792f6e4848e72366";
238
+
239
+ const txINLegacyCoinHash =
240
+ "0x6869c933396d976af85b273a825fe264a910d0064fe28ce97285073b9e5306bb";
241
+ const txOUTLegacyCoinHash =
242
+ "0xa720db7fe6327b1db23e778df5856ab1d8785d7e8edae98941a3f6e05fa58ddd";
243
+
244
+ const txINFungibleAssetHash =
245
+ "0x88856968603dee4f08579036bc30322b9a5f329561656888e3467ce27cc11ea7";
246
+ const txOUTFungibleAssetHash =
247
+ "0x8aa9e980760fe8aeb6804f387350b3019a2471aa61a5506a260c32cd5d6db32c";
248
+
249
+ const operationIN = operations.find(operation => operation.id === txINNativeHash);
250
+ const operationOUT = operations.find(operation => operation.id === txOUTNativeHash);
251
+
252
+ expect(operationIN).toMatchObject({
253
+ type: "IN",
254
+ value: 1000000n,
255
+ recipients: [tokenAccount.freshAddress],
256
+ senders: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
257
+ asset: { type: "native" },
258
+ tx: {
259
+ hash: "0xd841b502ce333e5b9ac394db81e6597d9bbbe475e5b12966b98b843d91cc0a09",
260
+ block: { height: 0 },
261
+ fees: 99900n,
262
+ date: new Date("2025-05-29T18:32:11.515Z"),
263
+ },
264
+ });
265
+
266
+ expect(operationOUT).toMatchObject({
267
+ type: "OUT",
268
+ value: 951100n,
269
+ recipients: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
270
+ senders: [tokenAccount.freshAddress],
271
+ asset: { type: "native" },
272
+ tx: {
273
+ hash: "0xb1114396c5b6f2f5ba955fa8e4102d0e3983d5ccdb0717ff792f6e4848e72366",
274
+ block: { height: 0 },
275
+ fees: 1100n,
276
+ date: new Date("2025-05-29T18:39:57.864Z"),
277
+ },
278
+ });
279
+
280
+ const coinIN = operations.find(operation => operation.id === txINLegacyCoinHash);
281
+ const coinOUT = operations.find(operation => operation.id === txOUTLegacyCoinHash);
282
+
283
+ expect(coinIN).toMatchObject({
284
+ type: "IN",
285
+ value: 2000000n,
286
+ recipients: [tokenAccount.freshAddress],
287
+ senders: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
288
+ asset: {
289
+ type: "token",
290
+ asset_type: "coin",
291
+ address:
292
+ "0xd11107bdf0d6d7040c6c0bfbdecb6545191fdf13e8d8d259952f53e1713f61b5::staked_coin::StakedAptos",
293
+ },
294
+ tx: {
295
+ hash: "0x6869c933396d976af85b273a825fe264a910d0064fe28ce97285073b9e5306bb",
296
+ block: { height: 0 },
297
+ fees: 51100n,
298
+ date: new Date("2025-05-29T18:32:58.804Z"),
299
+ },
300
+ });
301
+
302
+ expect(coinOUT).toMatchObject({
303
+ type: "OUT",
304
+ value: 1000000n,
305
+ recipients: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
306
+ senders: [tokenAccount.freshAddress],
307
+ asset: {
308
+ type: "token",
309
+ asset_type: "coin",
310
+ address:
311
+ "0xd11107bdf0d6d7040c6c0bfbdecb6545191fdf13e8d8d259952f53e1713f61b5::staked_coin::StakedAptos",
312
+ },
313
+ tx: {
314
+ hash: "0xa720db7fe6327b1db23e778df5856ab1d8785d7e8edae98941a3f6e05fa58ddd",
315
+ block: { height: 0 },
316
+ fees: 1200n,
317
+ date: new Date("2025-05-29T18:38:08.283Z"),
318
+ },
319
+ });
320
+
321
+ const fungibleAssetIN = operations.find(operation => operation.id === txINFungibleAssetHash);
322
+ const fungibleAssetOUT = operations.find(
323
+ operation => operation.id === txOUTFungibleAssetHash,
324
+ );
325
+
326
+ expect(fungibleAssetIN).toMatchObject({
327
+ type: "IN",
328
+ value: 1750n,
329
+ recipients: [tokenAccount.freshAddress],
330
+ senders: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
331
+ asset: {
332
+ type: "token",
333
+ asset_type: "fungible_asset",
334
+ address: "0x2ebb2ccac5e027a87fa0e2e5f656a3a4238d6a48d93ec9b610d570fc0aa0df12",
335
+ },
336
+ tx: {
337
+ hash: "0x88856968603dee4f08579036bc30322b9a5f329561656888e3467ce27cc11ea7",
338
+ block: { height: 0 },
339
+ fees: 54300n,
340
+ date: new Date("2025-05-29T18:33:58.097Z"),
341
+ },
342
+ });
343
+
344
+ expect(fungibleAssetOUT).toMatchObject({
345
+ type: "OUT",
346
+ value: 1000n,
347
+ recipients: ["0x24dbf71ba20209753035505c51d4607ed67aa0c81b930d9ef4483ec84b349fcb"],
348
+ senders: [tokenAccount.freshAddress],
349
+ asset: {
350
+ type: "token",
351
+ asset_type: "fungible_asset",
352
+ address: "0x2ebb2ccac5e027a87fa0e2e5f656a3a4238d6a48d93ec9b610d570fc0aa0df12",
353
+ },
354
+ tx: {
355
+ hash: "0x8aa9e980760fe8aeb6804f387350b3019a2471aa61a5506a260c32cd5d6db32c",
356
+ block: { height: 0 },
357
+ fees: 1000n,
358
+ date: new Date("2025-05-29T18:38:56.897Z"),
359
+ },
360
+ });
361
+ });
362
+ });
139
363
  });
@@ -19,12 +19,14 @@ jest.mock("../../logic/normalizeTransactionOptions", () => ({
19
19
  })),
20
20
  }));
21
21
 
22
- jest.mock("../../constants", () => ({
23
- DEFAULT_GAS: 100,
24
- DEFAULT_GAS_PRICE: 200,
25
- APTOS_ASSET_ID: "0x1::aptos_coin::AptosCoin",
26
- SUPPORTED_TOKEN_TYPES: ["coin", "fungible_asset"],
27
- }));
22
+ jest.mock("../../constants", () => {
23
+ const originalModules = jest.requireActual("../../constants");
24
+ return {
25
+ ...originalModules,
26
+ DEFAULT_GAS: 100,
27
+ DEFAULT_GAS_PRICE: 200,
28
+ };
29
+ });
28
30
 
29
31
  jest.mock("../../network", () => {
30
32
  return {
@@ -654,7 +654,7 @@ describe("Aptos API", () => {
654
654
  },
655
655
  });
656
656
  expect(balances).toHaveLength(1);
657
- expect(balances[0].asset_type).toBe(assets[0].asset_type);
657
+ expect(balances[0].contractAddress).toBe(assets[0].asset_type);
658
658
  expect(balances[0].amount).toStrictEqual(BigNumber(assets[0].amount));
659
659
  });
660
660
  });
package/src/constants.ts CHANGED
@@ -45,6 +45,10 @@ export enum DIRECTION {
45
45
  UNKNOWN = "UNKNOWN",
46
46
  }
47
47
 
48
- export const SUPPORTED_TOKEN_TYPES = ["coin", "fungible_asset"];
48
+ export enum TOKEN_TYPE {
49
+ COIN = "coin",
50
+ FUNGIBLE_ASSET = "fungible_asset",
51
+ }
52
+
49
53
  export const DEFAULT_GAS = new BigNumber(200);
50
54
  export const DEFAULT_GAS_PRICE = new BigNumber(100);
@@ -1,62 +1,70 @@
1
- import { InputEntryFunctionData, RawTransaction } from "@aptos-labs/ts-sdk";
2
- import type { Account, TokenAccount } from "@ledgerhq/types-live";
3
- import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/index";
4
- import { APTOS_ASSET_ID, SUPPORTED_TOKEN_TYPES } from "../constants";
1
+ import type { InputEntryFunctionData, RawTransaction } from "@aptos-labs/ts-sdk";
2
+ import type { Account } from "@ledgerhq/types-live";
3
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
4
+ import { APTOS_ASSET_ID, TOKEN_TYPE } from "../constants";
5
5
  import type { AptosAPI } from "../network";
6
6
  import { normalizeTransactionOptions } from "./normalizeTransactionOptions";
7
7
  import type { Transaction } from "../types";
8
+ import type BigNumber from "bignumber.js";
8
9
 
9
10
  const buildTransaction = async (
10
11
  account: Account,
11
12
  transaction: Transaction,
12
13
  aptosClient: AptosAPI,
14
+ contractAddress?: string,
15
+ tokenType?: TOKEN_TYPE,
13
16
  ): Promise<RawTransaction> => {
14
17
  const subAccount = findSubAccountById(account, transaction.subAccountId ?? "");
15
18
 
16
- const txPayload = getPayload(subAccount, transaction);
19
+ const payloadContracAddress = subAccount ? subAccount.token.contractAddress : contractAddress;
20
+ const payloadTokenType = (subAccount?.token?.tokenType as TOKEN_TYPE) ?? tokenType;
21
+
22
+ const txPayload = getPayload({
23
+ amount: transaction.amount,
24
+ recipient: transaction.recipient,
25
+ contractAddress: payloadContracAddress,
26
+ tokenType: payloadTokenType,
27
+ });
28
+
17
29
  const txOptions = normalizeTransactionOptions(transaction.options);
30
+
18
31
  const tx = await aptosClient.generateTransaction(account.freshAddress, txPayload, txOptions);
19
32
 
20
33
  return tx;
21
34
  };
22
35
 
23
- const getPayload = (
24
- tokenAccount: TokenAccount | undefined,
25
- transaction: Transaction,
26
- ): InputEntryFunctionData => {
27
- if (tokenAccount && isTokenAccount(tokenAccount)) {
28
- const { tokenType } = tokenAccount.token;
29
-
30
- if (!SUPPORTED_TOKEN_TYPES.includes(tokenType)) {
31
- throw new Error(`Token type ${tokenType} not supported`);
32
- }
33
-
34
- if (tokenType === "fungible_asset") {
35
- return {
36
- function: `0x1::primary_fungible_store::transfer`,
37
- typeArguments: ["0x1::fungible_asset::Metadata"],
38
- functionArguments: [
39
- tokenAccount.token.contractAddress,
40
- transaction.recipient,
41
- transaction.amount.toString(),
42
- ],
43
- };
44
- }
45
-
46
- if (tokenType === "coin") {
47
- return {
48
- function: `0x1::aptos_account::transfer_coins`,
49
- typeArguments: [tokenAccount.token.contractAddress],
50
- functionArguments: [transaction.recipient, transaction.amount.toString()],
51
- };
52
- }
36
+ const getPayload = (args: {
37
+ amount: BigNumber;
38
+ recipient: string;
39
+ contractAddress?: string | undefined;
40
+ tokenType?: string;
41
+ }): InputEntryFunctionData => {
42
+ if (args.tokenType !== undefined && !isTokenType(args.tokenType)) {
43
+ throw new Error(`Token type ${args.tokenType} not supported`);
44
+ }
45
+
46
+ if (args.tokenType === TOKEN_TYPE.FUNGIBLE_ASSET) {
47
+ return {
48
+ function: "0x1::primary_fungible_store::transfer",
49
+ typeArguments: ["0x1::fungible_asset::Metadata"],
50
+ functionArguments: [args.contractAddress, args.recipient, args.amount.toString()],
51
+ };
52
+ }
53
+
54
+ let address = args.contractAddress ?? "";
55
+ if (address === "") {
56
+ address = APTOS_ASSET_ID;
53
57
  }
54
58
 
55
59
  return {
56
60
  function: "0x1::aptos_account::transfer_coins",
57
- typeArguments: [APTOS_ASSET_ID],
58
- functionArguments: [transaction.recipient, transaction.amount.toString()],
61
+ typeArguments: [address],
62
+ functionArguments: [args.recipient, args.amount.toString()],
59
63
  };
60
64
  };
61
65
 
66
+ export const isTokenType = (value: string): boolean => {
67
+ return Object.values(TOKEN_TYPE).includes(value as TOKEN_TYPE);
68
+ };
69
+
62
70
  export default buildTransaction;