@ledgerhq/coin-aptos 2.0.0-nightly.5 → 2.0.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 (272) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.unimportedrc.json +18 -6
  3. package/CHANGELOG.md +152 -34
  4. package/jest.config.js +2 -1
  5. package/jest.integ.config.js +8 -0
  6. package/lib/__tests__/api/index.integ.test.d.ts +2 -0
  7. package/lib/__tests__/api/index.integ.test.d.ts.map +1 -0
  8. package/lib/__tests__/api/index.integ.test.js +52 -0
  9. package/lib/__tests__/api/index.integ.test.js.map +1 -0
  10. package/lib/__tests__/api/index.test.js +48 -404
  11. package/lib/__tests__/api/index.test.js.map +1 -1
  12. package/lib/__tests__/bridge/broadcast.test.js +5 -5
  13. package/lib/__tests__/bridge/broadcast.test.js.map +1 -1
  14. package/lib/__tests__/bridge/buildTransaction.test.js +60 -5
  15. package/lib/__tests__/bridge/buildTransaction.test.js.map +1 -1
  16. package/lib/__tests__/bridge/createTransaction.test.js +1 -1
  17. package/lib/__tests__/bridge/createTransaction.test.js.map +1 -1
  18. package/lib/__tests__/bridge/getFeesForTransaction.test.js +115 -14
  19. package/lib/__tests__/bridge/getFeesForTransaction.test.js.map +1 -1
  20. package/lib/__tests__/bridge/getTransactionStatus.test.js +175 -37
  21. package/lib/__tests__/bridge/getTransactionStatus.test.js.map +1 -1
  22. package/lib/__tests__/bridge/logic.test.js +889 -115
  23. package/lib/__tests__/bridge/logic.test.js.map +1 -1
  24. package/lib/__tests__/bridge/prepareTransaction.test.js +3 -3
  25. package/lib/__tests__/bridge/prepareTransaction.test.js.map +1 -1
  26. package/lib/__tests__/bridge/signOperation.test.js +131 -4
  27. package/lib/__tests__/bridge/signOperation.test.js.map +1 -1
  28. package/lib/__tests__/bridge/synchronisation.test.js +1215 -68
  29. package/lib/__tests__/bridge/synchronisation.test.js.map +1 -1
  30. package/lib/__tests__/network/client.test.d.ts +2 -0
  31. package/lib/__tests__/network/client.test.d.ts.map +1 -0
  32. package/lib/__tests__/network/client.test.js +520 -0
  33. package/lib/__tests__/network/client.test.js.map +1 -0
  34. package/lib/api/index.d.ts +4 -30
  35. package/lib/api/index.d.ts.map +1 -1
  36. package/lib/api/index.js +24 -170
  37. package/lib/api/index.js.map +1 -1
  38. package/lib/bridge/bridge.fixture.d.ts +2 -0
  39. package/lib/bridge/bridge.fixture.d.ts.map +1 -1
  40. package/lib/bridge/bridge.fixture.js +85 -13
  41. package/lib/bridge/bridge.fixture.js.map +1 -1
  42. package/lib/bridge/broadcast.d.ts +3 -2
  43. package/lib/bridge/broadcast.d.ts.map +1 -1
  44. package/lib/bridge/broadcast.js +4 -3
  45. package/lib/bridge/broadcast.js.map +1 -1
  46. package/lib/bridge/buildTransaction.d.ts +1 -1
  47. package/lib/bridge/buildTransaction.d.ts.map +1 -1
  48. package/lib/bridge/buildTransaction.js +29 -3
  49. package/lib/bridge/buildTransaction.js.map +1 -1
  50. package/lib/bridge/createTransaction.js +3 -3
  51. package/lib/bridge/createTransaction.js.map +1 -1
  52. package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
  53. package/lib/bridge/estimateMaxSpendable.js +6 -5
  54. package/lib/bridge/estimateMaxSpendable.js.map +1 -1
  55. package/lib/bridge/getFeesForTransaction.d.ts +1 -1
  56. package/lib/bridge/getFeesForTransaction.d.ts.map +1 -1
  57. package/lib/bridge/getFeesForTransaction.js +19 -13
  58. package/lib/bridge/getFeesForTransaction.js.map +1 -1
  59. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  60. package/lib/bridge/getTransactionStatus.js +28 -14
  61. package/lib/bridge/getTransactionStatus.js.map +1 -1
  62. package/lib/bridge/index.d.ts +1 -1
  63. package/lib/bridge/index.d.ts.map +1 -1
  64. package/lib/bridge/logic.d.ts +13 -11
  65. package/lib/bridge/logic.d.ts.map +1 -1
  66. package/lib/bridge/logic.js +152 -57
  67. package/lib/bridge/logic.js.map +1 -1
  68. package/lib/bridge/prepareTransaction.d.ts.map +1 -1
  69. package/lib/bridge/prepareTransaction.js +5 -4
  70. package/lib/bridge/prepareTransaction.js.map +1 -1
  71. package/lib/bridge/signOperation.d.ts.map +1 -1
  72. package/lib/bridge/signOperation.js +20 -6
  73. package/lib/bridge/signOperation.js.map +1 -1
  74. package/lib/bridge/synchronisation.d.ts +15 -0
  75. package/lib/bridge/synchronisation.d.ts.map +1 -1
  76. package/lib/bridge/synchronisation.js +129 -6
  77. package/lib/bridge/synchronisation.js.map +1 -1
  78. package/lib/config.d.ts +13 -0
  79. package/lib/config.d.ts.map +1 -0
  80. package/lib/config.js +9 -0
  81. package/lib/config.js.map +1 -0
  82. package/lib/constants.d.ts +9 -1
  83. package/lib/constants.d.ts.map +1 -1
  84. package/lib/constants.js +13 -2
  85. package/lib/constants.js.map +1 -1
  86. package/lib/network/client.d.ts +36 -0
  87. package/lib/network/client.d.ts.map +1 -0
  88. package/lib/network/client.js +247 -0
  89. package/lib/network/client.js.map +1 -0
  90. package/lib/network/graphql/queries.d.ts.map +1 -0
  91. package/lib/{api → network}/graphql/queries.js +6 -6
  92. package/lib/network/graphql/queries.js.map +1 -0
  93. package/lib/{api → network}/graphql/types.d.ts +9 -9
  94. package/lib/network/graphql/types.d.ts.map +1 -0
  95. package/lib/{api → network}/graphql/types.js.map +1 -1
  96. package/lib/network/index.d.ts +2 -1
  97. package/lib/network/index.d.ts.map +1 -1
  98. package/lib/network/index.js +15 -0
  99. package/lib/network/index.js.map +1 -1
  100. package/lib/test/bot-specs.d.ts.map +1 -1
  101. package/lib/test/bot-specs.js +40 -1
  102. package/lib/test/bot-specs.js.map +1 -1
  103. package/lib/test/bridgeDatasetTest.d.ts.map +1 -1
  104. package/lib/test/bridgeDatasetTest.js +44 -62
  105. package/lib/test/bridgeDatasetTest.js.map +1 -1
  106. package/lib/test/speculos-deviceActions.d.ts +1 -0
  107. package/lib/test/speculos-deviceActions.d.ts.map +1 -1
  108. package/lib/test/speculos-deviceActions.js +37 -5
  109. package/lib/test/speculos-deviceActions.js.map +1 -1
  110. package/lib/types/assets.d.ts +12 -0
  111. package/lib/types/assets.d.ts.map +1 -0
  112. package/lib/types/assets.js +3 -0
  113. package/lib/types/assets.js.map +1 -0
  114. package/lib/types/index.d.ts +18 -7
  115. package/lib/types/index.d.ts.map +1 -1
  116. package/lib-es/__tests__/api/index.integ.test.d.ts +2 -0
  117. package/lib-es/__tests__/api/index.integ.test.d.ts.map +1 -0
  118. package/lib-es/__tests__/api/index.integ.test.js +50 -0
  119. package/lib-es/__tests__/api/index.integ.test.js.map +1 -0
  120. package/lib-es/__tests__/api/index.test.js +52 -408
  121. package/lib-es/__tests__/api/index.test.js.map +1 -1
  122. package/lib-es/__tests__/bridge/broadcast.test.js +2 -2
  123. package/lib-es/__tests__/bridge/broadcast.test.js.map +1 -1
  124. package/lib-es/__tests__/bridge/buildTransaction.test.js +60 -5
  125. package/lib-es/__tests__/bridge/buildTransaction.test.js.map +1 -1
  126. package/lib-es/__tests__/bridge/createTransaction.test.js +1 -1
  127. package/lib-es/__tests__/bridge/createTransaction.test.js.map +1 -1
  128. package/lib-es/__tests__/bridge/getFeesForTransaction.test.js +112 -11
  129. package/lib-es/__tests__/bridge/getFeesForTransaction.test.js.map +1 -1
  130. package/lib-es/__tests__/bridge/getTransactionStatus.test.js +177 -39
  131. package/lib-es/__tests__/bridge/getTransactionStatus.test.js.map +1 -1
  132. package/lib-es/__tests__/bridge/logic.test.js +891 -117
  133. package/lib-es/__tests__/bridge/logic.test.js.map +1 -1
  134. package/lib-es/__tests__/bridge/prepareTransaction.test.js +2 -2
  135. package/lib-es/__tests__/bridge/prepareTransaction.test.js.map +1 -1
  136. package/lib-es/__tests__/bridge/signOperation.test.js +131 -4
  137. package/lib-es/__tests__/bridge/signOperation.test.js.map +1 -1
  138. package/lib-es/__tests__/bridge/synchronisation.test.js +1213 -69
  139. package/lib-es/__tests__/bridge/synchronisation.test.js.map +1 -1
  140. package/lib-es/__tests__/network/client.test.d.ts +2 -0
  141. package/lib-es/__tests__/network/client.test.d.ts.map +1 -0
  142. package/lib-es/__tests__/network/client.test.js +515 -0
  143. package/lib-es/__tests__/network/client.test.js.map +1 -0
  144. package/lib-es/api/index.d.ts +4 -30
  145. package/lib-es/api/index.d.ts.map +1 -1
  146. package/lib-es/api/index.js +22 -168
  147. package/lib-es/api/index.js.map +1 -1
  148. package/lib-es/bridge/bridge.fixture.d.ts +2 -0
  149. package/lib-es/bridge/bridge.fixture.d.ts.map +1 -1
  150. package/lib-es/bridge/bridge.fixture.js +82 -12
  151. package/lib-es/bridge/bridge.fixture.js.map +1 -1
  152. package/lib-es/bridge/broadcast.d.ts +3 -2
  153. package/lib-es/bridge/broadcast.d.ts.map +1 -1
  154. package/lib-es/bridge/broadcast.js +4 -3
  155. package/lib-es/bridge/broadcast.js.map +1 -1
  156. package/lib-es/bridge/buildTransaction.d.ts +1 -1
  157. package/lib-es/bridge/buildTransaction.d.ts.map +1 -1
  158. package/lib-es/bridge/buildTransaction.js +30 -4
  159. package/lib-es/bridge/buildTransaction.js.map +1 -1
  160. package/lib-es/bridge/createTransaction.js +1 -1
  161. package/lib-es/bridge/createTransaction.js.map +1 -1
  162. package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
  163. package/lib-es/bridge/estimateMaxSpendable.js +4 -3
  164. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
  165. package/lib-es/bridge/getFeesForTransaction.d.ts +1 -1
  166. package/lib-es/bridge/getFeesForTransaction.d.ts.map +1 -1
  167. package/lib-es/bridge/getFeesForTransaction.js +14 -8
  168. package/lib-es/bridge/getFeesForTransaction.js.map +1 -1
  169. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  170. package/lib-es/bridge/getTransactionStatus.js +28 -14
  171. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  172. package/lib-es/bridge/index.d.ts +1 -1
  173. package/lib-es/bridge/index.d.ts.map +1 -1
  174. package/lib-es/bridge/logic.d.ts +13 -11
  175. package/lib-es/bridge/logic.d.ts.map +1 -1
  176. package/lib-es/bridge/logic.js +146 -55
  177. package/lib-es/bridge/logic.js.map +1 -1
  178. package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
  179. package/lib-es/bridge/prepareTransaction.js +5 -4
  180. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  181. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  182. package/lib-es/bridge/signOperation.js +18 -4
  183. package/lib-es/bridge/signOperation.js.map +1 -1
  184. package/lib-es/bridge/synchronisation.d.ts +15 -0
  185. package/lib-es/bridge/synchronisation.d.ts.map +1 -1
  186. package/lib-es/bridge/synchronisation.js +124 -4
  187. package/lib-es/bridge/synchronisation.js.map +1 -1
  188. package/lib-es/config.d.ts +13 -0
  189. package/lib-es/config.d.ts.map +1 -0
  190. package/lib-es/config.js +4 -0
  191. package/lib-es/config.js.map +1 -0
  192. package/lib-es/constants.d.ts +9 -1
  193. package/lib-es/constants.d.ts.map +1 -1
  194. package/lib-es/constants.js +9 -1
  195. package/lib-es/constants.js.map +1 -1
  196. package/lib-es/network/client.d.ts +36 -0
  197. package/lib-es/network/client.d.ts.map +1 -0
  198. package/lib-es/network/client.js +240 -0
  199. package/lib-es/network/client.js.map +1 -0
  200. package/lib-es/network/graphql/queries.d.ts.map +1 -0
  201. package/lib-es/{api → network}/graphql/queries.js +6 -6
  202. package/lib-es/network/graphql/queries.js.map +1 -0
  203. package/lib-es/{api → network}/graphql/types.d.ts +9 -9
  204. package/lib-es/network/graphql/types.d.ts.map +1 -0
  205. package/lib-es/{api → network}/graphql/types.js.map +1 -1
  206. package/lib-es/network/index.d.ts +2 -1
  207. package/lib-es/network/index.d.ts.map +1 -1
  208. package/lib-es/network/index.js +1 -0
  209. package/lib-es/network/index.js.map +1 -1
  210. package/lib-es/test/bot-specs.d.ts.map +1 -1
  211. package/lib-es/test/bot-specs.js +41 -2
  212. package/lib-es/test/bot-specs.js.map +1 -1
  213. package/lib-es/test/bridgeDatasetTest.d.ts.map +1 -1
  214. package/lib-es/test/bridgeDatasetTest.js +44 -59
  215. package/lib-es/test/bridgeDatasetTest.js.map +1 -1
  216. package/lib-es/test/speculos-deviceActions.d.ts +1 -0
  217. package/lib-es/test/speculos-deviceActions.d.ts.map +1 -1
  218. package/lib-es/test/speculos-deviceActions.js +36 -4
  219. package/lib-es/test/speculos-deviceActions.js.map +1 -1
  220. package/lib-es/types/assets.d.ts +12 -0
  221. package/lib-es/types/assets.d.ts.map +1 -0
  222. package/lib-es/types/assets.js +2 -0
  223. package/lib-es/types/assets.js.map +1 -0
  224. package/lib-es/types/index.d.ts +18 -7
  225. package/lib-es/types/index.d.ts.map +1 -1
  226. package/package.json +12 -9
  227. package/src/__tests__/api/index.integ.test.ts +58 -0
  228. package/src/__tests__/api/index.test.ts +60 -477
  229. package/src/__tests__/bridge/broadcast.test.ts +2 -2
  230. package/src/__tests__/bridge/buildTransaction.test.ts +87 -5
  231. package/src/__tests__/bridge/createTransaction.test.ts +1 -1
  232. package/src/__tests__/bridge/getFeesForTransaction.test.ts +146 -11
  233. package/src/__tests__/bridge/getTransactionStatus.test.ts +217 -38
  234. package/src/__tests__/bridge/logic.test.ts +922 -118
  235. package/src/__tests__/bridge/prepareTransaction.test.ts +2 -2
  236. package/src/__tests__/bridge/signOperation.test.ts +147 -5
  237. package/src/__tests__/bridge/synchronisation.test.ts +1265 -71
  238. package/src/__tests__/network/client.test.ts +618 -0
  239. package/src/api/index.ts +31 -223
  240. package/src/bridge/bridge.fixture.ts +91 -12
  241. package/src/bridge/broadcast.ts +7 -7
  242. package/src/bridge/buildTransaction.ts +40 -7
  243. package/src/bridge/createTransaction.ts +1 -1
  244. package/src/bridge/estimateMaxSpendable.ts +4 -3
  245. package/src/bridge/getFeesForTransaction.ts +16 -10
  246. package/src/bridge/getTransactionStatus.ts +35 -13
  247. package/src/bridge/index.ts +1 -1
  248. package/src/bridge/logic.ts +202 -67
  249. package/src/bridge/prepareTransaction.ts +7 -4
  250. package/src/bridge/signOperation.ts +20 -4
  251. package/src/bridge/synchronisation.ts +171 -4
  252. package/src/config.ts +19 -0
  253. package/src/constants.ts +17 -1
  254. package/src/network/client.ts +315 -0
  255. package/src/{api → network}/graphql/queries.ts +6 -6
  256. package/src/{api → network}/graphql/types.ts +9 -9
  257. package/src/network/index.ts +2 -1
  258. package/src/test/bot-specs.ts +63 -3
  259. package/src/test/bridgeDatasetTest.ts +46 -59
  260. package/src/test/speculos-deviceActions.ts +40 -4
  261. package/src/types/assets.ts +15 -0
  262. package/src/types/index.ts +15 -1
  263. package/lib/api/graphql/queries.d.ts.map +0 -1
  264. package/lib/api/graphql/queries.js.map +0 -1
  265. package/lib/api/graphql/types.d.ts.map +0 -1
  266. package/lib-es/api/graphql/queries.d.ts.map +0 -1
  267. package/lib-es/api/graphql/queries.js.map +0 -1
  268. package/lib-es/api/graphql/types.d.ts.map +0 -1
  269. /package/lib/{api → network}/graphql/queries.d.ts +0 -0
  270. /package/lib/{api → network}/graphql/types.js +0 -0
  271. /package/lib-es/{api → network}/graphql/queries.d.ts +0 -0
  272. /package/lib-es/{api → network}/graphql/types.js +0 -0
