@exodus/solana-api 3.11.7 → 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 +30 -0
- package/README.md +5 -1
- package/package.json +6 -6
- package/src/account-state.js +1 -0
- package/src/api.js +11 -8
- package/src/get-balances.js +20 -1
- package/src/tx-log/solana-monitor.js +21 -6
- package/src/tx-send.js +6 -16
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
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
|
+
|
|
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)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
* fix: prevent invalid account owner (#4485)
|
|
25
|
+
|
|
26
|
+
* fix: SOL rename deprecated methods (#4512)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### License
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
* license: re-license under MIT license (#4515)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
6
36
|
## [3.11.7](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.11.6...@exodus/solana-api@3.11.7) (2024-11-06)
|
|
7
37
|
|
|
8
38
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @exodus/solana-api
|
|
2
|
+
|
|
3
|
+
Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Solana. See [Asset Packages](../../docs/asset-packages.md) for more detail on this package's role.
|
|
4
|
+
|
|
5
|
+
## Known Issues
|
|
2
6
|
|
|
3
7
|
- To get all transactions data from an address we gotta call 3 rpcs `getSignaturesForAddress` (get txIds) -> `getTransaction` (get tx details) -> `getBlockTime` (get tx timestamp). Pretty annoying and resource-consuming backend-side. (https://github.com/solana-labs/solana/issues/12411)
|
|
4
8
|
- calling `getBlockTime` might results in an error if the slot/block requested is too old (https://github.com/solana-labs/solana/issues/12413), looks like some Solana validators can choose to not keep all the ledger blocks (fix in progress by solana team).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-api",
|
|
3
|
-
"version": "3.11.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.11.9",
|
|
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",
|
|
7
7
|
"files": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"author": "Exodus Movement, Inc.",
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"publishConfig": {
|
|
17
|
-
"access": "
|
|
17
|
+
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"test": "run -T exodus-test --jest",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"@exodus/asset-lib": "^5.0.0",
|
|
27
27
|
"@exodus/assets": "^11.0.0",
|
|
28
28
|
"@exodus/basic-utils": "^3.0.1",
|
|
29
|
-
"@exodus/currency": "^
|
|
29
|
+
"@exodus/currency": "^6.0.1",
|
|
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.
|
|
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": "
|
|
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
|
},
|
package/src/account-state.js
CHANGED
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) {
|
|
@@ -137,7 +144,7 @@ export class Api {
|
|
|
137
144
|
async getRecentBlockHash(commitment) {
|
|
138
145
|
const result = await this.rpcCall(
|
|
139
146
|
'getLatestBlockhash',
|
|
140
|
-
[{ commitment: commitment || '
|
|
147
|
+
[{ commitment: commitment || 'confirmed', encoding: 'jsonParsed' }],
|
|
141
148
|
{ forceHttp: true }
|
|
142
149
|
)
|
|
143
150
|
return lodash.get(result, 'value.blockhash')
|
|
@@ -171,7 +178,7 @@ export class Api {
|
|
|
171
178
|
return this.rpcCall('getBlockTime', [slot])
|
|
172
179
|
}
|
|
173
180
|
|
|
174
|
-
async
|
|
181
|
+
async getSignaturesForAddress(address, { until, before, limit } = {}) {
|
|
175
182
|
until = until || undefined
|
|
176
183
|
return this.rpcCall('getSignaturesForAddress', [address, { until, before, limit }], { address })
|
|
177
184
|
}
|
|
@@ -195,7 +202,7 @@ export class Api {
|
|
|
195
202
|
|
|
196
203
|
const txsResultsByAccount = await Promise.all(
|
|
197
204
|
accountsToCheck.map((addr) =>
|
|
198
|
-
this.
|
|
205
|
+
this.getSignaturesForAddress(addr, {
|
|
199
206
|
until,
|
|
200
207
|
before,
|
|
201
208
|
limit,
|
|
@@ -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
|
}
|
|
@@ -1046,7 +1049,7 @@ export class Api {
|
|
|
1046
1049
|
).toString('base64')
|
|
1047
1050
|
const { accounts, unitsConsumed, err } = await this.simulateTransaction(encodedTransaction, {
|
|
1048
1051
|
...config,
|
|
1049
|
-
replaceRecentBlockhash:
|
|
1052
|
+
replaceRecentBlockhash: false,
|
|
1050
1053
|
sigVerify: false,
|
|
1051
1054
|
})
|
|
1052
1055
|
return {
|
package/src/get-balances.js
CHANGED
|
@@ -33,7 +33,18 @@ export const getBalancesFactory =
|
|
|
33
33
|
.clampLowerZero()
|
|
34
34
|
|
|
35
35
|
const total = stakingFeatureAvailable ? balance : balanceWithoutStaking
|
|
36
|
-
|
|
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
|
|
298
|
-
|
|
299
|
-
|
|
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 = {
|
|
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,25 +139,15 @@ export const createAndBroadcastTXFactory =
|
|
|
139
139
|
...magicEdenParams,
|
|
140
140
|
})
|
|
141
141
|
|
|
142
|
-
let { priorityFee } = feeData
|
|
143
|
-
|
|
144
142
|
const transactionForFeeEstimation = prepareForSigning(unsignedTransaction)
|
|
145
143
|
|
|
146
|
-
|
|
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
|
-
const { unitsConsumed: computeUnits } = await api.simulateUnsignedTransaction({
|
|
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
|
-
unsignedTransaction.txData.priorityFee = priorityFee
|
|
160
|
-
unsignedTransaction.txData.computeUnits = computeUnits
|
|
149
|
+
unsignedTransaction.txData.priorityFee = priorityFee ?? 0
|
|
150
|
+
unsignedTransaction.txData.computeUnits = computeUnits * feeData.computeUnitsMultiplier
|
|
161
151
|
|
|
162
152
|
const { txId, rawTx } = await assetClientInterface.signTransaction({
|
|
163
153
|
assetName: baseAsset.name,
|