@exodus/solana-api 3.20.5 → 3.20.7
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 +20 -0
- package/package.json +2 -2
- package/src/api.js +58 -7
- package/src/connection.js +3 -1
- package/src/create-unsigned-tx-for-send.js +2 -1
- package/src/get-fees.js +2 -1
- package/src/get-stake-activation/index.js +2 -1
- package/src/tx-send.js +17 -7
- package/src/txs-utils.js +5 -0
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.20.7](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.20.6...@exodus/solana-api@3.20.7) (2025-08-12)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix(solana): do not use coinAmount for staking txs (#6243)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [3.20.6](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.20.5...@exodus/solana-api@3.20.6) (2025-07-18)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* fix: SOL parse mint transaction (#6045)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [3.20.5](https://github.com/ExodusMovement/assets/compare/@exodus/solana-api@3.20.4...@exodus/solana-api@3.20.5) (2025-07-11)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-api",
|
|
3
|
-
"version": "3.20.
|
|
3
|
+
"version": "3.20.7",
|
|
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": "
|
|
49
|
+
"gitHead": "baa995bca5eb4f7c45041158f680e6b8859996e8",
|
|
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,11 @@ 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 {
|
|
25
|
+
import {
|
|
26
|
+
isSolTransferInstruction,
|
|
27
|
+
isSplMintInstruction,
|
|
28
|
+
isSplTransferInstruction,
|
|
29
|
+
} from './txs-utils.js'
|
|
26
30
|
|
|
27
31
|
const createApi = createApiCJS.default || createApiCJS
|
|
28
32
|
|
|
@@ -473,12 +477,15 @@ export class Api {
|
|
|
473
477
|
(ix) =>
|
|
474
478
|
ix.parsed &&
|
|
475
479
|
(isSplTransferInstruction({ program: ix.program, type: ix.parsed.type }) ||
|
|
480
|
+
isSplMintInstruction({ program: ix.program, type: ix.parsed.type }) ||
|
|
476
481
|
(!includeUnparsed &&
|
|
477
482
|
isSolTransferInstruction({ program: ix.program, type: ix.parsed.type })))
|
|
478
483
|
)
|
|
479
484
|
.map((ix) => {
|
|
480
|
-
|
|
481
|
-
const destination =
|
|
485
|
+
let source = lodash.get(ix, 'parsed.info.source')
|
|
486
|
+
const destination = isSplMintInstruction({ program: ix.program, type: ix.parsed.type })
|
|
487
|
+
? lodash.get(ix, 'parsed.info.account') // only for minting
|
|
488
|
+
: lodash.get(ix, 'parsed.info.destination')
|
|
482
489
|
const amount = Number(
|
|
483
490
|
lodash.get(ix, 'parsed.info.amount', 0) ||
|
|
484
491
|
lodash.get(ix, 'parsed.info.tokenAmount.amount', 0)
|
|
@@ -545,6 +552,10 @@ export class Api {
|
|
|
545
552
|
})
|
|
546
553
|
if (!tokenAccount) return
|
|
547
554
|
|
|
555
|
+
if (isSplMintInstruction({ program: ix.program, type: ix.parsed.type })) {
|
|
556
|
+
source = lodash.get(ix, 'parsed.info.mintAuthority')
|
|
557
|
+
}
|
|
558
|
+
|
|
548
559
|
const isSending = tokenAccountsByOwner.some(({ tokenAccountAddress }) => {
|
|
549
560
|
return [source].includes(tokenAccountAddress)
|
|
550
561
|
})
|
|
@@ -552,6 +563,8 @@ export class Api {
|
|
|
552
563
|
// owner if it's a send tx
|
|
553
564
|
return {
|
|
554
565
|
id: txId,
|
|
566
|
+
program: ix.program,
|
|
567
|
+
type: ix.parsed.type,
|
|
555
568
|
slot: txDetails.slot,
|
|
556
569
|
owner: isSending ? ownerAddress : null,
|
|
557
570
|
from: isSending ? ownerAddress : source,
|
|
@@ -679,6 +692,7 @@ export class Api {
|
|
|
679
692
|
// Parse Token txs
|
|
680
693
|
const tokenTxs = this._parseTokenTransfers({
|
|
681
694
|
instructions,
|
|
695
|
+
innerInstructions,
|
|
682
696
|
tokenAccountsByOwner,
|
|
683
697
|
ownerAddress,
|
|
684
698
|
fee,
|
|
@@ -769,6 +783,7 @@ export class Api {
|
|
|
769
783
|
|
|
770
784
|
_parseTokenTransfers({
|
|
771
785
|
instructions,
|
|
786
|
+
innerInstructions = [],
|
|
772
787
|
tokenAccountsByOwner,
|
|
773
788
|
ownerAddress,
|
|
774
789
|
fee,
|
|
@@ -780,8 +795,9 @@ export class Api {
|
|
|
780
795
|
preTokenBalances.length === 0 &&
|
|
781
796
|
postTokenBalances.length === 0 &&
|
|
782
797
|
!Array.isArray(tokenAccountsByOwner)
|
|
783
|
-
)
|
|
798
|
+
) {
|
|
784
799
|
return []
|
|
800
|
+
}
|
|
785
801
|
|
|
786
802
|
const tokenTxs = []
|
|
787
803
|
|
|
@@ -791,10 +807,13 @@ export class Api {
|
|
|
791
807
|
if (isSplTransferInstruction({ program, type })) {
|
|
792
808
|
let tokenAccount = lodash.find(tokenAccountsByOwner, { tokenAccountAddress: source })
|
|
793
809
|
const isSending = !!tokenAccount
|
|
794
|
-
if (!isSending)
|
|
810
|
+
if (!isSending) {
|
|
811
|
+
// receiving
|
|
795
812
|
tokenAccount = lodash.find(tokenAccountsByOwner, {
|
|
796
813
|
tokenAccountAddress: destination,
|
|
797
|
-
})
|
|
814
|
+
})
|
|
815
|
+
}
|
|
816
|
+
|
|
798
817
|
if (!tokenAccount) return // no transfers with our addresses involved
|
|
799
818
|
|
|
800
819
|
const owner = isSending ? ownerAddress : null
|
|
@@ -829,6 +848,36 @@ export class Api {
|
|
|
829
848
|
}
|
|
830
849
|
})
|
|
831
850
|
|
|
851
|
+
innerInstructions.forEach((parsedIx) => {
|
|
852
|
+
const { type, program, amount, from, to } = parsedIx
|
|
853
|
+
|
|
854
|
+
// Handle token minting (mintTo, mintToChecked)
|
|
855
|
+
if (isSplMintInstruction({ program, type })) {
|
|
856
|
+
const {
|
|
857
|
+
token: { tokenAccountAddress },
|
|
858
|
+
} = parsedIx
|
|
859
|
+
|
|
860
|
+
// Check if the destination token account belongs to our owner
|
|
861
|
+
const tokenAccount = lodash.find(tokenAccountsByOwner, {
|
|
862
|
+
tokenAccountAddress,
|
|
863
|
+
})
|
|
864
|
+
|
|
865
|
+
if (!tokenAccount) return // not our token account
|
|
866
|
+
|
|
867
|
+
delete tokenAccount.balance
|
|
868
|
+
delete tokenAccount.owner
|
|
869
|
+
|
|
870
|
+
tokenTxs.push({
|
|
871
|
+
owner: null, // no owner for minting (it's created from thin air)
|
|
872
|
+
token: tokenAccount,
|
|
873
|
+
from, // mint address as the source
|
|
874
|
+
to, // our address as recipient
|
|
875
|
+
amount: Number(amount || 0),
|
|
876
|
+
fee: 0, // no fee for receiving minted tokens
|
|
877
|
+
})
|
|
878
|
+
}
|
|
879
|
+
})
|
|
880
|
+
|
|
832
881
|
return tokenTxs
|
|
833
882
|
}
|
|
834
883
|
|
|
@@ -921,8 +970,10 @@ export class Api {
|
|
|
921
970
|
if (
|
|
922
971
|
tokenName === 'unknown' ||
|
|
923
972
|
(filterByTokens.length > 0 && !filterByTokens.includes(tokenName))
|
|
924
|
-
)
|
|
973
|
+
) {
|
|
925
974
|
return acc // filter by supported tokens only
|
|
975
|
+
}
|
|
976
|
+
|
|
926
977
|
if (acc[tokenName]) {
|
|
927
978
|
acc[tokenName] += Number(balance)
|
|
928
979
|
}
|
package/src/connection.js
CHANGED
|
@@ -54,8 +54,10 @@ export class Connection {
|
|
|
54
54
|
console.log(`solana ws: reply timeout (${method}) - ${JSON.stringify(params)} - ${id}`)
|
|
55
55
|
resolve(null)
|
|
56
56
|
}, TIMEOUT)
|
|
57
|
-
if (typeof this.rpcQueue[id].timeout.unref === 'function')
|
|
57
|
+
if (typeof this.rpcQueue[id].timeout.unref === 'function') {
|
|
58
58
|
this.rpcQueue[id].timeout.unref()
|
|
59
|
+
}
|
|
60
|
+
|
|
59
61
|
this.ws.send(JSON.stringify({ jsonrpc: '2.0', method, params, id }))
|
|
60
62
|
})
|
|
61
63
|
},
|
|
@@ -254,7 +254,7 @@ export const createUnsignedTxForSend = async ({
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
export const extractTxLogData = async ({ unsignedTx, api }) => {
|
|
257
|
-
if (!unsignedTx.txData.transactionBuffer)
|
|
257
|
+
if (!unsignedTx.txData.transactionBuffer) {
|
|
258
258
|
return {
|
|
259
259
|
method: unsignedTx.txData.method,
|
|
260
260
|
from: unsignedTx.txData.from,
|
|
@@ -264,6 +264,7 @@ export const extractTxLogData = async ({ unsignedTx, api }) => {
|
|
|
264
264
|
usedFeePayer: unsignedTx.txMeta.usedFeePayer,
|
|
265
265
|
fee: unsignedTx.txMeta.fee,
|
|
266
266
|
}
|
|
267
|
+
}
|
|
267
268
|
|
|
268
269
|
const txData = await parseTxBuffer(unsignedTx.txData.transactionBuffer, api)
|
|
269
270
|
return {
|
package/src/get-fees.js
CHANGED
|
@@ -35,8 +35,9 @@ export const getFeeAsyncFactory = ({ api }) => {
|
|
|
35
35
|
|
|
36
36
|
const stakeAddresses = []
|
|
37
37
|
for (const [addr, info] of Object.entries(stakingInfo.accounts || {})) {
|
|
38
|
-
if (method === 'undelegate' && (info.state === 'active' || info.state === 'activating'))
|
|
38
|
+
if (method === 'undelegate' && (info.state === 'active' || info.state === 'activating')) {
|
|
39
39
|
stakeAddresses.push(addr)
|
|
40
|
+
}
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
rest.stakeAddresses = stakeAddresses
|
|
@@ -21,12 +21,13 @@ export async function getStakeActivation(api, stakeAddress) {
|
|
|
21
21
|
})(),
|
|
22
22
|
])
|
|
23
23
|
|
|
24
|
-
if (!stakeAccount)
|
|
24
|
+
if (!stakeAccount) {
|
|
25
25
|
return {
|
|
26
26
|
status: 'inactive',
|
|
27
27
|
active: 0,
|
|
28
28
|
inactive: 0,
|
|
29
29
|
}
|
|
30
|
+
}
|
|
30
31
|
|
|
31
32
|
const rentExemptReserve = stakeAccount.data.parsed.info.meta.rentExemptReserve
|
|
32
33
|
if (stakeAccount.data.parsed.discriminant === 1) {
|
package/src/tx-send.js
CHANGED
|
@@ -62,17 +62,27 @@ export const createAndBroadcastTXFactory =
|
|
|
62
62
|
: asset.feeAsset.currency.baseUnit(txLogData.fee)
|
|
63
63
|
|
|
64
64
|
const isStakingTx = ['delegate', 'undelegate', 'withdraw'].includes(method)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
let coinAmount = asset.currency.ZERO
|
|
66
|
+
|
|
67
|
+
if (amount) {
|
|
68
|
+
const absoluteAmount = amount.abs()
|
|
69
|
+
if (isStakingTx) {
|
|
70
|
+
coinAmount = absoluteAmount
|
|
71
|
+
} else if (!selfSend) {
|
|
72
|
+
coinAmount = absoluteAmount.negate()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let data = Object.create(null)
|
|
68
77
|
|
|
69
|
-
let data
|
|
70
78
|
if (isStakingTx) {
|
|
71
79
|
data = {
|
|
72
|
-
staking: {
|
|
80
|
+
staking: {
|
|
81
|
+
...txLogData.stakingParams,
|
|
82
|
+
stake: coinAmount.toBaseNumber(),
|
|
83
|
+
},
|
|
73
84
|
}
|
|
74
|
-
|
|
75
|
-
data = Object.create(null)
|
|
85
|
+
coinAmount = asset.currency.ZERO
|
|
76
86
|
}
|
|
77
87
|
|
|
78
88
|
const tx = {
|
package/src/txs-utils.js
CHANGED
|
@@ -4,6 +4,8 @@ const TRANSFER_INSTRUCTION_TYPES = new Set([
|
|
|
4
4
|
'transferCheckedWithFee',
|
|
5
5
|
])
|
|
6
6
|
|
|
7
|
+
const MINT_INSTRUCTION_TYPES = new Set(['mintTo', 'mintToChecked'])
|
|
8
|
+
|
|
7
9
|
const isSolanaTx = (tx) => tx.coinName === 'solana'
|
|
8
10
|
export const isSolanaStaking = (tx) =>
|
|
9
11
|
isSolanaTx(tx) && ['createAccountWithSeed', 'delegate'].includes(tx?.data?.staking?.method)
|
|
@@ -18,3 +20,6 @@ export const isSplTransferInstruction = ({ program, type }) =>
|
|
|
18
20
|
|
|
19
21
|
export const isSolTransferInstruction = ({ program, type }) =>
|
|
20
22
|
program === 'system' && TRANSFER_INSTRUCTION_TYPES.has(type)
|
|
23
|
+
|
|
24
|
+
export const isSplMintInstruction = ({ program, type }) =>
|
|
25
|
+
program === 'spl-token' && MINT_INSTRUCTION_TYPES.has(type)
|