@@ -4,24 +4,37 @@ import {
4
4
  InputEntryFunctionData,
5
5
  MoveResource,
6
6
  WriteSetChange,
7
+ WriteSetChangeWriteResource,
7
8
  } from "@aptos-labs/ts-sdk";
8
9
  import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
9
- import type { Operation, OperationType } from "@ledgerhq/types-live";
10
+ import type { Account, Operation, OperationType, TokenAccount } from "@ledgerhq/types-live";
11
+ import {
12
+ decodeTokenAccountId,
13
+ encodeTokenAccountId,
14
+ findSubAccountById,
15
+ isTokenAccount,
16
+ } from "@ledgerhq/coin-framework/account/index";
10
17
  import BigNumber from "bignumber.js";
11
18
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
12
19
  import {
13
- APTOS_COIN_CHANGE,
20
+ APTOS_ASSET_ID,
21
+ APTOS_FUNGIBLE_STORE,
14
22
  BATCH_TRANSFER_TYPES,
15
23
  DELEGATION_POOL_TYPES,
16
24
  DIRECTION,
17
- TRANSFER_TYPES,
18
- WRITE_RESOURCE,
25
+ COIN_TRANSFER_TYPES,
26
+ FA_TRANSFER_TYPES,
27
+ APTOS_OBJECT_CORE,
19
28
  } from "../constants";
