@ledgerhq/coin-sui 0.17.0-nightly.2 → 0.17.0-nightly.20251031023756

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 (43) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +20 -20
  3. package/lib/bridge/preload.js +2 -2
  4. package/lib/bridge/preload.js.map +1 -1
  5. package/lib/bridge/synchronisation.d.ts.map +1 -1
  6. package/lib/bridge/synchronisation.js +19 -20
  7. package/lib/bridge/synchronisation.js.map +1 -1
  8. package/lib/network/index.d.ts +1 -5
  9. package/lib/network/index.d.ts.map +1 -1
  10. package/lib/network/sdk.d.ts +6 -5
  11. package/lib/network/sdk.d.ts.map +1 -1
  12. package/lib/network/sdk.js +11 -3
  13. package/lib/network/sdk.js.map +1 -1
  14. package/lib/test/bridge.dataset.d.ts.map +1 -1
  15. package/lib/test/bridge.dataset.js +1 -0
  16. package/lib/test/bridge.dataset.js.map +1 -1
  17. package/lib/types/bridge.d.ts +1 -0
  18. package/lib/types/bridge.d.ts.map +1 -1
  19. package/lib-es/bridge/preload.js +1 -1
  20. package/lib-es/bridge/preload.js.map +1 -1
  21. package/lib-es/bridge/synchronisation.d.ts.map +1 -1
  22. package/lib-es/bridge/synchronisation.js +19 -20
  23. package/lib-es/bridge/synchronisation.js.map +1 -1
  24. package/lib-es/network/index.d.ts +1 -5
  25. package/lib-es/network/index.d.ts.map +1 -1
  26. package/lib-es/network/sdk.d.ts +6 -5
  27. package/lib-es/network/sdk.d.ts.map +1 -1
  28. package/lib-es/network/sdk.js +11 -3
  29. package/lib-es/network/sdk.js.map +1 -1
  30. package/lib-es/test/bridge.dataset.d.ts.map +1 -1
  31. package/lib-es/test/bridge.dataset.js +1 -0
  32. package/lib-es/test/bridge.dataset.js.map +1 -1
  33. package/lib-es/types/bridge.d.ts +1 -0
  34. package/lib-es/types/bridge.d.ts.map +1 -1
  35. package/package.json +9 -9
  36. package/src/bridge/preload.ts +1 -1
  37. package/src/bridge/synchronisation.test.ts +118 -5
  38. package/src/bridge/synchronisation.ts +31 -29
  39. package/src/logic/estimateFees.integration.test.ts +1 -1
  40. package/src/network/sdk.test.ts +22 -0
  41. package/src/network/sdk.ts +21 -4
  42. package/src/test/bridge.dataset.ts +1 -0
  43. package/src/types/bridge.ts +1 -0
@@ -10,13 +10,13 @@ import {
10
10
  type GetAccountShape,
11
11
  } from "@ledgerhq/coin-framework/bridge/jsHelpers";
12
12
  import { type Operation } from "@ledgerhq/types-live";
13
- import { listTokensForCryptoCurrency } from "@ledgerhq/cryptoassets/tokens";
14
13
  import { getAccountBalances, getOperations, getStakesRaw } from "../network";
15
- import { DEFAULT_COIN_TYPE } from "../network/sdk";
14
+ import { AccountBalance, DEFAULT_COIN_TYPE } from "../network/sdk";
16
15
  import { SuiOperationExtra, SuiAccount } from "../types";
17
16
  import type { SyncConfig, TokenAccount } from "@ledgerhq/types-live";
18
17
  import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
19
18
  import { promiseAllBatched } from "@ledgerhq/live-promise";
19
+ import { getCryptoAssetsStore } from "@ledgerhq/coin-framework/crypto-assets/index";
20
20
 
