@ledgerhq/coin-multiversx 0.7.0 → 0.8.0-nightly.20251108023448

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 (52) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/lib/api/sdk.d.ts.map +1 -1
  3. package/lib/api/sdk.js +3 -1
  4. package/lib/api/sdk.js.map +1 -1
  5. package/lib/buildOptimisticOperation.js +1 -1
  6. package/lib/buildOptimisticOperation.js.map +1 -1
  7. package/lib/buildSubAccounts.d.ts.map +1 -1
  8. package/lib/buildSubAccounts.js +72 -53
  9. package/lib/buildSubAccounts.js.map +1 -1
  10. package/lib/deviceTransactionConfig.js +1 -1
  11. package/lib/deviceTransactionConfig.js.map +1 -1
  12. package/lib/encode.d.ts +1 -1
  13. package/lib/encode.d.ts.map +1 -1
  14. package/lib/encode.js +2 -2
  15. package/lib/encode.js.map +1 -1
  16. package/lib/prepareTransaction.js +1 -1
  17. package/lib/prepareTransaction.js.map +1 -1
  18. package/lib/signOperation.js +1 -1
  19. package/lib/signOperation.js.map +1 -1
  20. package/lib/speculos-deviceActions.d.ts.map +1 -1
  21. package/lib/speculos-deviceActions.js +4 -4
  22. package/lib/speculos-deviceActions.js.map +1 -1
  23. package/lib-es/api/sdk.d.ts.map +1 -1
  24. package/lib-es/api/sdk.js +3 -1
  25. package/lib-es/api/sdk.js.map +1 -1
  26. package/lib-es/buildOptimisticOperation.js +1 -1
  27. package/lib-es/buildOptimisticOperation.js.map +1 -1
  28. package/lib-es/buildSubAccounts.d.ts.map +1 -1
  29. package/lib-es/buildSubAccounts.js +69 -50
  30. package/lib-es/buildSubAccounts.js.map +1 -1
  31. package/lib-es/deviceTransactionConfig.js +1 -1
  32. package/lib-es/deviceTransactionConfig.js.map +1 -1
  33. package/lib-es/encode.d.ts +1 -1
  34. package/lib-es/encode.d.ts.map +1 -1
  35. package/lib-es/encode.js +2 -2
  36. package/lib-es/encode.js.map +1 -1
  37. package/lib-es/prepareTransaction.js +1 -1
  38. package/lib-es/prepareTransaction.js.map +1 -1
  39. package/lib-es/signOperation.js +1 -1
  40. package/lib-es/signOperation.js.map +1 -1
  41. package/lib-es/speculos-deviceActions.d.ts.map +1 -1
  42. package/lib-es/speculos-deviceActions.js +4 -4
  43. package/lib-es/speculos-deviceActions.js.map +1 -1
  44. package/package.json +4 -4
  45. package/src/api/sdk.ts +4 -1
  46. package/src/buildOptimisticOperation.ts +1 -1
  47. package/src/buildSubAccounts.ts +110 -54
  48. package/src/deviceTransactionConfig.ts +1 -1
  49. package/src/encode.ts +2 -2
  50. package/src/prepareTransaction.ts +1 -1
  51. package/src/signOperation.ts +1 -1
  52. package/src/speculos-deviceActions.ts +4 -5
@@ -1,4 +1,4 @@
1
- import { findTokenById } from "@ledgerhq/cryptoassets";
1
+ import { getCryptoAssetsStore } from "@ledgerhq/coin-framework/crypto-assets/index";
2
2
  import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
3
3
  import { Account, SyncConfig, TokenAccount } from "@ledgerhq/types-live";
4
4
  import BigNumber from "bignumber.js";
