@exodus/solana-api 3.11.8 → 3.11.9

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,18 @@
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.11.9](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.11.8...@exodus/solana-api@3.11.9) (2024-12-31)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: include rentExemptAmount in balance calculation (#4738)
13
+
14
+ * fix: integration tests (#4720)
15
+
16
+
17
+
6
18
  ## [3.11.8](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.11.6...@exodus/solana-api@3.11.8) (2024-12-10)
7
19
 
8
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "3.11.8",
3
+ "version": "3.11.9",
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",
@@ -30,7 +30,7 @@
30
30
  "@exodus/fetch": "^1.2.0",
31
31
  "@exodus/models": "^12.0.1",
32
32
  "@exodus/simple-retry": "^0.0.6",
33
- "@exodus/solana-lib": "^3.9.1",
33
+ "@exodus/solana-lib": "^3.9.3",
34
34
  "@exodus/solana-meta": "^2.0.2",
35
35
  "@exodus/timer": "^1.1.1",
36
36
  "bn.js": "^4.11.0",
@@ -47,7 +47,7 @@
47
47
  "@exodus/assets-testing": "^1.0.0",
48
48
  "@exodus/solana-web3.js": "^1.63.1-exodus.9-rc3"
49
49
  },
50
- "gitHead": "46f31ffb8a59f043ca4bd3855c88d6640ca35a1d",
50
+ "gitHead": "44b274a6a6f0228014aa0eaac5af850dcaf76fed",
51
51
  "bugs": {
52
52
  "url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Asolana-api"
53
53
  },
@@ -19,6 +19,7 @@ export const createAccountState = ({ assetList }) => {
19
19
  cursor: '',
20
20
  balance: asset.currency.ZERO,
21
21
  tokenBalances: Object.create(null),
22
+ rentExemptAmount: asset.currency.ZERO,
22
23
  stakingInfo: {
23
24
  loaded: false,
24
25
  staking: {
package/src/api.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  import BN from 'bn.js'
18
18
  import lodash from 'lodash'
19
19
  import assert from 'minimalistic-assert'
20
+ import ms from 'ms'
20
21
  import urljoin from 'url-join'
21
22
  import wretch from 'wretch'
22
23
 
@@ -46,6 +47,12 @@ export class Api {
46
47
  const result = await this.rpcCall('getTokenSupply', [mintAddress])
47
48
  return result?.value?.amount
48
49
  })
50
+
51
+ this.getMinimumBalanceForRentExemption = memoize(
52
+ (accountSize) => this.rpcCall('getMinimumBalanceForRentExemption', [accountSize]),
53
+ (accountSize) => accountSize,
54
+ ms('15m')
55
+ )
49
56
  }
50
57
 
51
58
  setServer(rpcUrl) {
@@ -876,10 +883,6 @@ export class Api {
876
883
  }, 0)
877
884
  }
878
885
 
879
- async getMinimumBalanceForRentExemption(size) {
880
- return this.rpcCall('getMinimumBalanceForRentExemption', [size])
881
- }
882
-
883
886
  async getProgramAccounts(programId, config) {
884
887
  return this.rpcCall('getProgramAccounts', [programId, config])
885
888
  }
@@ -33,7 +33,18 @@ export const getBalancesFactory =
33
33
  .clampLowerZero()
34
34
 
35
35
  const total = stakingFeatureAvailable ? balance : balanceWithoutStaking
36
- const spendable = balanceWithoutStaking.sub(asset.accountReserve || zero).clampLowerZero()
36
+
37
+ const networkReserve = accountState.rentExemptAmount || zero
38
+
39
+ const accountReserve = asset.accountReserve || zero
40
+
41
+ // there is no wallet reserve when there are no tokens nor staking actions. Just network reserve for the rent exempt amount.
42
+ const walletReserve =
43
+ hasStakedFunds({ locked, withdrawable, pending }) || hasTokensBalance({ accountState })
44
+ ? accountReserve.sub(networkReserve).clampLowerZero()
45
+ : zero
46
+
47
+ const spendable = balanceWithoutStaking.sub(walletReserve).sub(networkReserve).clampLowerZero()
37
48
 
38
49
  const staked = locked
39
50
  const unstaking = pending
@@ -47,6 +58,8 @@ export const getBalancesFactory =
47
58
  spendable,
48
59
  staked,
49
60
  unstaking,
61
+ networkReserve,
62
+ walletReserve,
50
63
  }
51
64
  }
52
65
 
@@ -93,3 +106,9 @@ const getBalanceFromAccountState = ({ asset, accountState }) => {
93
106
  asset.currency.ZERO
94
107
  )
95
108
  }