21
21
  /**
22
22
  * Get the shape of the account including its operations and balance.
@@ -53,28 +53,28 @@ export const getAccountShape: GetAccountShape<SuiAccount> = async (info, syncCon
53
53
  );
54
54
 
55
55
  const accountBalances = await getAccountBalances(address);
56
- const tokensCurrencies = listTokensForCryptoCurrency(currency);
57
- const tokensCurrenciesMap = tokensCurrencies.reduce(
58
- (acc, token) => {
59
- acc[token.contractAddress] = token;
60
- return acc;
61
- },
62
- {} as Record<string, (typeof tokensCurrencies)[0]>,
63
- );
64
56
  const balance =
65
57
  accountBalances.find(({ coinType }) => coinType === DEFAULT_COIN_TYPE)?.balance ?? BigNumber(0);
66
- const subAccountsBalances = accountBalances.filter(
67
- ({ coinType }) => tokensCurrenciesMap[coinType],
68
- );
58
+
59
+ const subAccountsBalances: AccountBalance[] = [];
60
+ for (const accountBalance of accountBalances) {
61
+ const token = await getCryptoAssetsStore().findTokenByAddressInCurrency(
62
+ accountBalance.coinType,
63
+ currency.id,
64
+ );
65
+ if (token) {
66
+ subAccountsBalances.push(accountBalance);
67
+ }
68
+ }
69
69
 
70
70
  const subAccounts =
71
71
  (await buildSubAccounts({
72
72
  accountId,
73
- initialAccount,
74
73
  operations,
75
74
  subAccountsBalances,
76
75
  syncConfig,
77
- tokensCurrenciesMap,
76
+ currencyId: currency.id,
77
+ subAccounts: initialAccount?.subAccounts ?? [],
78
78
  })) || [];
79
79
 
80
80
  return {
@@ -102,39 +102,41 @@ export const sync = makeSync({ getAccountShape, shouldMergeOps: false });
102
102
 
103
103
  async function buildSubAccounts({
104
104
  accountId,
105
- initialAccount,
106
105
  operations,
107
106
  subAccountsBalances,
108
107
  syncConfig,
109
- tokensCurrenciesMap,
108
+ currencyId,
109
+ subAccounts,
110
110
  }: {
111
111
  accountId: string;
112
- initialAccount?: SuiAccount | null | undefined;
113
112
  operations: Operation[];
114
113
  subAccountsBalances: { coinType: string; blockHeight: number; balance: BigNumber }[];
115
114
  syncConfig: SyncConfig;
116
- tokensCurrenciesMap: Record<string, TokenCurrency>;
115
+ currencyId: string;
116
+ subAccounts: TokenAccount[];
117
117
  }) {
118
- if (Object.keys(tokensCurrenciesMap).length === 0) return undefined;
118
+ if (subAccountsBalances.length === 0) return undefined;
119
119
  const { blacklistedTokenIds = [] } = syncConfig;
120
120
  const tokenAccounts: TokenAccount[] = [];
121
121
  const existingAccountByTicker: { [ticker: string]: TokenAccount } = {}; // used for fast lookup
122
122
  const existingAccountTickers: string[] = []; // used to keep track of ordering
123
123
 
124
- if (initialAccount?.subAccounts) {
125
- for (const existingSubAccount of initialAccount.subAccounts) {
126
- if (existingSubAccount.type === "TokenAccount") {
127
- const { ticker, id } = existingSubAccount.token;
128
- if (!blacklistedTokenIds.includes(id)) {
129
- existingAccountTickers.push(ticker);
130
- existingAccountByTicker[ticker] = existingSubAccount;
131
- }
124
+ for (const existingSubAccount of subAccounts) {
125
+ if (existingSubAccount.type === "TokenAccount") {
126
+ const { ticker, id } = existingSubAccount.token;
127
+ if (!blacklistedTokenIds.includes(id)) {
128
+ existingAccountTickers.push(ticker);
129
+ existingAccountByTicker[ticker] = existingSubAccount;
132
130
  }
133
131
  }
134
132
  }
135
133
 
136
134
  await promiseAllBatched(3, subAccountsBalances, async accountBalance => {
137
- const token = tokensCurrenciesMap[accountBalance.coinType];
135
+ const token = await getCryptoAssetsStore().findTokenByAddressInCurrency(
136
+ accountBalance.coinType,
137
+ currencyId,
138
+ );
139
+
138
140
  if (token && !blacklistedTokenIds.includes(token.id)) {
139
141
  const initialTokenAccount = existingAccountByTicker[token.ticker];
140
142
  const tokenAccount = await buildSubAccount({
@@ -3,7 +3,7 @@ import { getFullnodeUrl } from "@mysten/sui/client";
3
3
  import coinConfig from "../config";
4
4
  import { estimateFees } from "./estimateFees";
5
5
 
6
- const SENDER = "0xad79719ac7edb44f6e253f1f771e8291e281a6aaf1e4789b52bf85336f525e8e";
6
+ const SENDER = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
7
7
  const RECIPIENT = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
8
8
 
9
9
  describe("estimateFees", () => {
@@ -7,6 +7,7 @@ import type {
7
7
  TransactionBlockData,
8
8
  SuiTransactionBlockResponse,
9
9
  SuiTransactionBlockKind,
10
+ PaginatedTransactionResponse,
10
11
  } from "@mysten/sui/client";
11
12
  import assert, { fail } from "assert";
12
13
 
@@ -2121,6 +2122,27 @@ describe("getCoinsForAmount", () => {
2121
2122
  });
2122
2123
  });
2123
2124
 
2125
+ describe.only("dedup", () => {
2126
+ const outs: PaginatedTransactionResponse = {
2127
+ data: [],
2128
+ hasNextPage: false,
2129
+ };
2130
+ const ins: PaginatedTransactionResponse = {
2131
+ data: [],
2132
+ hasNextPage: true,
2133
+ };
2134
+
2135
+ test("handles no data in asc mode", async () => {
2136
+ const r = sdk.dedupOperations(outs, ins, "asc");
2137
+ expect(r.operations.length).toBe(0);
2138
+ });
2139
+
2140
+ test("handles no data in desc mode", async () => {
2141
+ const r = sdk.dedupOperations(outs, ins, "desc");
2142
+ expect(r.operations.length).toBe(0);
2143
+ });
2144
+ });
2145
+
2124
2146
  describe("pagination", () => {
2125
2147
  test("handles single page scenarios", async () => {
2126
2148
  const mockCoins = createMockCoins(["800", "400", "300"]);
@@ -137,10 +137,16 @@ function isUnstaking(block?: SuiTransactionBlockKind): block is ProgrammableTran
137
137
  return hasMoveCallWithFunction("request_withdraw_stake", block);
138
138
  }
139
139
 
140
+ export type AccountBalance = {
141
+ coinType: string;
142
+ blockHeight: number;
143
+ balance: BigNumber;
144
+ };
145
+
140
146
  /**
141
147
  * Get account balance (native and tokens)
142
148
  */
