@exodus/solana-api 3.26.0 → 3.26.2

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.26.2](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.26.1...@exodus/solana-api@3.26.2) (2025-12-15)
7
+
8
+ **Note:** Version bump only for package @exodus/solana-api
9
+
10
+
11
+
12
+
13
+
14
+ ## [3.26.1](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.26.0...@exodus/solana-api@3.26.1) (2025-12-12)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+
20
+ * fix: remove unused SOL RPC call (#7107)
21
+
22
+ * fix: SOL pending sent txs (#7105)
23
+
24
+ * fix: use batch operations for Solana monitors (#7085)
25
+
26
+
27
+
6
28
  ## [3.26.0](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.25.4...@exodus/solana-api@3.26.0) (2025-12-08)
7
29
 
8
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "3.26.0",
3
+ "version": "3.26.2",
4
4
  "description": "Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Solana",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -49,7 +49,7 @@
49
49
  "@exodus/assets-testing": "^1.0.0",
50
50
  "@exodus/solana-web3.js": "^1.63.1-exodus.9-rc3"
51
51
  },
52
- "gitHead": "79ca0050003451552fa7bf075913b87e098885c8",
52
+ "gitHead": "937ac1707d279c2dda93b162586b5a596236ce08",
53
53
  "bugs": {
54
54
  "url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Asolana-api"
55
55
  },
package/src/api.js CHANGED
@@ -127,14 +127,6 @@ export class Api {
127
127
  ])
128
128
  }
129
129
 
