@exodus/solana-api 3.13.7 → 3.14.1

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,26 @@
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.14.1](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.14.0...@exodus/solana-api@3.14.1) (2025-03-11)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: prevent SOL invalid account owner error (#5187)
13
+
14
+
15
+
16
+ ## [3.14.0](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.13.7...@exodus/solana-api@3.14.0) (2025-03-10)
17
+
18
+
19
+ ### Features
20
+
21
+
22
+ * feat: SOL internal sends in txLog (#5199)
23
+
24
+
25
+
6
26
  ## [3.13.7](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.13.6...@exodus/solana-api@3.13.7) (2025-03-03)
7
27
 
8
28
 
package/README.md CHANGED
@@ -1,8 +1,24 @@
1
- # @exodus/solana-api
1
+ # @exodus/solana-api · [![npm version](https://img.shields.io/badge/npm-public-blue.svg?style=flat)](https://www.npmjs.com/package/@exodus/solana-api)
2
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.
3
+ The **solana-api** provides utilities for interacting with the Solana blockchain.
4
+ It offers functionality to query transaction data, monitor network health, and broadcast transactions, specifically:
4
5
 
5
- ## Known Issues
6
+ - Provides an interface for communicating with the Solana backend, exposing methods to fetch transaction and account details, check node health, query staked amounts, and retrieve withdrawal histories.
7
+ - Includes functions for broadcasting transactions to the Solana network.
8
+ - Monitors transactions, retrieves staking data, and logs transaction history, ensuring the wallet’s state remains in sync with on-chain data.
6
9
 
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)
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).
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ Install the package via `yarn`:
15
+
16
+ ```bash
17
+ yarn add @exodus/solana-api
18
+ ```
19
+
20
+ ## License
21
+
22
+ This project is licensed under the MIT License.
23
+ You are free to use, modify, and distribute this software under the terms of the MIT License.
24
+ For more details, see the [LICENSE](LICENSE) file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "3.13.7",
3
+ "version": "3.14.1",
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",
@@ -46,7 +46,7 @@
46
46
  "@exodus/assets-testing": "^1.0.0",
47
47
  "@exodus/solana-web3.js": "^1.63.1-exodus.9-rc3"
48
48
  },
49
- "gitHead": "710aac6a67806530fc11b56ac73c3ddb7808855a",
49
+ "gitHead": "83588a56196a14b58f5b05aa2097e4ef6c54079e",
50
50
  "bugs": {
51
51
  "url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Asolana-api"
52
52
  },
package/src/api.js CHANGED
@@ -22,7 +22,7 @@ import urljoin from 'url-join'
22
22
 
23
23
  import { Connection } from './connection.js'
24
24
  import { getStakeActivation } from './get-stake-activation/index.js'
25
- import { isSplTransferInstruction } from './txs-utils.js'
25
+ import { isSolTransferInstruction, isSplTransferInstruction } from './txs-utils.js'
26
26
 
27
27
  const createApi = createApiCJS.default || createApiCJS
28
28
 
@@ -449,7 +449,11 @@ export class Api {
449
449
  return [...acc, ...val.instructions]
450
450
  }, [])
451
451
  .filter(
452
- (ix) => ix.parsed && isSplTransferInstruction({ program: ix.program, type: ix.parsed.type })
452
+ (ix) =>
453
+ ix.parsed &&
454
+ (isSplTransferInstruction({ program: ix.program, type: ix.parsed.type }) ||
455
+ (!includeUnparsed &&
456
+ isSolTransferInstruction({ program: ix.program, type: ix.parsed.type })))
453
457
  )