20
- import type { AptosMoveResource, AptosTransaction, TransactionOptions } from "../types";
21
-
22
- export const DEFAULT_GAS = new BigNumber(200);
23
- export const DEFAULT_GAS_PRICE = new BigNumber(100);
24
- export const ESTIMATE_GAS_MUL = new BigNumber(1.0); // define buffer for gas estimation change here, if needed
29
+ import type {
30
+ AptosFungibleoObjectCoreResourceData,
31
+ AptosFungibleStoreResourceData,
32
+ AptosMoveResource,
33
+ AptosTransaction,
34
+ Transaction,
35
+ TransactionOptions,
36
+ } from "../types";
37
+ import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets";
25
38
 
26
39
  const CLEAN_HEX_REGEXP = /^0x0*|^0+/;
27
40
 
@@ -30,13 +43,21 @@ export function isTestnet(currencyId: string): boolean {
30
43
  }
31
44
 
32
45
  export const getMaxSendBalance = (
33
- amount: BigNumber,
34
46
  gas: BigNumber,
35
47
  gasPrice: BigNumber,
48
+ account: Account,
49
+ transaction?: Transaction,
36
50
  ): BigNumber => {
51
+ const tokenAccount = findSubAccountById(account, transaction?.subAccountId ?? "");
52
+ const fromTokenAccount = tokenAccount && isTokenAccount(tokenAccount);
53
+
37
54
  const totalGas = gas.multipliedBy(gasPrice);
38
55
 
39
- return amount.gt(totalGas) ? amount.minus(totalGas) : new BigNumber(0);
56
+ return fromTokenAccount
57
+ ? tokenAccount.spendableBalance
58
+ : account.spendableBalance.gt(totalGas)
59
+ ? account.spendableBalance.minus(totalGas)
60
+ : new BigNumber(0);
40
61
  };
