@ledgerhq/coin-solana 0.37.0 → 0.38.0-nightly.20251126160702

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 (156) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/lib/bridge/bridge.d.ts +2 -4
  3. package/lib/bridge/bridge.d.ts.map +1 -1
  4. package/lib/bridge/bridge.js +16 -16
  5. package/lib/bridge/bridge.js.map +1 -1
  6. package/lib/bridge/js.d.ts +1 -2
  7. package/lib/bridge/js.d.ts.map +1 -1
  8. package/lib/bridge/js.js +9 -11
  9. package/lib/bridge/js.js.map +1 -1
  10. package/lib/cli-transaction.js +2 -2
  11. package/lib/cli-transaction.js.map +1 -1
  12. package/lib/config.d.ts +0 -1
  13. package/lib/config.d.ts.map +1 -1
  14. package/lib/config.js.map +1 -1
  15. package/lib/helpers/token.js +2 -2
  16. package/lib/helpers/token.js.map +1 -1
  17. package/lib/network/chain/index.d.ts.map +1 -1
  18. package/lib/network/chain/index.js +29 -38
  19. package/lib/network/chain/index.js.map +1 -1
  20. package/lib/network/chain/web3.d.ts.map +1 -1
  21. package/lib/network/chain/web3.js +9 -5
  22. package/lib/network/chain/web3.js.map +1 -1
  23. package/lib/network/index.d.ts +0 -2
  24. package/lib/network/index.d.ts.map +1 -1
  25. package/lib/network/index.js +1 -5
  26. package/lib/network/index.js.map +1 -1
  27. package/lib/network/nft/index.js +3 -3
  28. package/lib/network/nft/index.js.map +1 -1
  29. package/lib/preload-data.d.ts.map +1 -1
  30. package/lib/preload-data.js +0 -1
  31. package/lib/preload-data.js.map +1 -1
  32. package/lib/preload.d.ts +2 -6
  33. package/lib/preload.d.ts.map +1 -1
  34. package/lib/preload.js +2 -107
  35. package/lib/preload.js.map +1 -1
  36. package/lib/prepareTransaction.js +2 -2
  37. package/lib/prepareTransaction.js.map +1 -1
  38. package/lib/signOperation.d.ts +1 -1
  39. package/lib/signOperation.d.ts.map +1 -1
  40. package/lib/signOperation.js +1 -1
  41. package/lib/signOperation.js.map +1 -1
  42. package/lib/synchronization.d.ts.map +1 -1
  43. package/lib/synchronization.js +11 -7
  44. package/lib/synchronization.js.map +1 -1
  45. package/lib/transaction.js +2 -2
  46. package/lib/transaction.js.map +1 -1
  47. package/lib/types.d.ts +0 -2
  48. package/lib/types.d.ts.map +1 -1
  49. package/lib-es/bridge/bridge.d.ts +2 -4
  50. package/lib-es/bridge/bridge.d.ts.map +1 -1
  51. package/lib-es/bridge/bridge.js +17 -17
  52. package/lib-es/bridge/bridge.js.map +1 -1
  53. package/lib-es/bridge/js.d.ts +1 -2
  54. package/lib-es/bridge/js.d.ts.map +1 -1
  55. package/lib-es/bridge/js.js +10 -12
  56. package/lib-es/bridge/js.js.map +1 -1
  57. package/lib-es/cli-transaction.js +1 -1
  58. package/lib-es/cli-transaction.js.map +1 -1
  59. package/lib-es/config.d.ts +0 -1
  60. package/lib-es/config.d.ts.map +1 -1
  61. package/lib-es/config.js.map +1 -1
  62. package/lib-es/helpers/token.js +1 -1
  63. package/lib-es/helpers/token.js.map +1 -1
  64. package/lib-es/network/chain/index.d.ts.map +1 -1
  65. package/lib-es/network/chain/index.js +29 -38
  66. package/lib-es/network/chain/index.js.map +1 -1
  67. package/lib-es/network/chain/web3.d.ts.map +1 -1
  68. package/lib-es/network/chain/web3.js +9 -5
  69. package/lib-es/network/chain/web3.js.map +1 -1
  70. package/lib-es/network/index.d.ts +0 -2
  71. package/lib-es/network/index.d.ts.map +1 -1
  72. package/lib-es/network/index.js +0 -2
  73. package/lib-es/network/index.js.map +1 -1
  74. package/lib-es/network/nft/index.js +1 -1
  75. package/lib-es/network/nft/index.js.map +1 -1
  76. package/lib-es/preload-data.d.ts.map +1 -1
  77. package/lib-es/preload-data.js +0 -1
  78. package/lib-es/preload-data.js.map +1 -1
  79. package/lib-es/preload.d.ts +2 -6
  80. package/lib-es/preload.d.ts.map +1 -1
  81. package/lib-es/preload.js +2 -81
  82. package/lib-es/preload.js.map +1 -1
  83. package/lib-es/prepareTransaction.js +1 -1
  84. package/lib-es/prepareTransaction.js.map +1 -1
  85. package/lib-es/signOperation.d.ts +1 -1
  86. package/lib-es/signOperation.d.ts.map +1 -1
  87. package/lib-es/signOperation.js +1 -1
  88. package/lib-es/signOperation.js.map +1 -1
  89. package/lib-es/synchronization.d.ts.map +1 -1
  90. package/lib-es/synchronization.js +10 -6
  91. package/lib-es/synchronization.js.map +1 -1
  92. package/lib-es/transaction.js +1 -1
  93. package/lib-es/transaction.js.map +1 -1
  94. package/lib-es/types.d.ts +0 -2
  95. package/lib-es/types.d.ts.map +1 -1
  96. package/package.json +6 -8
  97. package/src/__tests__/getTransactions.test.ts +81 -0
  98. package/src/__tests__/unit/hw-signMessage.unit.test.ts +0 -1
  99. package/src/bridge/bridge.ts +27 -35
  100. package/src/bridge/js.ts +9 -29
  101. package/src/cli-transaction.ts +1 -1
  102. package/src/config.ts +0 -1
  103. package/src/helpers/token.ts +1 -1
  104. package/src/network/chain/index.ts +29 -38
  105. package/src/network/chain/web3.integration.test.ts +21 -1
  106. package/src/network/chain/web3.ts +11 -5
  107. package/src/network/index.ts +0 -2
  108. package/src/network/nft/index.ts +1 -1
  109. package/src/preload-data.ts +0 -1
  110. package/src/preload.test.ts +121 -12
  111. package/src/preload.ts +2 -111
  112. package/src/prepareTransaction.ts +1 -1
  113. package/src/signOperation.test.ts +2 -2
  114. package/src/signOperation.ts +2 -2
  115. package/src/synchronization.ts +10 -6
  116. package/src/tests/tokens-bridge.unit.test.ts +11 -16
  117. package/src/transaction.ts +1 -1
  118. package/src/types.ts +0 -2
  119. package/lib/cryptoAssetsStore.d.ts +0 -5
  120. package/lib/cryptoAssetsStore.d.ts.map +0 -1
  121. package/lib/cryptoAssetsStore.js +0 -17
  122. package/lib/cryptoAssetsStore.js.map +0 -1
  123. package/lib/network/cached.d.ts +0 -3
  124. package/lib/network/cached.d.ts.map +0 -1
  125. package/lib/network/cached.js +0 -56
  126. package/lib/network/cached.js.map +0 -1
  127. package/lib/network/queued.d.ts +0 -3
  128. package/lib/network/queued.d.ts.map +0 -1
  129. package/lib/network/queued.js +0 -27
  130. package/lib/network/queued.js.map +0 -1
  131. package/lib/network/traced.d.ts +0 -3
  132. package/lib/network/traced.d.ts.map +0 -1
  133. package/lib/network/traced.js +0 -80
  134. package/lib/network/traced.js.map +0 -1
  135. package/lib-es/cryptoAssetsStore.d.ts +0 -5
  136. package/lib-es/cryptoAssetsStore.d.ts.map +0 -1
  137. package/lib-es/cryptoAssetsStore.js +0 -12
  138. package/lib-es/cryptoAssetsStore.js.map +0 -1
  139. package/lib-es/network/cached.d.ts +0 -3
  140. package/lib-es/network/cached.d.ts.map +0 -1
  141. package/lib-es/network/cached.js +0 -49
  142. package/lib-es/network/cached.js.map +0 -1
  143. package/lib-es/network/queued.d.ts +0 -3
  144. package/lib-es/network/queued.d.ts.map +0 -1
  145. package/lib-es/network/queued.js +0 -23
  146. package/lib-es/network/queued.js.map +0 -1
  147. package/lib-es/network/traced.d.ts +0 -3
  148. package/lib-es/network/traced.d.ts.map +0 -1
  149. package/lib-es/network/traced.js +0 -76
  150. package/lib-es/network/traced.js.map +0 -1
  151. package/src/cryptoAssetsStore.test.ts +0 -19
  152. package/src/cryptoAssetsStore.ts +0 -16
  153. package/src/network/cached.ts +0 -111
  154. package/src/network/queued.ts +0 -25
  155. package/src/network/traced.ts +0 -89
  156. package/src/tests/preload.unit.test.ts +0 -182