143
- export const getAccountBalances = async (addr: string) => {
149
+ export const getAccountBalances = async (addr: string): Promise<AccountBalance[]> => {
144
150
  const balances = await getAllBalancesCached(addr);
145
151
  return balances.map(({ coinType, totalBalance }) => ({
146
152
  coinType,
@@ -196,7 +202,18 @@ export const getOperationRecipients = (transaction?: TransactionBlockData): stri
196
202
  recipients.push(String(input.value));
197
203
  }
198
204
  });
199
- if (isUnstaking(transaction.transaction) || isStaking(transaction.transaction)) return [];
205
+ if (isStaking(transaction.transaction)) {
206
+ const address = transaction.transaction.inputs.find(
207
+ (input: SuiCallArg) => "valueType" in input && input.valueType === "address",
208
+ );
209
+ if (address && address.type === "pure" && address.valueType === "address") {
210
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
211
+ recipients.push(address.value as string);
212
+ }
213
+ }
214
+ if (isUnstaking(transaction.transaction)) {
215
+ return [];
216
+ }
200
217
  return recipients;
201
218
  }
202
219
  return [];
@@ -680,8 +697,8 @@ export const getListOperations = async (
680
697
  });
681
698
 
682
699
  const oldestOpTime = (ops: PaginatedTransactionResponse) =>
683
- Number(ops.data[ops.data.length - 1].timestampMs ?? 0);
684
- const newestOpTime = (ops: PaginatedTransactionResponse) => Number(ops.data[0].timestampMs ?? 0);
700
+ Number(ops.data[ops.data.length - 1]?.timestampMs ?? 0);
701
+ const newestOpTime = (ops: PaginatedTransactionResponse) => Number(ops.data[0]?.timestampMs ?? 0);
685
702
 
686
703
  /**
687
704
  * Some IN operations are also OUT operations because the sender receive a new version of the coin objects,
@@ -31,6 +31,7 @@ const suiAccount1: AccountRaw = {
31
31
  };
32
32
 
33
33
  const sui: CurrenciesData<Transaction> = {
34
+ FIXME_ignorePreloadFields: ["tokens"],
34
35
  scanAccounts: [
35
36
  {
36
37
  name: "sui seed 1",
@@ -15,6 +15,7 @@ export type MappedStake = StakeObject & {
15
15
  validator: SuiValidator;
16
16
  stakedSuiId: string;
17
17
  formattedAmount: string;
18
+ formattedEstimatedReward: string;
18
19
  };
19
20
 
20
21
  /**