130
- async getPriorityFee(transaction) {
131
- // https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api
132
- const result = await this.rpcCall('getPriorityFeeEstimate', [
133
- { transaction, options: { recommended: true } },
134
- ])
135
- return result.priorityFeeEstimate
136
- }
137
-
138
130
  async getBalance(address) {
139
131
  const result = await this.rpcCall('getBalance', [address, { encoding: 'jsonParsed' }])
140
132
  return lodash.get(result, 'value', 0)
@@ -24,6 +24,7 @@ export const createTxFactory = ({ assetClientInterface, api, feePayerClient }) =
24
24
  feeData: providedFeeData,
25
25
  fromAddress: providedFromAddress,
26
26
  toAddress,
27
+ address,
27
28
  amount,
28
29
  reference,
29
30
  memo,
@@ -56,6 +57,8 @@ export const createTxFactory = ({ assetClientInterface, api, feePayerClient }) =
56
57
  assert(asset, 'asset is required')
57
58
  assert(walletAccount, 'walletAccount is required')
58
59
 
60
+ toAddress = toAddress ?? address
61
+
59
62
  let tokenParams = Object.create(null)
60
63
 
61
64
  const baseAsset = asset.baseAsset
package/src/rpc-api.js CHANGED
@@ -112,14 +112,6 @@ export class RpcApi {
112
112
  return result?.value?.blockhash
113
113
  }
114
114
 
115
- async getPriorityFee(transaction) {
116
- // https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api
117
- const result = await this.rpcCall('getPriorityFeeEstimate', [
118
- { transaction, options: { recommended: true } },
119
- ])
120
- return result.priorityFeeEstimate
121
- }
122
-
123
115
  async getBlockTime(slot) {
124
116
  // might result in error if executed on a validator with partial ledger (https://github.com/solana-labs/solana/issues/12413)
125
117
  return this.rpcCall('getBlockTime', [slot])
@@ -128,13 +128,22 @@ export class SolanaClarityMonitor extends BaseMonitor {
128
128
  const isHistoryUpdateTick =
129
129
  this.tickCount[walletAccount] % this.ticksBetweenHistoryFetches === 0
130
130
 
131
- const shouldUpdateHistory = refresh || isHistoryUpdateTick || balanceChanged
131
+ const baseAssetTxLog = await this.aci.getTxLog({
132
+ assetName,
133
+ walletAccount,
134
+ })
135
+ const hasUnconfirmedSentTx = [...baseAssetTxLog].some((tx) => tx.pending && tx.sent)
136
+
137
+ const shouldUpdateHistory =
138
+ refresh || isHistoryUpdateTick || balanceChanged || hasUnconfirmedSentTx
132
139
  const shouldUpdateOnlyBalance = balanceChanged && !shouldUpdateHistory
133
140
 
141
+ // start a batch
142
+ const batch = this.aci.createOperationsBatch()
134
143
  // getHistory is more likely to fail/be rate limited, so we want to update users balance only on a lot of ticks
135
144
  if (this.shouldUpdateBalanceBeforeHistory || shouldUpdateOnlyBalance) {
136
145
  // update all state at once
137
- await this.updateState({ account, walletAccount, staking })
146
+ this.updateState({ account, walletAccount, staking, batch })
138
147
  await this.emitUnknownTokensEvent({ tokenAccounts })
139
148
  }
140
149
 
@@ -151,13 +160,21 @@ export class SolanaClarityMonitor extends BaseMonitor {
151
160
 
152
161
  // update all state at once
153
162
  const clearedLogItems = await this.markStaleTransactions({ walletAccount, logItemsByAsset })
154
- await this.updateTxLogByAsset({ walletAccount, logItemsByAsset: clearedLogItems, refresh })
155
- await this.updateState({ account, cursorState, walletAccount, staking })
163
+ this.updateTxLogByAssetBatch({
164
+ logItemsByAsset: clearedLogItems,
165
+ walletAccount,
166
+ refresh,
167
+ batch,
168
+ })
169
+ this.updateState({ account, cursorState, walletAccount, staking, batch })
156
170
  await this.emitUnknownTokensEvent({ tokenAccounts })
157
171
  if (refresh || cursorChanged) {
158
172
  this.cursors[walletAccount] = cursorState.cursor
159
173
  }
160
174
  }
175
+
176
+ // close batch
177
+ await this.aci.executeOperationsBatch(batch)
161
178
  }
162
179
 
163
180
  async getHistory({ address, accountState, refresh, tokenAccounts } = Object.create(null)) {
@@ -319,7 +336,8 @@ export class SolanaClarityMonitor extends BaseMonitor {
319
336
  }
320
337
  }
321
338
 
322
- async updateState({ account, cursorState = {}, walletAccount, staking }) {
339
+ updateState({ account, cursorState = {}, walletAccount, staking, batch }) {
340
+ const assetName = this.asset.name
323
341
  const { balance, tokenBalances, rentExemptAmount, accountSize, ownerChanged } = account
324
342
  const newData = {
325
343
  balance,
@@ -330,7 +348,7 @@ export class SolanaClarityMonitor extends BaseMonitor {
330
348
  stakingInfo: staking,
331
349
  ...cursorState,
332
350
  }
333
- return this.updateAccountState({ newData, walletAccount })
351
+ return this.updateAccountStateBatch({ assetName, walletAccount, newData, batch })
334
352
  }
335
353
 
336
354
  async getStakingInfo({ address, accountState, walletAccount }) {
@@ -170,10 +170,12 @@ export class SolanaMonitor extends BaseMonitor {
170
170
  const shouldUpdateHistory = refresh || isHistoryUpdateTick || balanceChanged
171
171
  const shouldUpdateOnlyBalance = balanceChanged && !shouldUpdateHistory
172
172
 
173
+ // start a batch
174
+ const batch = this.aci.createOperationsBatch()
173
175
  // getHistory is more likely to fail/be rate limited, so we want to update users balance only on a lot of ticks
174
176
  if (this.shouldUpdateBalanceBeforeHistory || shouldUpdateOnlyBalance) {
175
177
  // update all state at once
176
- await this.updateState({ account, walletAccount, staking })
178
+ this.updateState({ account, walletAccount, staking, batch })
177
179
  await this.emitUnknownTokensEvent({ tokenAccounts })
178
180
  }
179
181
 
@@ -190,13 +192,21 @@ export class SolanaMonitor extends BaseMonitor {
190
192
 
191
193
  // update all state at once
192
194
  const clearedLogItems = await this.markStaleTransactions({ walletAccount, logItemsByAsset })
193
- await this.updateTxLogByAsset({ walletAccount, logItemsByAsset: clearedLogItems, refresh })
194
- await this.updateState({ account, cursorState, walletAccount, staking })
195
+ this.updateTxLogByAssetBatch({
196
+ logItemsByAsset: clearedLogItems,
197
+ walletAccount,
198
+ refresh,
199
+ batch,
200
+ })
201
+ this.updateState({ account, cursorState, walletAccount, staking, batch })
195
202
  await this.emitUnknownTokensEvent({ tokenAccounts })
196
203
  if (refresh || cursorChanged) {
197
204
  this.cursors[walletAccount] = cursorState.cursor
198
205
  }
199
206
  }
207
+
208
+ // close batch
209
+ await this.aci.executeOperationsBatch(batch)
200
210
  }
201
211
 
202
212
  async getHistory({ address, accountState, refresh, tokenAccounts } = Object.create(null)) {
@@ -352,7 +362,8 @@ export class SolanaMonitor extends BaseMonitor {
352
362
  }
353
363
  }
354
364
 
355
- async updateState({ account, cursorState = {}, walletAccount, staking }) {
365
+ updateState({ account, cursorState = {}, walletAccount, staking, batch }) {
366
+ const assetName = this.asset.name
356
367
  const { balance, tokenBalances, rentExemptAmount, accountSize, ownerChanged } = account
357
368
  const newData = {
358
369
  balance,
@@ -363,7 +374,7 @@ export class SolanaMonitor extends BaseMonitor {
363
374
  stakingInfo: staking,
364
375
  ...cursorState,
365
376
  }
366
- return this.updateAccountState({ newData, walletAccount })
377
+ return this.updateAccountStateBatch({ assetName, walletAccount, newData, batch })
367
378
  }
368
379
 
369
380
  async getStakingInfo({ address, accountState, walletAccount }) {
package/src/tx-send.js CHANGED
@@ -6,26 +6,13 @@ export const createAndBroadcastTXFactory = ({ api, assetClientInterface }) => {
6
6
  assert(assetClientInterface, 'assetClientInterface is required')
7
7
  assert(api, 'api is required')
8
8
 
9
- return async ({ asset, walletAccount, unsignedTx: predefinedUnsignedTx, ...legacyParams }) => {
9
+ return async ({ asset, walletAccount, unsignedTx: providedUnsignedTx, ...legacyParams }) => {
10
10
  const assetName = asset.name
11
11
  const baseAsset = asset.baseAsset
12
12
 
13
- const resolveTxs = async () => {
14
- if (predefinedUnsignedTx) {
15
- return predefinedUnsignedTx
16
- }
17
-
18
- return baseAsset.api.createTx({
19
- asset,
20
- walletAccount,
21
- amount: legacyParams.amount,
22
- toAddress: legacyParams.address,
23
- ...legacyParams,
24
- ...legacyParams.options,
25
- })
26
- }
27
-
28
- const unsignedTx = await resolveTxs()
13
+ const { unsignedTx } = providedUnsignedTx
14
+ ? { unsignedTx: providedUnsignedTx }
15
+ : await baseAsset.api.createTx({ asset, walletAccount, ...legacyParams })
29
16
 
30
17
  const signedTx = await assetClientInterface.signTransaction({
31
18
  assetName: baseAsset.name,