109
+
110
+ const hasStakedFunds = ({ locked, withdrawable, pending }) =>
111
+ [locked, withdrawable, pending].some((amount) => amount.isPositive)
112
+
113
+ const hasTokensBalance = ({ accountState }) =>
114
+ Object.values(accountState?.tokenBalances || {}).some((balance) => balance.isPositive)
@@ -294,10 +294,18 @@ export class SolanaMonitor extends BaseMonitor {
294
294
 
295
295
  async getAccount({ refresh, address, tokenAccounts, accountState, walletAccount }) {
296
296
  const tokens = Object.keys(this.assets).filter((name) => name !== this.asset.name)
297
- const [solBalance, splBalances] = await Promise.all([
298
- this.api.getBalance(address),
299
- this.api.getTokensBalance({ address, filterByTokens: tokens, tokenAccounts }),
300
- ])
297
+ const accountInfo = await this.api.getAccountInfo(address).catch(() => {})
298
+ const accountSize = accountInfo?.space || 0
299
+ const solBalance = accountInfo?.lamports || 0
300
+
301
+ const rentExemptValue = await this.api.getMinimumBalanceForRentExemption(accountSize)
302
+ const rentExemptAmount = this.asset.currency.baseUnit(rentExemptValue)
303
+
304
+ const splBalances = await this.api.getTokensBalance({
305
+ address,
306
+ filterByTokens: tokens,
307
+ tokenAccounts,
308
+ })
301
309
 
302
310
  const tokenBalances = _.mapValues(splBalances, (balance, name) =>
303
311
  this.assets[name].currency.baseUnit(balance).toDefault()
@@ -334,14 +342,21 @@ export class SolanaMonitor extends BaseMonitor {
334
342
  account: {
335
343
  balance,
336
344
  tokenBalances,
345
+ rentExemptAmount,
337
346
  },
338
347
  staking,
339
348
  }
340
349
  }
341
350
 
342
351
  async updateState({ account, cursorState, walletAccount, staking }) {
343
- const { balance, tokenBalances } = account
344
- const newData = { balance, tokenBalances, stakingInfo: staking, ...cursorState }
352
+ const { balance, tokenBalances, rentExemptAmount } = account
353
+ const newData = {
354
+ balance,
355
+ rentExemptAmount,
356
+ tokenBalances,
357
+ stakingInfo: staking,
358
+ ...cursorState,
359
+ }
345
360
  return this.updateAccountState({ newData, walletAccount })
346
361
  }
347
362
 
package/src/tx-send.js CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  TOKEN_2022_PROGRAM_ID,
6
6
  TOKEN_PROGRAM_ID,
7
7
  } from '@exodus/solana-lib'
8
- import { transactionToBase58 } from '@exodus/solana-lib/src/tx/common.js'
9
8
  import assert from 'minimalistic-assert'
10
9
 
11
10
  export const createAndBroadcastTXFactory =
@@ -34,6 +33,7 @@ export const createAndBroadcastTXFactory =
34
33
  expectedMintAddress,
35
34
  metadataAddress,
36
35
  creators,
36
+ priorityFee,
37
37
  // </MagicEden>
38
38
  reference,
39
39
  memo,
@@ -129,7 +129,7 @@ export const createAndBroadcastTXFactory =
129
129
  from,
130
130
  to: address,
131
131
  amount,
132
- fee: feeAmount,
132
+ fee: feeData.fee, // feeAmount includes the priortyFee
133
133
  recentBlockhash,
134
134
  feeData,
135
135
  reference,
@@ -139,26 +139,14 @@ export const createAndBroadcastTXFactory =
139
139
  ...magicEdenParams,
140
140
  })
141
141
 
142
- let { priorityFee } = feeData
143
-
144
142
  const transactionForFeeEstimation = prepareForSigning(unsignedTransaction)
145
143
 
146
- if (!priorityFee) {
147
- try {
148
- priorityFee = await api.getPriorityFee(transactionToBase58(transactionForFeeEstimation))
149
- } catch (e) {
150
- console.warn(`Failed to fetch priority fee: ${e.message}`)
151
- priorityFee = feeData.fallbackPriorityFee
152
- }
153
- }
154
-
155
144
  const { unitsConsumed: computeUnits, err } = await api.simulateUnsignedTransaction({
156
145
  message: transactionForFeeEstimation.message,
157
146
  })
147
+ if (err) throw new Error(JSON.stringify(err))
158
148
 
159
- if (err) throw new Error(err)
160
-
161
- unsignedTransaction.txData.priorityFee = priorityFee
149
+ unsignedTransaction.txData.priorityFee = priorityFee ?? 0
162
150
  unsignedTransaction.txData.computeUnits = computeUnits * feeData.computeUnitsMultiplier
163
151
 
164
152
  const { txId, rawTx } = await assetClientInterface.signTransaction({