41
62
 
42
63
  export function normalizeTransactionOptions(options: TransactionOptions): TransactionOptions {
@@ -88,9 +109,10 @@ export const txsToOps = (
88
109
  info: { address: string },
89
110
  id: string,
90
111
  txs: (AptosTransaction | null)[],
91
- ): Operation[] => {
112
+ ): [Operation[], Operation[]] => {
92
113
  const { address } = info;
93
114
  const ops: Operation[] = [];
115
+ const opsTokens: Operation[] = [];
94
116
 
95
117
  txs.forEach(tx => {
96
118
  if (tx !== null) {
@@ -107,10 +129,12 @@ export const txsToOps = (
107
129
  return; // skip transaction without functions in payload
108
130
  }
109
131
 
110
- const { amount_in, amount_out } = getAptosAmounts(tx, address);
111
- op.value = calculateAmount(tx.sender, address, op.fee, amount_in, amount_out);
132
+ const { coin_id, amount_in, amount_out } = getCoinAndAmounts(tx, address);
133
+ op.value = calculateAmount(tx.sender, address, amount_in, amount_out);
112
134
  op.type = compareAddress(tx.sender, address) ? DIRECTION.OUT : DIRECTION.IN;
113
135
  op.senders.push(tx.sender);
136
+ op.hasFailed = !tx.success;
137
+ op.id = encodeOperationId(op.accountId, tx.hash, op.type);
114
138
 
115
139
  processRecipients(payload, address, op, function_address);
116
140
 
@@ -119,13 +143,30 @@ export const txsToOps = (
119
143
  op.type = DIRECTION.UNKNOWN;
120
144
  }
121
145
 
122
- op.hasFailed = !tx.success;
123
- op.id = encodeOperationId(id, tx.hash, op.type);
124
- if (op.type !== DIRECTION.UNKNOWN) ops.push(op);
146
+ if (op.type !== DIRECTION.UNKNOWN && coin_id !== null) {
147
+ if (coin_id === APTOS_ASSET_ID) {
148
+ ops.push(op);
149
+ } else {
150
+ const token = findTokenByAddressInCurrency(coin_id.toLowerCase(), "aptos");
151
+ if (token !== undefined) {
152
+ op.accountId = encodeTokenAccountId(id, token);
153
+ opsTokens.push(op);
154
+
155
+ if (op.type === DIRECTION.OUT) {
156
+ ops.push({
157
+ ...op,
158
+ accountId: decodeTokenAccountId(op.accountId).accountId,
159
+ value: op.fee,
160
+ type: "FEES",
161
+ });
162
+ }
163
+ }
164
+ }
165
+ }
125
166
  }
126
167
  });
127
168
 
128
- return ops;
169
+ return [ops, opsTokens];
129
170
  };
130
171
 
131
172
  export function compareAddress(addressA: string, addressB: string) {
@@ -151,7 +192,7 @@ export function processRecipients(
151
192
  ): void {
152
193
  // get recipients buy 3 groups
153
194
  if (
154
- (TRANSFER_TYPES.includes(payload.function) ||
195
+ (COIN_TRANSFER_TYPES.includes(payload.function) ||
155
196
  DELEGATION_POOL_TYPES.includes(payload.function)) &&
156
197
  payload.functionArguments &&
157
198
  payload.functionArguments.length > 0 &&
@@ -159,6 +200,15 @@ export function processRecipients(
159
200
  ) {
160
201
  // 1. Transfer like functions (includes some delegation pool functions)
161
202
  op.recipients.push(payload.functionArguments[0].toString());
203
+ } else if (
204
+ FA_TRANSFER_TYPES.includes(payload.function) &&
205
+ payload.functionArguments &&
206
+ payload.functionArguments.length > 1 &&
207
+ typeof payload.functionArguments[0] === "object" &&
208
+ typeof payload.functionArguments[1] === "string"
209
+ ) {
210
+ // 1. Transfer like functions (includes some delegation pool functions)
211
+ op.recipients.push(payload.functionArguments[1].toString());
162
212
  } else if (
163
213
  BATCH_TRANSFER_TYPES.includes(payload.function) &&
164
214
  payload.functionArguments &&
@@ -179,87 +229,172 @@ export function processRecipients(
179
229
  }
180
230
  }
181
231
 
182
- function checkWriteSets(tx: AptosTransaction, event: Event, event_name: string): boolean {
183
- return tx.changes.some(change => {
184
- return isChangeOfAptos(change, event, event_name);
185
- });
186
- }
187
-
188
- export function isChangeOfAptos(
189
- writeSetChange: WriteSetChange,
232
+ export function getEventCoinAddress(
233
+ change: WriteSetChangeWriteResource,
190
234
  event: Event,
191
235
  event_name: string,
192
- ): boolean {
193
- // to validate the event is related to Aptos Tokens we need to find change of type "write_resource"
194
- // with the same guid as event
195
- if (writeSetChange.type !== WRITE_RESOURCE) {
196
- return false;
236
+ ): string | null {
237
+ const change_data = change.data;
238
+
239
+ const mr = change_data as MoveResource<AptosMoveResource>; // -> this is data that we want to parse
240
+
241
+ if (!(event_name in mr.data)) {
242
+ return null;
197
243
  }
198
244
 
199
- if (!("data" in writeSetChange)) {
200
- return false;
245
+ const change_event_data = mr.data[event_name];
246
+ if (
247
+ change_event_data.guid.id.addr !== event.guid.account_address ||
248
+ change_event_data.guid.id.creation_num !== event.guid.creation_number
249
+ ) {
250
+ return null;
201
251
  }
202
252
 
203
- const change_data = writeSetChange.data;
253
+ const address = extractAddress(mr.type);
204
254
 
205
- if (!("type" in change_data)) {
206
- return false;
255
+ return address;
256
+ }
257
+
258
+ export function getEventFAAddress(
259
+ change: WriteSetChangeWriteResource,
260
+ event: Event,
261
+ _event_name: string,
262
+ ): string | null {
263
+ const change_data = change.data;
264
+
265
+ if (change_data.type !== APTOS_FUNGIBLE_STORE) {
266
+ return null;
207
267
  }
208
268
 
209
- const mr = change_data as MoveResource<AptosMoveResource>;
269
+ const mr = change_data as MoveResource<AptosFungibleStoreResourceData>;
210
270
 
211
- if (mr.type !== APTOS_COIN_CHANGE) {
212
- return false;
271
+ if (change.address !== event.data.store) {
272
+ return null;
213
273
  }
214
274
 
215
- const change_event_data = mr.data[event_name];
275
+ return mr.data.metadata.inner;
276
+ }
216
277
 
217
- return (
218
- change_event_data.guid.id.addr === event.guid.account_address &&
219
- change_event_data.guid.id.creation_num === event.guid.creation_number
220
- );
278
+ export function getResourceAddress(
279
+ tx: AptosTransaction,
280
+ event: Event,
281
+ event_name: string,
282
+ getAddressProcessor: (
283
+ change: WriteSetChangeWriteResource,
284
+ event: Event,
285
+ event_name: string,
286
+ ) => string | null,
287
+ ): string | null {
288
+ for (const change of tx.changes) {
289
+ if (isWriteSetChangeWriteResource(change)) {
290
+ const address = getAddressProcessor(change, event, event_name);
291
+ if (address !== null) {
292
+ return address;
293
+ }
294
+ }
295
+ }
296
+ return null;
297
+ }
298
+
299
+ function isWriteSetChangeWriteResource(
300
+ change: WriteSetChange,
301
+ ): change is WriteSetChangeWriteResource {
302
+ return (change as WriteSetChangeWriteResource).data !== undefined;
303
+ }
304
+
305
+ export function checkFAOwner(tx: AptosTransaction, event: Event, user_address: string): boolean {
306
+ for (const change of tx.changes) {
307
+ if (isWriteSetChangeWriteResource(change)) {
308
+ const storeData = change.data as MoveResource<AptosFungibleoObjectCoreResourceData>;
309
+ if (
310
+ change.address == event.data.store &&
311
+ storeData.type == APTOS_OBJECT_CORE &&
312
+ storeData.data.owner == user_address
313
+ ) {
314
+ return true;
315
+ }
316
+ }
317
+ }
318
+ return false;
221
319
  }
222
320
 
223
- export function getAptosAmounts(
321
+ export function getCoinAndAmounts(
224
322
  tx: AptosTransaction,
225
323
  address: string,
226
- ): { amount_in: BigNumber; amount_out: BigNumber } {
227
- let amount_in = new BigNumber(0);
228
- let amount_out = new BigNumber(0);
324
+ ): { coin_id: string | null; amount_in: BigNumber; amount_out: BigNumber } {
325
+ let coin_id: string | null = null;
326
+ let amount_in = BigNumber(0);
327
+ let amount_out = BigNumber(0);
328
+
229
329
  // collect all events related to the address and calculate the overall amounts
230
330
  tx.events.forEach(event => {
231
- if (compareAddress(event.guid.account_address, address)) {
232
- switch (event.type) {
233
- case "0x1::coin::WithdrawEvent":
234
- if (checkWriteSets(tx, event, "withdraw_events")) {
235
- amount_out = amount_out.plus(event.data.amount);
236
- }
237
- break;
238
- case "0x1::coin::DepositEvent":
239
- if (checkWriteSets(tx, event, "deposit_events")) {
240
- amount_in = amount_in.plus(event.data.amount);
331
+ switch (event.type) {
332
+ case "0x1::coin::WithdrawEvent":
333
+ if (compareAddress(event.guid.account_address, address)) {
334
+ coin_id = getResourceAddress(tx, event, "withdraw_events", getEventCoinAddress);
335
+ amount_out = amount_out.plus(event.data.amount);
336
+ }
337
+ break;
338
+ case "0x1::coin::DepositEvent":
339
+ if (compareAddress(event.guid.account_address, address)) {
340
+ coin_id = getResourceAddress(tx, event, "deposit_events", getEventCoinAddress);
341
+ amount_in = amount_in.plus(event.data.amount);
342
+ }
343
+ break;
344
+ case "0x1::fungible_asset::Withdraw":
345
+ if (checkFAOwner(tx, event, address)) {
346
+ coin_id = getResourceAddress(tx, event, "withdraw_events", getEventFAAddress);
347
+ amount_out = amount_out.plus(event.data.amount);
348
+ }
349
+ break;
350
+ case "0x1::fungible_asset::Deposit":
351
+ if (checkFAOwner(tx, event, address)) {
352
+ coin_id = getResourceAddress(tx, event, "deposit_events", getEventFAAddress);
353
+ amount_in = amount_in.plus(event.data.amount);
354
+ }
355
+ break;
356
+ case "0x1::transaction_fee::FeeStatement":
357
+ if (tx.sender === address) {
358
+ if (coin_id === null) coin_id = APTOS_ASSET_ID;
359
+ if (coin_id === APTOS_ASSET_ID) {
360
+ const fees = BigNumber(tx.gas_unit_price).times(BigNumber(tx.gas_used));
361
+ amount_out = amount_out.plus(fees);
241
362
  }
242
- break;
243
- }
363
+ }
364
+ break;
244
365
  }
245
366
  });
246
- return { amount_in, amount_out };
367
+ return { coin_id, amount_in, amount_out }; // TODO: manage situation when there are several coinID from the events parsing
247
368
  }
248
369
 
249
370
  export function calculateAmount(
250
371
  sender: string,
251
372
  address: string,
252
- fee: BigNumber,
253
373
  amount_in: BigNumber,
254
374
  amount_out: BigNumber,
255
375
  ): BigNumber {
256
376
  const is_sender: boolean = compareAddress(sender, address);
257
- // Include fees if our address is the sender
258
- if (is_sender) {
259
- amount_out = amount_out.plus(fee);
260
- }
261
377
  // LL negates the amount for SEND transactions
262
378
  // to show positive amount on the send transaction (ex: in "cancel" tx, when amount will be returned to our account)
263
379
  // we need to make it negative
264
380
  return is_sender ? amount_out.minus(amount_in) : amount_in.minus(amount_out);
265
381
  }
382
+
383
+ /**
384
+ * Extracts the address from a string like "0x1::coin::CoinStore<address::module::type>"
385
+ * @param {string} str - The input string containing the address.
386
+ * @returns {string | null} - The extracted address or null if not found.
387
+ */
388
+ function extractAddress(str: string): string | null {
389
+ const match = str.match(/<([^<>]+)>{1}$/);
390
+ return match ? match[1] : null;
391
+ }
392
+
393
+ export function getTokenAccount(
394
+ account: Account,
395
+ transaction: Transaction,
396
+ ): TokenAccount | undefined {
397
+ const tokenAccount = findSubAccountById(account, transaction.subAccountId ?? "");
398
+ const fromTokenAccount = tokenAccount && isTokenAccount(tokenAccount);
399
+ return fromTokenAccount ? tokenAccount : undefined;
400
+ }
@@ -1,10 +1,11 @@
1
1
  import type { Account } from "@ledgerhq/types-live";
2
2
  import BigNumber from "bignumber.js";
3
3
 
4
- import { AptosAPI } from "../api";
4
+ import { AptosAPI } from "../network";
5
5
  import { getEstimatedGas } from "./getFeesForTransaction";
6
6
  import type { Transaction } from "../types";
7
- import { DEFAULT_GAS, DEFAULT_GAS_PRICE, getMaxSendBalance } from "./logic";
7
+ import { getMaxSendBalance } from "./logic";
8
+ import { DEFAULT_GAS, DEFAULT_GAS_PRICE } from "./../constants";
8
9
 
9
10
  const prepareTransaction = async (
10
11
  account: Account,
@@ -28,9 +29,10 @@ const prepareTransaction = async (
28
29
  if (transaction.useAllAmount) {
29
30
  // we will use this amount in simulation, to estimate gas
30
31
  transaction.amount = getMaxSendBalance(
31
- account.spendableBalance,
32
32
  new BigNumber(DEFAULT_GAS),
33
33
  new BigNumber(DEFAULT_GAS_PRICE),
34
+ account,
35
+ transaction,
34
36
  );
35
37
  }
36
38
 
@@ -39,9 +41,10 @@ const prepareTransaction = async (
39
41
  if (transaction.useAllAmount) {
40
42
  // correct the transaction amount according to estimated fees
41
43
  transaction.amount = getMaxSendBalance(
42
- account.spendableBalance,
43
44
  BigNumber(estimate.maxGasAmount),
44
45
  BigNumber(estimate.gasUnitPrice),
46
+ account,
47
+ transaction,
45
48
  );
46
49
  }
47
50
 
@@ -4,11 +4,12 @@ import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
4
4
  import buildTransaction from "./buildTransaction";
5
5
  import BigNumber from "bignumber.js";
6
6
  import type { Account, AccountBridge, Operation, OperationType } from "@ledgerhq/types-live";
7
- import { AptosAPI } from "../api";
7
+ import { AptosAPI } from "../network";
8
8
 
9
9
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
10
10
  import { AptosSigner } from "../types";
11
11
  import { signTransaction } from "../network";
12
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
12
13
 
13
14
  export const getAddress = (a: Account) => ({
14
15
  address: a.freshAddress,
@@ -45,14 +46,15 @@ const buildSignOperation =
45
46
  recipients.push(transaction.recipient);
46
47
  }
47
48
 
49
+ const subAccount =
50
+ !!transaction.subAccountId && findSubAccountById(account, transaction.subAccountId);
51
+
48
52
  // build optimistic operation
49
53
  const operation: Operation = {
50
54
  id: encodeOperationId(accountId, hash, type),
51
55
  hash,
52
56
  type,
53
- value: transaction.useAllAmount
54
- ? account.balance.minus(fee)
55
- : transaction.amount.plus(fee),
57
+ value: subAccount ? fee : transaction.amount.plus(fee),
56
58
  fee,
57
59
  extra,
58
60
  blockHash: null,
@@ -62,6 +64,20 @@ const buildSignOperation =
62
64
  accountId,
63
65
  date: new Date(),
64
66
  transactionSequenceNumber: Number(rawTx.sequence_number),
67
+ subOperations: subAccount
68
+ ? [
69
+ {
70
+ id: encodeOperationId(subAccount.id, "", "OUT"),
71
+ type: "OUT",
72
+ accountId: transaction.subAccountId,
73
+ senders: [account.freshAddress],
74
+ recipients: [transaction.recipient],
75
+ value: transaction.amount,
76
+ fee,
77
+ date: new Date(),
78
+ } as Operation,
79
+ ]
80
+ : [],
65
81
  };
66
82
 
67
83
  o.next({
@@ -1,9 +1,160 @@
1
+ import { inferSubOperations } from "@ledgerhq/coin-framework/serialization/index";
1
2
  import { decodeAccountId, encodeAccountId } from "@ledgerhq/coin-framework/account";
2
3
  import type { GetAccountShape } from "@ledgerhq/coin-framework/bridge/jsHelpers";
3
4
  import { mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
4
- import { AptosAPI } from "../api";
5
+ import { AptosAPI } from "../network";
5
6
  import { txsToOps } from "./logic";
6
7
  import type { AptosAccount } from "../types";
8
+ import { Account, Operation, TokenAccount } from "@ledgerhq/types-live";
9
+ import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
10
+ import {
11
+ decodeTokenAccountId,
12
+ emptyHistoryCache,
13
+ encodeTokenAccountId,
14
+ } from "@ledgerhq/coin-framework/account/index";
15
+ import { AccountShapeInfo } from "@ledgerhq/coin-framework/bridge/jsHelpers";
16
+
17
+ /**
18
+ * List of properties of a sub account that can be updated when 2 "identical" accounts are found
19
+ */
20
+ const updatableSubAccountProperties: { name: string; isOps: boolean }[] = [
21
+ { name: "balance", isOps: false },
22
+ { name: "spendableBalance", isOps: false },
23
+ { name: "balanceHistoryCache", isOps: false },
24
+ { name: "operations", isOps: true },
25
+ { name: "pendingOperations", isOps: true },
26
+ ];
27
+ /**
28
+ * In charge of smartly merging sub accounts while maintaining references as much as possible
29
+ */
30
+ export const mergeSubAccounts = (
31
+ initialAccount: Account | undefined,
32
+ newSubAccounts: TokenAccount[],
33
+ ): Array<TokenAccount> => {
34
+ const oldSubAccounts: Array<TokenAccount> | undefined = initialAccount?.subAccounts;
35
+ if (!oldSubAccounts) {
36
+ return newSubAccounts;
37
+ }
38
+
39
+ // Creating a map of already existing sub accounts by id
40
+ const oldSubAccountsById: { [key: string]: TokenAccount } = {};
41
+ for (const oldSubAccount of oldSubAccounts) {
42
+ oldSubAccountsById[oldSubAccount.id!] = oldSubAccount;
43
+ }
44
+
45
+ // Looping on new sub accounts to compare them with already existing ones
46
+ // Already existing will be updated if necessary (see `updatableSubAccountProperties`)
47
+ // Fresh new sub accounts will be added/pushed after already existing
48
+ const newSubAccountsToAdd: TokenAccount[] = [];
49
+ for (const newSubAccount of newSubAccounts) {
50
+ const duplicatedAccount: TokenAccount | undefined = oldSubAccountsById[newSubAccount.id!];
51
+
52
+ // If this sub account was not already in the initialAccount
53
+ if (!duplicatedAccount) {
54
+ // We'll add it later
55
+ newSubAccountsToAdd.push(newSubAccount);
56
+ continue;
57
+ }
58
+
59
+ const updates: Partial<TokenAccount> & { [key: string]: any } = {};
60
+ for (const { name, isOps } of updatableSubAccountProperties) {
61
+ if (!isOps) {
62
+ if (
63
+ newSubAccount[name as keyof TokenAccount] !==
64
+ duplicatedAccount[name as keyof TokenAccount]
65
+ ) {
66
+ updates[name] = newSubAccount[name as keyof TokenAccount];
67
+ }
68
+ } else {
69
+ updates[name] =
70
+ mergeOps(
71
+ duplicatedAccount[name as keyof TokenAccount] as Operation[],
72
+ newSubAccount[name as keyof TokenAccount] as Operation[],
73
+ ) || [];
74
+ }
75
+ }
76
+
77
+ // Updating the operationsCount in case the mergeOps changed it
78
+ updates.operationsCount =
79
+ updates.operations?.length || duplicatedAccount?.operations?.length || 0;
80
+
81
+ // Modifying the Map with the updated sub account with a new ref
82
+ oldSubAccountsById[newSubAccount.id!] = {
83
+ ...duplicatedAccount,
84
+ ...updates,
85
+ };
86
+ }
87
+ const updatedSubAccounts = Object.values(oldSubAccountsById);
88
+ return [...updatedSubAccounts, ...newSubAccountsToAdd];
89
+ };
90
+
91
+ /**
92
+ * Fetch the balance for a token and creates a TokenAccount based on this and the provided operations
93
+ */
94
+ export const getSubAccountShape = async (
95
+ currency: CryptoCurrency,
96
+ address: string,
97
+ parentId: string,
98
+ token: TokenCurrency,
99
+ operations: Operation[],
100
+ ): Promise<TokenAccount> => {
101
+ const aptosClient = new AptosAPI(currency.id);
102
+ const tokenAccountId = encodeTokenAccountId(parentId, token);
103
+ const balance = await aptosClient.getBalance(address, token);
104
+ const firstOperation = operations
105
+ .sort((a, b) => b.date.getTime() - a.date.getTime())
106
+ .at(operations.length - 1);
107
+
108
+ return {
109
+ type: "TokenAccount",
110
+ id: tokenAccountId,
111
+ parentId,
112
+ token,
113
+ balance,
114
+ spendableBalance: balance,
115
+ creationDate: firstOperation?.date || new Date(0),
116
+ operations,
117
+ operationsCount: operations.length,
118
+ pendingOperations: [],
119
+ balanceHistoryCache: emptyHistoryCache,
120
+ swapHistory: [],
121
+ };
122
+ };
123
+
124
+ /**
125
+ * Getting all token related operations in order to provide TokenAccounts
126
+ */
127
+ export const getSubAccounts = async (
128
+ infos: AccountShapeInfo<Account>,
129
+ address: string,
130
+ accountId: string,
131
+ lastTokenOperations: Operation[],
132
+ ): Promise<TokenAccount[]> => {
133
+ const { currency } = infos;
134
+
135
+ // Creating a Map of Operations by TokenCurrencies in order to know which TokenAccounts should be synced as well
136
+ const operationsByToken = lastTokenOperations.reduce<Map<TokenCurrency, Operation[]>>(
137
+ (acc, operation) => {
138
+ const { token } = decodeTokenAccountId(operation.accountId);
139
+ if (!token) return acc; // TODO: do we need to check blacklistedTokenIds
140
+
141
+ if (!acc.has(token)) {
142
+ acc.set(token, []);
143
+ }
144
+ acc.get(token)?.push(operation);
145
+ return acc;
146
+ },
147
+ new Map<TokenCurrency, Operation[]>(),
148
+ );
149
+
150
+ // Fetching all TokenAccounts possible and providing already filtered operations
151
+ const subAccountsPromises: Promise<TokenAccount>[] = [];
152
+ for (const [token, ops] of operationsByToken.entries()) {
153
+ subAccountsPromises.push(getSubAccountShape(currency, address, accountId, token, ops));
154
+ }
155
+
156
+ return Promise.all(subAccountsPromises);
157
+ };
7
158
 
8
159
  export const getAccountShape: GetAccountShape = async info => {
9
160
  const { address, initialAccount, currency, derivationMode, rest } = info;
@@ -26,14 +177,29 @@ export const getAccountShape: GetAccountShape = async info => {
26
177
  const xpub = initialAccount?.xpub || publicKey || "";
27
178
 
28
179
  const oldOperations = initialAccount?.operations || [];
29
- const startAt = (oldOperations[0]?.extra as any)?.version;
30
180
 
31
181
  const aptosClient = new AptosAPI(currency.id);
32
- const { balance, transactions, blockHeight } = await aptosClient.getAccountInfo(address, startAt);
182
+ const { balance, transactions, blockHeight } = await aptosClient.getAccountInfo(address);
33
183
 
34
- const newOperations = txsToOps(info, accountId, transactions);
184
+ const [newOperations, tokenOperations]: [Operation[], Operation[]] = txsToOps(
185
+ info,
186
+ accountId,
187
+ transactions,
188
+ );
35
189
  const operations = mergeOps(oldOperations, newOperations);
36
190
 
191
+ const newSubAccounts = await getSubAccounts(info, address, accountId, tokenOperations);
192
+ const shouldSyncFromScratch = initialAccount === undefined;
193
+ const subAccounts = shouldSyncFromScratch
194
+ ? newSubAccounts
195
+ : mergeSubAccounts(initialAccount, newSubAccounts);
196
+
197
+ operations.forEach(op => {
198
+ const subOperations = inferSubOperations(op.hash, subAccounts);
199
+ op.subOperations =
200
+ subOperations.length === 1 ? subOperations : subOperations.filter(op => !!op.blockHash);
201
+ });
202
+
37
203
  const shape: Partial<AptosAccount> = {
38
204
  type: "Account",
39
205
  id: accountId,
@@ -44,6 +210,7 @@ export const getAccountShape: GetAccountShape = async info => {
44
210
  operationsCount: operations.length,
45
211
  blockHeight,
46
212
  lastSyncDate: new Date(),
213
+ subAccounts,
47
214
  };
48
215
 
49
216
  return shape;
package/src/config.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { type AptosSettings } from "@aptos-labs/ts-sdk";
2
+ import buildCoinConfig, {
3
+ type CoinConfig,
4
+ type CurrencyConfig,
5
+ } from "@ledgerhq/coin-framework/config";
6
+ import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
7
+
8
+ export type AptosConfig = {
9
+ aptosSettings: AptosSettings;
10
+ };
11
+
12
+ export type AptosCoinConfig = CurrencyConfig & AptosConfig;
13
+
14
+ const coinConfig: {
15
+ setCoinConfig: (config: CoinConfig<AptosCoinConfig>) => void;
16
+ getCoinConfig: (currency?: CryptoCurrency) => AptosCoinConfig;
17
+ } = buildCoinConfig<AptosCoinConfig>();
18
+
19
+ export default coinConfig;