@@ -6,6 +6,7 @@ import { emptyHistoryCache, encodeTokenAccountId } from "@ledgerhq/coin-framewor
6
6
  import { mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
7
7
  import { getESDTOperations, getAccountESDTTokens } from "./api";
8
8
  import { addPrefixToken, extractTokenId } from "./logic";
9
+ import { ESDTToken } from "./types";
9
10
 
10
11
  async function buildMultiversXESDTTokenAccount({
11
12
  parentAccountId,
@@ -41,27 +42,114 @@ async function buildMultiversXESDTTokenAccount({
41
42
  return tokenAccount;
42
43
  }
43
44
 
45
+ function buildExistingAccountMaps(
46
+ existingAccount: Account | null | undefined,
47
+ blacklistedTokenIds: string[],
48
+ ): { existingAccountByTicker: Record<string, TokenAccount>; existingAccountTickers: string[] } {
49
+ const existingAccountByTicker: Record<string, TokenAccount> = {};
50
+ const existingAccountTickers: string[] = [];
51
+
52
+ if (!existingAccount?.subAccounts) {
53
+ return { existingAccountByTicker, existingAccountTickers };
54
+ }
55
+
56
+ for (const existingSubAccount of existingAccount.subAccounts) {
57
+ if (existingSubAccount.type !== "TokenAccount") continue;
58
+
59
+ const { ticker, id } = existingSubAccount.token;
60
+
61
+ if (!blacklistedTokenIds.includes(id)) {
62
+ existingAccountTickers.push(ticker);
63
+ existingAccountByTicker[ticker] = existingSubAccount;
64
+ }
65
+ }
66
+
67
+ return { existingAccountByTicker, existingAccountTickers };
68
+ }
69
+
44
70
  async function syncESDTTokenAccountOperations(
45
71
  tokenAccount: TokenAccount,
46
72
  address: string,
47
73
  ): Promise<TokenAccount> {
48
74
  const oldOperations = tokenAccount?.operations || [];
49
- // Needed for incremental synchronisation
50
75
  const startAt = oldOperations.length ? Math.floor(oldOperations[0].date.valueOf() / 1000) : 0;
51
76
 
52
77
  const tokenIdentifierHex = extractTokenId(tokenAccount.token.id);
53
78
  const tokenIdentifier = Buffer.from(tokenIdentifierHex, "hex").toString();
54
79
 
55
- // Merge new operations with the previously synced ones
56
80
  const newOperations = await getESDTOperations(tokenAccount.id, address, tokenIdentifier, startAt);
57
81
  const operations = mergeOps(oldOperations, newOperations);
58
82
 
59
83
  if (operations === oldOperations) return tokenAccount;
60
84
 
61
- const copy = { ...tokenAccount };
62
- copy.operations = operations;
63
- copy.operationsCount = operations.length;
64
- return copy;
85
+ return {
86
+ ...tokenAccount,
87
+ operations,
88
+ operationsCount: operations.length,
89
+ };
90
+ }
91
+
92
+ async function updateTokenAccountBalance(
93
+ tokenAccount: TokenAccount,
94
+ newBalance: BigNumber,
95
+ ): Promise<TokenAccount> {
96
+ if (newBalance.eq(tokenAccount.balance)) {
97
+ return tokenAccount;
98
+ }
99
+
100
+ return {
101
+ ...tokenAccount,
102
+ balance: newBalance,
103
+ spendableBalance: newBalance,
104
+ };
105
+ }
106
+
107
+ async function processESDT({
108
+ esdt,
109
+ accountId,
110
+ accountAddress,
111
+ existingAccountByTicker,
112
+ existingAccountTickers,
113
+ blacklistedTokenIds,
114
+ }: {
115
+ esdt: ESDTToken;
116
+ accountId: string;
117
+ accountAddress: string;
118
+ existingAccountByTicker: Record<string, TokenAccount>;
119
+ existingAccountTickers: string[];
120
+ blacklistedTokenIds: string[];
121
+ }): Promise<TokenAccount | null> {
122
+ const esdtIdentifierHex = Buffer.from(esdt.identifier).toString("hex");
123
+ const token = await getCryptoAssetsStore().findTokenById(addPrefixToken(esdtIdentifierHex));
124
+
125
+ if (!token || blacklistedTokenIds.includes(token.id)) {
126
+ return null;
127
+ }
128
+
129
+ const existingTokenAccount = existingAccountByTicker[token.ticker];
130
+ const balance = new BigNumber(esdt.balance);
131
+
132
+ let tokenAccount: TokenAccount;
133
+
134
+ if (existingTokenAccount) {
135
+ const syncedTokenAccount = await syncESDTTokenAccountOperations(
136
+ existingTokenAccount,
137
+ accountAddress,
138
+ );
139
+ tokenAccount = await updateTokenAccountBalance(syncedTokenAccount, balance);
140
+ } else {
141
+ tokenAccount = await buildMultiversXESDTTokenAccount({
142
+ parentAccountId: accountId,
143
+ accountAddress,
144
+ token,
145
+ balance,
146
+ });
147
+ }
148
+
149
+ existingAccountTickers.push(token.ticker);
150
+ existingAccountByTicker[token.ticker] = tokenAccount;
151
+
152
+ return tokenAccount;
65
153
  }
66
154
 
67
155
  async function MultiversXBuildESDTTokenAccounts({
@@ -79,56 +167,24 @@ async function MultiversXBuildESDTTokenAccounts({
79
167
  const { blacklistedTokenIds = [] } = syncConfig;
80
168
  const tokenAccounts: TokenAccount[] = [];
81
169
 
82
- const existingAccountByTicker: { [key: string]: TokenAccount } = {}; // used for fast lookup
83
-
84
- const existingAccountTickers: string[] = []; // used to keep track of ordering
85
-
86
- if (existingAccount && existingAccount.subAccounts) {
87
- for (const existingSubAccount of existingAccount.subAccounts) {
88
- if (existingSubAccount.type === "TokenAccount") {
89
- const { ticker, id } = existingSubAccount.token;
90
-
91
- if (!blacklistedTokenIds.includes(id)) {
92
- existingAccountTickers.push(ticker);
93
- existingAccountByTicker[ticker] = existingSubAccount;
94
- }
95
- }
96
- }
97
- }
170
+ const { existingAccountByTicker, existingAccountTickers } = buildExistingAccountMaps(
171
+ existingAccount,
172
+ blacklistedTokenIds,
173
+ );
98
174
 
99
175
  const accountESDTs = await getAccountESDTTokens(accountAddress);
100
176
  for (const esdt of accountESDTs) {
101
- const esdtIdentifierHex = Buffer.from(esdt.identifier).toString("hex");
102
- const token = findTokenById(addPrefixToken(esdtIdentifierHex));
103
-
104
- if (token && !blacklistedTokenIds.includes(token.id)) {
105
- let tokenAccount = existingAccountByTicker[token.ticker];
106
- if (!tokenAccount) {
107
- tokenAccount = await buildMultiversXESDTTokenAccount({
108
- parentAccountId: accountId,
109
- accountAddress,
110
- token,
111
- balance: new BigNumber(esdt.balance),
112
- });
113
- } else {
114
- const inputTokenAccount = tokenAccount;
115
- tokenAccount = await syncESDTTokenAccountOperations(inputTokenAccount, accountAddress);
116
- const balance = new BigNumber(esdt.balance);
117
- if (!balance.eq(tokenAccount.balance)) {
118
- // only recreate the object if balance changed
119
- if (inputTokenAccount === tokenAccount) {
120
- tokenAccount = { ...tokenAccount };
121
- }
122
- tokenAccount.balance = balance;
123
- tokenAccount.spendableBalance = balance;
124
- }
125
- }
126
-
127
- if (tokenAccount) {
128
- tokenAccounts.push(tokenAccount);
129
- existingAccountTickers.push(token.ticker);
130
- existingAccountByTicker[token.ticker] = tokenAccount;
131
- }
177
+ const tokenAccount = await processESDT({
178
+ esdt,
179
+ accountId,
180
+ accountAddress,
181
+ existingAccountByTicker,
182
+ existingAccountTickers,
183
+ blacklistedTokenIds,
184
+ });
185
+
186
+ if (tokenAccount) {
187
+ tokenAccounts.push(tokenAccount);
132
188
  }
133
189
  }
134
190
 
@@ -21,7 +21,7 @@ async function getDeviceTransactionConfig({
21
21
  const isEsdtTransfer = subAccountId !== undefined && subAccountId !== null;
22
22
 
23
23
  if (isEsdtTransfer) {
24
- const { token } = decodeTokenAccountId(subAccountId);
24
+ const { token } = await decodeTokenAccountId(subAccountId);
25
25
 
26
26
  if (token) {
27
27
  fields.push({
package/src/encode.ts CHANGED
@@ -4,8 +4,8 @@ import type { Transaction } from "./types";
4
4
  import { extractTokenId } from "./logic";
5
5
 
6
6
  export class MultiversXEncodeTransaction {
7
- static ESDTTransfer(t: Transaction, ta: TokenAccount): string {
8
- const { token } = decodeTokenAccountId(ta.id);
7
+ static async ESDTTransfer(t: Transaction, ta: TokenAccount): Promise<string> {
8
+ const { token } = await decodeTokenAccountId(ta.id);
9
9
  const tokenIdentifierHex = token && extractTokenId(token.id);
10
10
  let amountHex = t.useAllAmount ? ta.balance.toString(16) : t.amount.toString(16);
11
11
 
@@ -26,7 +26,7 @@ export const prepareTransaction: AccountBridge<Transaction>["prepareTransaction"
26
26
  null;
27
27
 
28
28
  if (tokenAccount) {
29
- preparedTx.data = MultiversXEncodeTransaction.ESDTTransfer(transaction, tokenAccount);
29
+ preparedTx.data = await MultiversXEncodeTransaction.ESDTTransfer(transaction, tokenAccount);
30
30
  preparedTx.gasLimit = GAS.ESDT_TRANSFER;
31
31
  } else {
32
32
  switch (transaction.mode) {
@@ -36,7 +36,7 @@ export const buildSignOperation =
36
36
  await signerContext(deviceId, signer => signer.setAddress(account.freshAddressPath));
37
37
 
38
38
  if (tokenAccount) {
39
- const { token } = decodeTokenAccountId(tokenAccount.id);
39
+ const { token } = await decodeTokenAccountId(tokenAccount.id);
40
40
  if (!token) {
41
41
  throw new Error("Invalid token");
42
42
  }
@@ -190,7 +190,7 @@ export const acceptEsdtTransferTransaction: DeviceAction<Transaction, any> = dev
190
190
  {
191
191
  title: "Token",
192
192
  button: SpeculosButton.RIGHT,
193
- expectedValue: ({ account, transaction }) => {
193
+ expectedValue: async ({ account, transaction }) => {
194
194
  const { subAccounts } = account;
195
195
  const { subAccountId } = transaction;
196
196
  const tokenAccount = !subAccountId
@@ -201,15 +201,14 @@ export const acceptEsdtTransferTransaction: DeviceAction<Transaction, any> = dev
201
201
  throw new Error();
202
202
  }
203
203
 
204
- const { token } = decodeTokenAccountId(tokenAccount.id);
205
-
204
+ const { token } = await decodeTokenAccountId(tokenAccount.id);
206
205
  return token?.name ?? "";
207
206
  },
208
207
  },
209
208
  {
210
209
  title: "Value",
211
210
  button: SpeculosButton.RIGHT,
212
- expectedValue: ({ account, transaction }) => {
211
+ expectedValue: async ({ account, transaction }) => {
213
212
  const { subAccounts } = account;
214
213
  const { subAccountId } = transaction;
215
214
  const tokenAccount = !subAccountId
@@ -220,7 +219,7 @@ export const acceptEsdtTransferTransaction: DeviceAction<Transaction, any> = dev
220
219
  throw new Error();
221
220
  }
222
221
 
223
- const { token } = decodeTokenAccountId(tokenAccount.id);
222
+ const { token } = await decodeTokenAccountId(tokenAccount.id);
224
223
  if (!token) {
225
224
  throw new Error();
226
225
  }