454
458
  .map((ix) => {
455
459
  const source = lodash.get(ix, 'parsed.info.source')
@@ -470,6 +474,49 @@ export class Api {
470
474
  return
471
475
  }
472
476
 
477
+ if (
478
+ source === ownerAddress &&
479
+ isSolTransferInstruction({ program: ix.program, type: ix.parsed.type })
480
+ ) {
481
+ const lamports = Number(lodash.get(ix, 'parsed.info.lamports', 0))
482
+ if (solanaTransferTx) {
483
+ solanaTransferTx.lamports += lamports
484
+ if (!Array.isArray(solanaTransferTx.to)) {
485
+ solanaTransferTx.data = {
486
+ sent: [
487
+ {
488
+ address: solanaTransferTx.to,
489
+ amount: solanaTransferTx.amount,
490
+ },
491
+ ],
492
+ }
493
+ solanaTransferTx.to = [solanaTransferTx.to]
494
+ }
495
+
496
+ solanaTransferTx.to.push(destination)
497
+ solanaTransferTx.data.sent.push({ address: destination, amount })
498
+ } else {
499
+ solanaTransferTx = {
500
+ source,
501
+ owner: source,
502
+ from: source,
503
+ to: [destination],
504
+ lamports,
505
+ data: {
506
+ sent: [
507
+ {
508
+ address: destination,
509
+ amount,
510
+ },
511
+ ],
512
+ },
513
+ fee,
514
+ }
515
+ }
516
+
517
+ return
518
+ }
519
+
473
520
  const tokenAccount = tokenAccountsByOwner.find(({ tokenAccountAddress }) => {
474
521
  return [source, destination].includes(tokenAccountAddress)
475
522
  })
@@ -557,6 +604,7 @@ export class Api {
557
604
  to: solanaTransferTx.destination,
558
605
  amount: solanaTransferTx.lamports, // number
559
606
  fee: isSending ? fee : 0,
607
+ data: solanaTransferTx.data,
560
608
  }
561
609
  }
562
610
 
@@ -22,29 +22,38 @@ export class SolanaAutoWithdrawMonitor {
22
22
 
23
23
  async tick() {
24
24
  const walletAccounts = await this.aci.getWalletAccounts({ assetName: this.assetName })
25
- await Promise.all(walletAccounts.map((walletAccount) => this._tick({ walletAccount })))
25
+ for (const walletAccount of walletAccounts) {
26
+ await this._tick({ walletAccount })
27
+ }
26
28
  }
27
29
 
28
30
  async _tick({ walletAccount }) {
29
- const accountState = await this.aci.getAccountState({
30
- assetName: this.assetName,
31
- walletAccount,
32
- })
33
- const { cursor, stakingInfo } = accountState
34
- const { loaded, withdrawable } = stakingInfo
31
+ this.processing = this.processing || new Set()
32
+ if (this.processing.has(walletAccount)) return
33
+ this.processing.add(walletAccount)
34
+
35
+ try {
36
+ const accountState = await this.aci.getAccountState({
37
+ assetName: this.assetName,
38
+ walletAccount,
39
+ })
40
+ const { cursor, stakingInfo } = accountState
41
+ const { loaded, withdrawable } = stakingInfo
35
42
 
36
- if (!Array.isArray(this.cursors[walletAccount])) this.cursors[walletAccount] = []
37
- const cursorChanged = !this.cursors[walletAccount].includes(cursor)
38
- const performedWithdraw = this.cursors[walletAccount].length > 0
43
+ if (!Array.isArray(this.cursors[walletAccount])) this.cursors[walletAccount] = []
44
+ const cursorChanged = !this.cursors[walletAccount].includes(cursor)
39
45
 
40
- if (loaded && cursorChanged && withdrawable.isPositive && !performedWithdraw) {
41
- this.cursors[walletAccount].push(cursor)
42
- try {
43
- const txIds = await this.tryWithdraw({ accountState, walletAccount })
44
- this.cursors[walletAccount].push(...txIds)
45
- } catch (e) {
46
- console.log('solana auto withdraw error:', e)
46
+ if (loaded && cursorChanged && withdrawable.isPositive) {
47
+ this.cursors[walletAccount].push(cursor)
48
+ try {
49
+ const txIds = await this.tryWithdraw({ accountState, walletAccount })
50
+ this.cursors[walletAccount].push(...txIds)
51
+ } catch (e) {
52
+ console.log('solana auto withdraw error:', e)
53
+ }
47
54
  }
55
+ } finally {
56
+ this.processing.delete(walletAccount)
48
57
  }
49
58
  }
50
59
 
@@ -249,11 +249,18 @@ export class SolanaMonitor extends BaseMonitor {
249
249
 
250
250
  if (tx.owner === address) {
251
251
  // send transaction
252
- item.to = tx.to
252
+ item.to = Array.isArray(tx.to) ? undefined : tx.to
253
253
  item.feeAmount = baseAsset.currency.baseUnit(tx.fee) // in SOL
254
254
  item.feeCoinName = baseAsset.name
255
255
  item.coinAmount = item.coinAmount.negate()
256
256
 
257
+ if (tx.data?.sent) {
258
+ item.data.sent = tx.data.sent.map((s) => ({
259
+ address: s.address,
260
+ amount: baseAsset.currency.baseUnit(s.amount).toDefaultString({ unit: true }),
261
+ }))
262
+ }
263
+
257
264
  if (tx.to === tx.owner) {
258
265
  item.selfSend = true
259
266
  item.coinAmount = asset.currency.ZERO
package/src/txs-utils.js CHANGED
@@ -15,3 +15,6 @@ export const isSolanaRewardsActivityTx = (tx) =>
15
15
 
16
16
  export const isSplTransferInstruction = ({ program, type }) =>
17
17
  program === 'spl-token' && TRANSFER_INSTRUCTION_TYPES.has(type)
18
+
19
+ export const isSolTransferInstruction = ({ program, type }) =>
20
+ program === 'system' && TRANSFER_INSTRUCTION_TYPES.has(type)