@@ -13,7 +13,7 @@ import type { AccountBridge, AccountLike, CurrencyBridge } from "@ledgerhq/types
13
13
  import type { SolanaAccount, SolanaPreloadDataV1, Transaction, TransactionStatus } from "../types";
14
14
  import { prepareTransaction as prepareTransactionWithAPI } from "../prepareTransaction";
15
15
  import { estimateMaxSpendableWithAPI } from "../estimateMaxSpendable";
16
- import { PRELOAD_MAX_AGE, hydrate, preloadWithAPI } from "../preload";
16
+ import { PRELOAD_MAX_AGE, preloadWithAPI } from "../preload";
17
17
  import { getTransactionStatus } from "../getTransactionStatus";
18
18
  import { getAccountShapeWithAPI } from "../synchronization";
19
19
  import { createTransaction } from "../createTransaction";
@@ -32,33 +32,31 @@ import {
32
32
  assignToTokenAccountRaw,
33
33
  } from "../serialization";
34
34
  import nftResolvers from "../nftResolvers";
35
+ import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
35
36
 
36
- function makePrepare(getChainAPI: (config: Config) => Promise<ChainAPI>) {
37
- const prepareTransaction: AccountBridge<
38
- Transaction,
39
- SolanaAccount
40
- >["prepareTransaction"] = async (mainAccount, transaction) => {
37
+ function makePrepare(getChainAPI: (config: Config) => ChainAPI) {
38
+ const prepareTransaction: AccountBridge<Transaction, SolanaAccount>["prepareTransaction"] = (
39
+ mainAccount,
40
+ transaction,
41
+ ) => {
41
42
  const config: Config = {
42
43
  endpoint: endpointByCurrencyId(mainAccount.currency.id),
43
44
  };
44
45
 
45
- const chainAPI = await getChainAPI(config);
46
+ const chainAPI = getChainAPI(config);
46
47
  return prepareTransactionWithAPI(mainAccount, transaction, chainAPI);
47
48
  };
48
49
 
49
50
  return prepareTransaction;
50
51
  }
51
52
 
52
- function makeSyncAndScan(
53
- getChainAPI: (config: Config) => Promise<ChainAPI>,
54
- getAddress: GetAddressFn,
55
- ) {
56
- const getAccountShape: GetAccountShape<SolanaAccount> = async info => {
53
+ function makeSyncAndScan(getChainAPI: (config: Config) => ChainAPI, getAddress: GetAddressFn) {
54
+ const getAccountShape: GetAccountShape<SolanaAccount> = info => {
57
55
  const config: Config = {
58
56
  endpoint: endpointByCurrencyId(info.currency.id),
59
57
  };
60
58
 
61
- const chainAPI = await getChainAPI(config);
59
+ const chainAPI = getChainAPI(config);
62
60
  return getAccountShapeWithAPI(info, chainAPI);
63
61
  };
64
62
  return {
@@ -67,11 +65,11 @@ function makeSyncAndScan(
67
65
  };
68
66
  }
69
67
 
70
- function makeEstimateMaxSpendable(getChainAPI: (config: Config) => Promise<ChainAPI>) {
68
+ function makeEstimateMaxSpendable(getChainAPI: (config: Config) => ChainAPI) {
71
69
  const estimateMaxSpendable: AccountBridge<
72
70
  Transaction,
73
71
  SolanaAccount
74
- >["estimateMaxSpendable"] = async arg => {
72
+ >["estimateMaxSpendable"] = arg => {
75
73
  const { account, parentAccount } = arg;
76
74
 
77
75
  const currencyId =
@@ -85,7 +83,7 @@ function makeEstimateMaxSpendable(getChainAPI: (config: Config) => Promise<Chain
85
83
  endpoint: endpointByCurrencyId(currencyId),
86
84
  };
87
85
 
88
- const api = await getChainAPI(config);
86
+ const api = getChainAPI(config);
89
87
 
90
88
  return estimateMaxSpendableWithAPI(arg, api);
91
89
  };
@@ -106,38 +104,36 @@ function makeEstimateMaxSpendable(getChainAPI: (config: Config) => Promise<Chain
106
104
  }
107
105
 
108
106
  function makeBroadcast(
109
- getChainAPI: (config: Config) => Promise<ChainAPI>,
107
+ getChainAPI: (config: Config) => ChainAPI,
110
108
  ): AccountBridge<Transaction, SolanaAccount>["broadcast"] {
111
- return async info => {
109
+ return info => {
112
110
  const config: Config = {
113
111
  endpoint: endpointByCurrencyId(info.account.currency.id),
114
112
  };
115
- const api = await getChainAPI(config);
113
+ const api = getChainAPI(config);
116
114
  return broadcastWithAPI(info, api);
117
115
  };
118
116
  }
119
117
 
120
118
  function makeSign(
121
- getChainAPI: (config: Config) => Promise<ChainAPI>,
119
+ getChainAPI: (config: Config) => ChainAPI,
122
120
  signerContext: SignerContext<SolanaSigner>,
123
121
  ): AccountBridge<Transaction, SolanaAccount>["signOperation"] {
124
122
  return info => {
125
123
  const config: Config = {
126
124
  endpoint: endpointByCurrencyId(info.account.currency.id),
127
125
  };
128
- const api = () => getChainAPI(config);
126
+ const api = getChainAPI(config);
129
127
  return buildSignOperation(signerContext, api)(info);
130
128
  };
131
129
  }
132
130
 
133
- function makePreload(
134
- getChainAPI: (config: Config) => Promise<ChainAPI>,
135
- ): CurrencyBridge["preload"] {
131
+ function makePreload(getChainAPI: (config: Config) => ChainAPI): CurrencyBridge["preload"] {
136
132
  const preload: CurrencyBridge["preload"] = (currency): Promise<SolanaPreloadDataV1> => {
137
133
  const config: Config = {
138
134
  endpoint: endpointByCurrencyId(currency.id),
139
135
  };
140
- const api = () => getChainAPI(config);
136
+ const api = getChainAPI(config);
141
137
  return preloadWithAPI(currency, api);
142
138
  };
143
139
  return preload;
@@ -151,29 +147,25 @@ function getPreloadStrategy() {
151
147
 
152
148
  export function makeBridges({
153
149
  getAPI,
154
- getQueuedAPI,
155
- getQueuedAndCachedAPI,
156
150
  signerContext,
157
151
  }: {
158
- getAPI: (config: Config) => Promise<ChainAPI>;
159
- getQueuedAPI: (config: Config) => Promise<ChainAPI>;
160
- getQueuedAndCachedAPI: (config: Config) => Promise<ChainAPI>;
152
+ getAPI: (config: Config) => ChainAPI;
161
153
  signerContext: SignerContext<SolanaSigner>;
162
154
  }): {
163
155
  currencyBridge: CurrencyBridge;
164
156
  accountBridge: AccountBridge<Transaction, SolanaAccount, TransactionStatus>;
165
157
  } {
166
158
  const getAddress = resolver(signerContext);
167
- const { sync, scan } = makeSyncAndScan(getQueuedAPI, getAddress);
159
+ const { sync, scan } = makeSyncAndScan(getAPI, getAddress);
168
160
 
169
161
  const accountBridge: AccountBridge<Transaction, SolanaAccount, TransactionStatus> = {
170
162
  createTransaction,
171
163
  updateTransaction,
172
- estimateMaxSpendable: makeEstimateMaxSpendable(getQueuedAndCachedAPI),
164
+ estimateMaxSpendable: makeEstimateMaxSpendable(getAPI),
173
165
  getTransactionStatus,
174
166
  sync,
175
167
  receive: makeAccountBridgeReceive(getAddress),
176
- prepareTransaction: makePrepare(getQueuedAndCachedAPI),
168
+ prepareTransaction: makePrepare(getAPI),
177
169
  broadcast: makeBroadcast(getAPI),
178
170
  signOperation: makeSign(getAPI, signerContext),
179
171
  signRawOperation: () => {
@@ -189,8 +181,8 @@ export function makeBridges({
189
181
  };
190
182
 
191
183
  const currencyBridge: CurrencyBridge = {
192
- preload: makePreload(getQueuedAndCachedAPI),
193
- hydrate,
184
+ preload: makePreload(getAPI),
185
+ hydrate: (_data: unknown, _currency: CryptoCurrency) => {},
194
186
  scanAccounts: scan,
195
187
  getPreloadStrategy,
196
188
  nftResolvers,
package/src/bridge/js.ts CHANGED
@@ -1,14 +1,10 @@
1
- import { makeLRUCache, minutes } from "@ledgerhq/live-network/cache";
2
1
  import { log } from "@ledgerhq/logs";
3
- import { cached, Config, getChainAPI, queued } from "../network";
4
- import { traced } from "../network/traced";
2
+ import { Config, getChainAPI } from "../network";
5
3
  import { makeBridges } from "./bridge";
6
4
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
7
5
  import { SolanaSigner } from "../signer";
8
6
  import { CoinConfig } from "@ledgerhq/coin-framework/config";
9
7
  import solanaCoinConfig, { SolanaCoinConfig } from "../config";
10
- import { setCryptoAssetsStoreGetter } from "../cryptoAssetsStore";
11
- import { CryptoAssetsStoreGetter } from "@ledgerhq/types-live";
12
8
 
13
9
  const httpRequestLogger = (url: string, options: any) => {
14
10
  log("network", url, {
@@ -18,36 +14,20 @@ const httpRequestLogger = (url: string, options: any) => {
18
14
  });
19
15
  };
20
16
 
21
- const getAPI = makeLRUCache(
22
- (config: Config) => Promise.resolve(traced(getChainAPI(config, httpRequestLogger))),
23
- config => config.endpoint,
24
- minutes(1000),
25
- );
26
-
27
- const getQueuedAPI = makeLRUCache(
28
- (config: Config) =>
29
- getAPI(config).then(api => queued(api, solanaCoinConfig.getCoinConfig().queuedInterval)),
30
- config => config.endpoint,
31
- minutes(1000),
32
- );
33
-
34
- const getQueuedAndCachedAPI = makeLRUCache(
35
- (config: Config) => getQueuedAPI(config).then(cached),
36
- config => config.endpoint,
37
- minutes(1000),
38
- );
39
-
40
17
  export function createBridges(
41
18
  signerContext: SignerContext<SolanaSigner>,
42
19
  coinConfig: CoinConfig<SolanaCoinConfig>,
43
- cryptoAssetsStoreGetter: CryptoAssetsStoreGetter,
44
20
  ) {
45
21
  solanaCoinConfig.setCoinConfig(coinConfig);
46
- setCryptoAssetsStoreGetter(cryptoAssetsStoreGetter);
22
+ const chainAPICache = new Map<string, ReturnType<typeof getChainAPI>>();
47
23
  return makeBridges({
48
- getAPI,
49
- getQueuedAPI,
50
- getQueuedAndCachedAPI,
24
+ getAPI: (config: Config) => {
25
+ const endpoint = config.endpoint;
26
+ if (!chainAPICache.has(endpoint)) {
27
+ chainAPICache.set(endpoint, getChainAPI(config, httpRequestLogger));
28
+ }
29
+ return chainAPICache.get(endpoint)!;
30
+ },
51
31
  signerContext,
52
32
  });
53
33
  }
@@ -3,7 +3,7 @@ import invariant from "invariant";
3
3
  import { getAccountCurrency } from "@ledgerhq/coin-framework/account/index";
4
4
  import { Transaction } from "./types";
5
5
  import { assertUnreachable } from "./utils";
6
- import { getCryptoAssetsStore } from "./cryptoAssetsStore";
6
+ import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
7
7
 
8
8
  const modes = [
9
9
  "send",
package/src/config.ts CHANGED
@@ -2,7 +2,6 @@ import buildCoinConfig, { type CurrencyConfig } from "@ledgerhq/coin-framework/c
2
2
 
3
3
  export type SolanaConfig = {
4
4
  token2022Enabled: boolean;
5
- queuedInterval: number;
6
5
  legacyOCMSMaxVersion: string;
7
6
  };
8
7
 
@@ -10,7 +10,7 @@ import {
10
10
  } from "../types";
11
11
  import { TransferFeeConfigExt } from "../network/chain/account/tokenExtensions";
12
12
  import { PARSED_PROGRAMS } from "../network/chain/program/constants";
13
- import { getCryptoAssetsStore } from "../cryptoAssetsStore";
13
+ import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
14
14
 
15
15
  export async function tokenIsListedOnLedger(currencyId: string, mint: string): Promise<boolean> {
16
16
  const token = await getCryptoAssetsStore().findTokenByAddressInCurrency(mint, currencyId);
@@ -154,44 +154,38 @@ export function getChainAPI(
154
154
  fetch(url, options);
155
155
  };
156
156
 
157
- let _connection: Connection;
158
- const connection = () => {
159
- if (!_connection) {
160
- _connection = new Connection(config.endpoint, {
161
- ...(fetchMiddleware ? { fetchMiddleware } : {}),
162
- fetch: kyNoTimeout as typeof fetch, // Type cast for jest test having an issue with the type
163
- commitment: "confirmed",
164
- confirmTransactionInitialTimeout: getEnv("SOLANA_TX_CONFIRMATION_TIMEOUT") || 0,
165
- });
166
- }
167
- return _connection;
168
- };
157
+ const connection = new Connection(config.endpoint, {
158
+ ...(fetchMiddleware ? { fetchMiddleware } : {}),
159
+ fetch: kyNoTimeout as typeof fetch, // Type cast for jest test having an issue with the type
160
+ commitment: "confirmed",
161
+ confirmTransactionInitialTimeout: getEnv("SOLANA_TX_CONFIRMATION_TIMEOUT") || 0,
162
+ });
169
163
 
170
164
  return {
171
165
  getBalance: (address: string) =>
172
- connection().getBalance(new PublicKey(address)).catch(remapErrors),
166
+ connection.getBalance(new PublicKey(address)).catch(remapErrors),
173
167
 
174
168
  getLatestBlockhash: (commitmentOrConfig?: Commitment | GetLatestBlockhashConfig) =>
175
- connection().getLatestBlockhash(commitmentOrConfig).catch(remapErrors),
169
+ connection.getLatestBlockhash(commitmentOrConfig).catch(remapErrors),
176
170
 
177
171
  getFeeForMessage: (msg: VersionedMessage) =>
178
- connection()
172
+ connection
179
173
  .getFeeForMessage(msg)
180
174
  .then(r => r.value)
181
175
  .catch(remapErrors),
182
176
 
183
177
  getBalanceAndContext: (address: string) =>
184
- connection().getBalanceAndContext(new PublicKey(address)).catch(remapErrors),
178
+ connection.getBalanceAndContext(new PublicKey(address)).catch(remapErrors),
185
179
 
186
180
  getParsedTokenAccountsByOwner: (address: string) =>
187
- connection()
181
+ connection
188
182
  .getParsedTokenAccountsByOwner(new PublicKey(address), {
189
183
  programId: TOKEN_PROGRAM_ID,
190
184
  })
191
185
  .catch(remapErrors),
192
186
 
193
187
  getParsedToken2022AccountsByOwner: (address: string) =>
194
- connection()
188
+ connection
195
189
  .getParsedTokenAccountsByOwner(new PublicKey(address), {
196
190
  programId: TOKEN_2022_PROGRAM_ID,
197
191
  })
@@ -199,7 +193,7 @@ export function getChainAPI(
199
193
 
200
194
  getStakeAccountsByStakeAuth: makeLRUCache(
201
195
  (authAddr: string) =>
202
- connection()
196
+ connection
203
197
  .getParsedProgramAccounts(StakeProgram.programId, {
204
198
  filters: [
205
199
  {
@@ -217,7 +211,7 @@ export function getChainAPI(
217
211
 
218
212
  getStakeAccountsByWithdrawAuth: makeLRUCache(
219
213
  (authAddr: string) =>
220
- connection()
214
+ connection
221
215
  .getParsedProgramAccounts(StakeProgram.programId, {
222
216
  filters: [
223
217
  {
@@ -234,51 +228,48 @@ export function getChainAPI(
234
228
  ),
235
229
 
236
230
  getInflationReward: (addresses: string[]) =>
237
- connection()
238
- .getInflationReward(addresses.map(addr => new PublicKey(addr)))
239
- .catch(remapErrors),
231
+ connection.getInflationReward(addresses.map(addr => new PublicKey(addr))).catch(remapErrors),
240
232
 
241
- getVoteAccounts: () => connection().getVoteAccounts().catch(remapErrors),
233
+ getVoteAccounts: () => connection.getVoteAccounts().catch(remapErrors),
242
234
 
243
235
  getSignaturesForAddress: (address: string, opts?: SignaturesForAddressOptions) => {
244
236
  const callback = () => {
245
- return connection().getSignaturesForAddress(new PublicKey(address), opts);
237
+ return connection.getSignaturesForAddress(new PublicKey(address), opts);
246
238
  };
247
239
  return callback().catch(remapErrorsWithRetry(callback));
248
240
  },
249
241
 
250
242
  getParsedTransactions: (signatures: string[]) =>
251
- connection()
243
+ connection
252
244
  .getParsedTransactions(signatures, {
253
245
  maxSupportedTransactionVersion: 0,
254
246
  })
255
247
  .catch(remapErrors),
256
248
 
257
249
  getAccountInfo: (address: string) =>
258
- connection()
250
+ connection
259
251
  .getParsedAccountInfo(new PublicKey(address))
260
252
  .then(r => r.value)
261
253
  .catch(remapErrors),
262
254
 
263
255
  getMultipleAccounts: (addresses: string[]) =>
264
- connection()
256
+ connection
265
257
  .getMultipleParsedAccounts(addresses.map(address => new PublicKey(address)))
266
258
  .then(r => r.value)
267
259
  .catch(remapErrors),
268
260
 
269
261
  sendRawTransaction: (buffer: Buffer, recentBlockhash?: BlockhashWithExpiryBlockHeight) => {
270
262
  return (async () => {
271
- const conn = connection();
272
263
  const commitment = "confirmed";
273
264
 
274
- const signature = await conn.sendRawTransaction(buffer, {
265
+ const signature = await connection.sendRawTransaction(buffer, {
275
266
  preflightCommitment: commitment,
276
267
  });
277
268
 
278
269
  if (!recentBlockhash) {
279
- recentBlockhash = await conn.getLatestBlockhash(commitment);
270
+ recentBlockhash = await connection.getLatestBlockhash(commitment);
280
271
  }
281
- const { value: status } = await conn.confirmTransaction(
272
+ const { value: status } = await connection.confirmTransaction(
282
273
  {
283
274
  blockhash: recentBlockhash.blockhash,
284
275
  lastValidBlockHeight: recentBlockhash.lastValidBlockHeight,
@@ -312,15 +303,15 @@ export function getChainAPI(
312
303
  },
313
304
 
314
305
  getAssocTokenAccMinNativeBalance: () =>
315
- getMinimumBalanceForRentExemptAccount(connection()).catch(remapErrors),
306
+ getMinimumBalanceForRentExemptAccount(connection).catch(remapErrors),
316
307
 
317
308
  getMinimumBalanceForRentExemption: (dataLength: number) =>
318
- connection().getMinimumBalanceForRentExemption(dataLength).catch(remapErrors),
309
+ connection.getMinimumBalanceForRentExemption(dataLength).catch(remapErrors),
319
310
 
320
- getEpochInfo: () => connection().getEpochInfo().catch(remapErrors),
311
+ getEpochInfo: () => connection.getEpochInfo().catch(remapErrors),
321
312
 
322
313
  getRecentPrioritizationFees: (accounts: string[]) => {
323
- return connection()
314
+ return connection
324
315
  .getRecentPrioritizationFees({
325
316
  lockedWritableAccounts: accounts.map(acc => new PublicKey(acc)),
326
317
  })
@@ -345,7 +336,7 @@ export function getChainAPI(
345
336
  recentBlockhash: PublicKey.default.toString(),
346
337
  }).compileToLegacyMessage(),
347
338
  );
348
- const rpcResponse = await connection().simulateTransaction(testTransaction, {
339
+ const rpcResponse = await connection.simulateTransaction(testTransaction, {
349
340
  replaceRecentBlockhash: true,
350
341
  sigVerify: false,
351
342
  });
@@ -353,6 +344,6 @@ export function getChainAPI(
353
344
  },
354
345
 
355
346
  config,
356
- connection: connection(),
347
+ connection,
357
348
  };
358
349
  }
@@ -2,7 +2,7 @@ import { create } from "superstruct";
2
2
  import { getChainAPI } from ".";
3
3
  import { PARSED_PROGRAMS } from "./program/constants";
4
4
  import { PublicKeyFromString } from "./validators/pubkey";
5
- import { getMaybeTokenAccount } from "./web3";
5
+ import { getMaybeTokenAccount, getTransactions } from "./web3";
6
6
 
7
7
  const api = getChainAPI({
8
8
  endpoint: "https://solana.coin.ledger.com",
@@ -74,3 +74,23 @@ describe("findTokenAccAddress", () => {
74
74
  expect(ata).toEqual(address);
75
75
  });
76
76
  });
77
+
78
+ describe("getTransactions", () => {
79
+ it.each([
80
+ {
81
+ address: "Cv9b7PuxVdKXTKTBXvZSQfSqbMNmPHP8brv77ZL2D95m",
82
+ untilTxSignature: undefined,
83
+ },
84
+ {
85
+ address: "Hj69wRzkrFuf1Nby4yzPEFHdsmQdMoVYjvDKZSLjZFEp",
86
+ untilTxSignature: undefined,
87
+ },
88
+ ])(
89
+ "returns the expected transactions without any fail transactions",
90
+ async ({ address, untilTxSignature }) => {
91
+ const txs = await getTransactions(address, untilTxSignature, api);
92
+ const hasError = txs.some(tx => tx.info.err);
93
+ expect(hasError).toBe(false);
94
+ },
95
+ );
96
+ });
@@ -122,12 +122,18 @@ async function* getTransactionsBatched(
122
122
  const transactions = await api.getParsedTransactions(
123
123
  signaturesInfoBatch.map(tx => tx.signature),
124
124
  );
125
- const txsDetails = transactions.reduce((acc, tx, index) => {
125
+ const sortedTransactions = transactions.sort((a, b) => (b?.slot ?? 0) - (a?.slot ?? 0));
126
+ const txsDetails = sortedTransactions.reduce((acc, tx) => {
126
127
  if (tx && !tx.meta?.err && tx.blockTime) {
127
- acc.push({
128
- info: signaturesInfoBatch[index],
129
- parsed: tx,
130
- });
128
+ const info = signaturesInfoBatch.find(s =>
129
+ tx.transaction.signatures.includes(s.signature),
130
+ )!;
131
+ if (info) {
132
+ acc.push({
133
+ info,
134
+ parsed: tx,
135
+ });
136
+ }
131
137
  }
132
138
  return acc;
133
139
  }, [] as TransactionDescriptor[]);
@@ -1,4 +1,2 @@
1
1
  export * from "./chain/index";
2
- export { cached } from "./cached";
3
- export { queued } from "./queued";
4
2
  export { logged } from "./logged";
@@ -1,5 +1,5 @@
1
1
  import { getEnv } from "@ledgerhq/live-env";
2
- import network from "@ledgerhq/live-network/network";
2
+ import network from "@ledgerhq/live-network";
3
3
  import { NFTCollectionMetadataResponse, NFTMetadataResponse } from "@ledgerhq/types-live";
4
4
  import { CollectionMetadataInput, NftMetadataInput } from "@ledgerhq/coin-framework/nft/types";
5
5
  import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
@@ -6,7 +6,6 @@ const initialData: SolanaPreloadDataV1 = {
6
6
  version: "1",
7
7
  validatorsWithMeta: [],
8
8
  validators: [],
9
- splTokens: null,
10
9
  };
11
10
 
12
11
  const dataByCurrency = new Map<string, SolanaPreloadDataV1>([
@@ -1,14 +1,123 @@
1
+ /* eslint-disable @typescript-eslint/consistent-type-assertions */
2
+
1
3
  import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
2
- import { hydrate } from "./preload";
3
- import { SolanaPreloadData } from "./types";
4
-
5
- describe("hydrate", () => {
6
- it.each([undefined, null, {}, { version: null }, { version: undefined }, {}])(
7
- "should return undefined if data is corrupted (%p value)",
8
- value => {
9
- expect(hydrate(value as unknown as SolanaPreloadData, {} as CryptoCurrency)).toEqual(
10
- undefined,
11
- );
12
- },
13
- );
4
+ import { preloadWithAPI } from "./preload";
5
+ import { ChainAPI } from "./network";
6
+ import { getValidators, ValidatorsAppValidator } from "./network/validator-app";
7
+ import { Cluster } from "@solana/web3.js";
8
+ import { LEDGER_VALIDATOR_BY_CHORUS_ONE, LEDGER_VALIDATOR_BY_FIGMENT } from "./utils";
9
+ import { getSolanaPreloadData } from "./preload-data";
10
+ import { firstValueFrom } from "rxjs";
11
+
12
+ jest.mock("./network/validator-app");
13
+
14
+ const mockedGetValidators = jest.mocked(getValidators);
15
+
16
+ describe("preloadWithAPI", () => {
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ it("should return correctly the preloaded data for solana", async () => {
22
+ const validators: ValidatorsAppValidator[] = [
23
+ {
24
+ activeStake: 0,
25
+ commission: 1,
26
+ totalScore: 2,
27
+ voteAccount: "some random account",
28
+ },
29
+ {
30
+ activeStake: 3,
31
+ commission: 4,
32
+ totalScore: 5,
33
+ voteAccount: "some random account 2",
34
+ },
35
+ ];
36
+ mockedGetValidators.mockImplementationOnce((_cluster: Cluster) => Promise.resolve(validators));
37
+
38
+ const currency = {
39
+ id: "solana",
40
+ } as unknown as CryptoCurrency;
41
+
42
+ const result = await preloadWithAPI(currency, {} as unknown as ChainAPI);
43
+ expect(result).toEqual({
44
+ validators: expect.arrayContaining([
45
+ ...validators,
46
+ LEDGER_VALIDATOR_BY_FIGMENT,
47
+ LEDGER_VALIDATOR_BY_CHORUS_ONE,
48
+ ]),
49
+ validatorsWithMeta: [],
50
+ version: "1",
51
+ });
52
+
53
+ const solanaPreloadedData = await firstValueFrom(getSolanaPreloadData(currency));
54
+ expect(solanaPreloadedData).toEqual(result);
55
+ });
56
+
57
+ it("should return correctly the preloaded data for solana_devnet", async () => {
58
+ const voteAccount = {
59
+ activatedStake: 6,
60
+ commission: 7,
61
+ votePubkey: "some random account from account",
62
+ };
63
+
64
+ const api = {
65
+ getVoteAccounts: () =>
66
+ Promise.resolve({
67
+ current: [voteAccount],
68
+ }),
69
+ } as unknown as ChainAPI;
70
+
71
+ const currency = {
72
+ id: "solana_devnet",
73
+ } as unknown as CryptoCurrency;
74
+ const result = await preloadWithAPI(currency, api);
75
+ expect(result).toEqual({
76
+ validators: expect.arrayContaining([
77
+ {
78
+ activeStake: voteAccount.activatedStake,
79
+ commission: voteAccount.commission,
80
+ voteAccount: voteAccount.votePubkey,
81
+ totalScore: 0,
82
+ },
83
+ ]),
84
+ validatorsWithMeta: [],
85
+ version: "1",
86
+ });
87
+
88
+ const solanaPreloadedData = await firstValueFrom(getSolanaPreloadData(currency));
89
+ expect(solanaPreloadedData).toEqual(result);
90
+ });
91
+
92
+ it("should return correctly the preloaded data for solana_testnet", async () => {
93
+ const validators: ValidatorsAppValidator[] = [
94
+ {
95
+ activeStake: 0,
96
+ commission: 1,
97
+ totalScore: 2,
98
+ voteAccount: "some random account",
99
+ },
100
+ {
101
+ activeStake: 3,
102
+ commission: 4,
103
+ totalScore: 5,
104
+ voteAccount: "some random account 2",
105
+ },
106
+ ];
107
+ mockedGetValidators.mockImplementationOnce((_cluster: Cluster) => Promise.resolve(validators));
108
+
109
+ const currency = {
110
+ id: "solana_testnet",
111
+ } as unknown as CryptoCurrency;
112
+
113
+ const result = await preloadWithAPI(currency, {} as unknown as ChainAPI);
114
+ expect(result).toEqual({
115
+ validators: expect.arrayContaining(validators),
116
+ validatorsWithMeta: [],
117
+ version: "1",
118
+ });
119
+
120
+ const solanaPreloadedData = await firstValueFrom(getSolanaPreloadData(currency));
121
+ expect(solanaPreloadedData).toEqual(result);
122
+ });
14
123
  });