@exodus/solana-lib 3.22.0 → 3.22.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 +16 -0
- package/package.json +2 -2
- package/src/constants.js +2 -3
- package/src/keypair.js +8 -0
- package/src/magiceden/coders.js +2 -0
- package/src/msg/sign-message-deprecated.js +2 -0
- package/src/tx/instruction-utils.js +25 -10
- package/src/tx/prepare-for-signing.js +5 -2
- package/src/tx/verify-only-fee-payer-changed.js +4 -17
- package/src/vendor/publickey.js +5 -0
- package/src/vendor/transaction.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.22.2](https://github.com/ExodusMovement/assets/compare/@exodus/solana-lib@3.22.1...@exodus/solana-lib@3.22.2) (2026-03-27)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @exodus/solana-lib
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [3.22.1](https://github.com/ExodusMovement/assets/compare/@exodus/solana-lib@3.22.0...@exodus/solana-lib@3.22.1) (2026-03-18)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @exodus/solana-lib
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [3.22.0](https://github.com/ExodusMovement/assets/compare/@exodus/solana-lib@3.21.1...@exodus/solana-lib@3.22.0) (2026-03-13)
|
|
7
23
|
|
|
8
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-lib",
|
|
3
|
-
"version": "3.22.
|
|
3
|
+
"version": "3.22.2",
|
|
4
4
|
"description": "Solana utils, such as for cryptography, address encoding/decoding, transaction building, etc.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"type": "git",
|
|
49
49
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "07a8157aec179f656835db50568127788d4b91b6"
|
|
52
52
|
}
|
package/src/constants.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ComputeBudgetProgram,
|
|
2
3
|
PublicKey,
|
|
3
4
|
StakeProgram,
|
|
4
5
|
SystemProgram,
|
|
@@ -15,9 +16,7 @@ export const STAKE_PROGRAM_ID = StakeProgram.programId
|
|
|
15
16
|
|
|
16
17
|
export const TOKEN_PROGRAM_ID = TokenProgram.programId
|
|
17
18
|
|
|
18
|
-
export const COMPUTE_BUDGET_PROGRAM_ID =
|
|
19
|
-
'ComputeBudget111111111111111111111111111111'
|
|
20
|
-
)
|
|
19
|
+
export const COMPUTE_BUDGET_PROGRAM_ID = ComputeBudgetProgram.programId
|
|
21
20
|
|
|
22
21
|
export const TOKEN_2022_PROGRAM_ID = Token2022Program.programId
|
|
23
22
|
|
package/src/keypair.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
2
3
|
edwardsToPublicSync,
|
|
4
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
3
5
|
signDetachedSync,
|
|
6
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
4
7
|
verifyDetachedSync,
|
|
5
8
|
} from '@exodus/crypto/curve25519'
|
|
9
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
6
10
|
import { randomBytes } from '@exodus/crypto/randomBytes'
|
|
7
11
|
|
|
8
12
|
import { PublicKey } from './vendor/publickey.js'
|
|
@@ -10,6 +14,7 @@ import { PublicKey } from './vendor/publickey.js'
|
|
|
10
14
|
export function getKeyPairFromPrivateKey(seed) {
|
|
11
15
|
const pair = Buffer.from(seed, 'hex')
|
|
12
16
|
const privateKey = pair.subarray(0, 32)
|
|
17
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
13
18
|
const publicKey = edwardsToPublicSync({ privateKey, format: 'buffer' })
|
|
14
19
|
|
|
15
20
|
// Recheck just in case
|
|
@@ -23,6 +28,7 @@ export function getKeyPairFromPrivateKey(seed) {
|
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export function generateKeyPair() {
|
|
31
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
26
32
|
const { publicKey, privateKey } = getKeyPairFromPrivateKey(randomBytes(32))
|
|
27
33
|
|
|
28
34
|
return {
|
|
@@ -36,6 +42,7 @@ export function getPublicKey(privateKey) {
|
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
export function sign(data, privateKey) {
|
|
45
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
39
46
|
return signDetachedSync({
|
|
40
47
|
message: data,
|
|
41
48
|
privateKey: Buffer.from(privateKey, 'hex'),
|
|
@@ -44,5 +51,6 @@ export function sign(data, privateKey) {
|
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
export function verifySignature(data, signature, publicKey) {
|
|
54
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
47
55
|
return verifyDetachedSync({ message: data, signature, publicKey: Buffer.from(publicKey, 'hex') })
|
|
48
56
|
}
|
package/src/magiceden/coders.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as BufferLayout from '@exodus/buffer-layout'
|
|
2
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
2
3
|
import { hashSync } from '@exodus/crypto/hash'
|
|
3
4
|
|
|
4
5
|
import { bnAmountU64, publicKey } from '../vendor/utils/layout.js'
|
|
@@ -21,6 +22,7 @@ const idl = {
|
|
|
21
22
|
export function sighash(nameSpace, ixName) {
|
|
22
23
|
const preimage = `${nameSpace}:${ixName}`
|
|
23
24
|
|
|
25
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
24
26
|
return hashSync('sha256', preimage).slice(0, 8)
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
1
2
|
import { signDetachedSync } from '@exodus/crypto/curve25519'
|
|
2
3
|
import bs58 from 'bs58'
|
|
3
4
|
import assert from 'minimalistic-assert'
|
|
@@ -13,6 +14,7 @@ export function signMessage({ message, privateKey }) {
|
|
|
13
14
|
!isTransactionMessage(messageBuffer),
|
|
14
15
|
'attempted to sign transaction using message signing'
|
|
15
16
|
)
|
|
17
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
16
18
|
const signature = signDetachedSync({
|
|
17
19
|
message: messageBuffer,
|
|
18
20
|
privateKey: Buffer.from(privateKey, 'hex').subarray(0, 32),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
COMPUTE_BUDGET_PROGRAM_ID,
|
|
3
|
+
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
|
3
4
|
SYSTEM_PROGRAM_ID,
|
|
4
5
|
TOKEN_2022_PROGRAM_ID,
|
|
5
6
|
TOKEN_PROGRAM_ID,
|
|
@@ -62,10 +63,20 @@ export function hasValidShape(instruction, reqs) {
|
|
|
62
63
|
)
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
export function
|
|
66
|
-
const programId = instruction?.programId
|
|
66
|
+
export function isTokenProgram(programId) {
|
|
67
67
|
if (!programId) return false
|
|
68
|
-
|
|
68
|
+
const id = programId.toString()
|
|
69
|
+
return id === TOKEN_PROGRAM_ID.toString() || id === TOKEN_2022_PROGRAM_ID.toString()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isATAProgram(programId) {
|
|
73
|
+
if (!programId) return false
|
|
74
|
+
return programId.toString() === SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID.toString()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @deprecated Use `isTokenProgram(instruction.programId)` instead. */
|
|
78
|
+
export function isTokenProgramInstruction(instruction) {
|
|
79
|
+
return isTokenProgram(instruction?.programId)
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
export function isSystemTransferInstruction(instruction) {
|
|
@@ -81,14 +92,18 @@ export function isSystemTransferInstruction(instruction) {
|
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
|
|
84
|
-
export function
|
|
85
|
-
const programId = instruction?.programId
|
|
95
|
+
export function isComputeBudgetProgram(programId) {
|
|
86
96
|
if (!programId) return false
|
|
87
|
-
return programId.
|
|
97
|
+
return programId.toString() === COMPUTE_BUDGET_PROGRAM_ID.toString()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @deprecated Use `isComputeBudgetProgram(instruction.programId)` instead. */
|
|
101
|
+
export function isComputeBudgetInstruction(instruction) {
|
|
102
|
+
return isComputeBudgetProgram(instruction?.programId)
|
|
88
103
|
}
|
|
89
104
|
|
|
90
105
|
export function isSetAuthorityInstruction(instruction) {
|
|
91
|
-
if (!
|
|
106
|
+
if (!isTokenProgram(instruction?.programId)) return false
|
|
92
107
|
const reqs = TOKEN_INSTRUCTION_REQUIREMENTS[TOKEN_INSTRUCTION_LAYOUTS.SetAuthority.index]
|
|
93
108
|
if (!hasValidShape(instruction, reqs)) return false
|
|
94
109
|
const buffer = toBuffer(instruction.data)
|
|
@@ -96,7 +111,7 @@ export function isSetAuthorityInstruction(instruction) {
|
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
export function isTransferInstruction(instruction) {
|
|
99
|
-
if (!
|
|
114
|
+
if (!isTokenProgram(instruction?.programId)) return false
|
|
100
115
|
const reqs = TOKEN_INSTRUCTION_REQUIREMENTS[TOKEN_INSTRUCTION_LAYOUTS.Transfer.index]
|
|
101
116
|
if (!hasValidShape(instruction, reqs)) return false
|
|
102
117
|
const buffer = toBuffer(instruction.data)
|
|
@@ -104,7 +119,7 @@ export function isTransferInstruction(instruction) {
|
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
export function isTransferCheckedInstruction(instruction) {
|
|
107
|
-
if (!
|
|
122
|
+
if (!isTokenProgram(instruction?.programId)) return false
|
|
108
123
|
const reqs = TOKEN_INSTRUCTION_REQUIREMENTS[TOKEN_INSTRUCTION_LAYOUTS.TransferChecked.index]
|
|
109
124
|
if (!hasValidShape(instruction, reqs)) return false
|
|
110
125
|
const buffer = toBuffer(instruction.data)
|
|
@@ -112,7 +127,7 @@ export function isTransferCheckedInstruction(instruction) {
|
|
|
112
127
|
}
|
|
113
128
|
|
|
114
129
|
export function isTransferCheckedWithFeeInstruction(instruction) {
|
|
115
|
-
if (!
|
|
130
|
+
if (!isTokenProgram(instruction?.programId)) return false
|
|
116
131
|
const reqs = TOKEN_INSTRUCTION_REQUIREMENTS[TOKEN_INSTRUCTION_LAYOUTS.TransferFeeExtension.index]
|
|
117
132
|
if (!hasValidShape(instruction, reqs)) return false
|
|
118
133
|
const buffer = toBuffer(instruction.data)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
1
2
|
import { isNumberUnit } from '@exodus/currency'
|
|
2
3
|
import { VersionedTransaction } from '@exodus/solana-web3.js'
|
|
3
4
|
import BN from 'bn.js'
|
|
@@ -55,11 +56,13 @@ export function prepareForSigning(unsignedTx, { checkBalances = true } = {}) {
|
|
|
55
56
|
const address = from
|
|
56
57
|
|
|
57
58
|
const amount = unitAmount
|
|
58
|
-
?
|
|
59
|
+
? // eslint-disable-next-line @exodus/import/no-deprecated
|
|
60
|
+
new BN(isNumberUnit(unitAmount) ? unitAmount.toBaseString() : unitAmount).toString()
|
|
59
61
|
: unitAmount
|
|
60
62
|
|
|
61
63
|
const fee = feeAmount
|
|
62
|
-
?
|
|
64
|
+
? // eslint-disable-next-line @exodus/import/no-deprecated
|
|
65
|
+
new BN(isNumberUnit(feeAmount) ? feeAmount.toBaseString() : feeAmount).toNumber()
|
|
63
66
|
: feeAmount
|
|
64
67
|
|
|
65
68
|
const txData = { ...unsignedTx.txData, address, amount, fee }
|
|
@@ -2,13 +2,13 @@ import lodash from 'lodash'
|
|
|
2
2
|
import assert from 'minimalistic-assert'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
|
5
6
|
SPL_TOKEN_AUTHORITY_TYPE,
|
|
6
7
|
SPL_TOKEN_INSTRUCTION_TYPE,
|
|
7
|
-
TOKEN_2022_PROGRAM_ID,
|
|
8
8
|
TOKEN_PROGRAM_ID,
|
|
9
9
|
} from '../constants.js'
|
|
10
|
-
import { ASSOCIATED_TOKEN_PROGRAM_ID } from '../helpers/spl-token.js'
|
|
11
10
|
import { SYSVAR_RENT_PUBKEY } from '../vendor/index.js'
|
|
11
|
+
import { isATAProgram, isTokenProgram } from './instruction-utils.js'
|
|
12
12
|
|
|
13
13
|
export function verifyOnlyFeePayerChanged(beforeTx, afterTx) {
|
|
14
14
|
assert(
|
|
@@ -72,7 +72,7 @@ export function verifyOnlyFeePayerChanged(beforeTx, afterTx) {
|
|
|
72
72
|
return keyStr === SYSVAR_RENT_PUBKEY.toString()
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
-
if (containsRentSysvar && isATAProgram(programId)) {
|
|
75
|
+
if (containsRentSysvar && (isATAProgram(programId) || isTokenProgram(programId))) {
|
|
76
76
|
const adjustedBeforeAccounts = [...beforeAccounts]
|
|
77
77
|
adjustedBeforeAccounts[0] = afterTx.message.accountKeys[0]
|
|
78
78
|
assert(
|
|
@@ -237,19 +237,6 @@ export function verifyOnlyFeePayerChanged(beforeTx, afterTx) {
|
|
|
237
237
|
)
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
function isTokenProgram(programId) {
|
|
241
|
-
const programIdStr = programId.toString()
|
|
242
|
-
return (
|
|
243
|
-
programIdStr === TOKEN_PROGRAM_ID.toString() ||
|
|
244
|
-
programIdStr === TOKEN_2022_PROGRAM_ID.toString()
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function isATAProgram(programId) {
|
|
249
|
-
const programIdStr = programId.toString()
|
|
250
|
-
return isTokenProgram(programId) || programIdStr === ASSOCIATED_TOKEN_PROGRAM_ID.toString()
|
|
251
|
-
}
|
|
252
|
-
|
|
253
240
|
function getTokenAccountCreations(transaction) {
|
|
254
241
|
const createdAccounts = []
|
|
255
242
|
|
|
@@ -258,7 +245,7 @@ function getTokenAccountCreations(transaction) {
|
|
|
258
245
|
|
|
259
246
|
// Check for ATA creation (Associated Token Account Program)
|
|
260
247
|
if (
|
|
261
|
-
programId ===
|
|
248
|
+
programId === SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID.toString() && // For ATA creation, the new account is typically at index 1
|
|
262
249
|
// Index 0 is payer, Index 1 is the ATA being created, Index 5 is the token program
|
|
263
250
|
instruction.accounts?.length >= 2
|
|
264
251
|
) {
|
package/src/vendor/publickey.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { typedView } from '@exodus/bytes/array.js'
|
|
2
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
2
3
|
import { edwardsToMontgomeryPublicSync } from '@exodus/crypto/curve25519'
|
|
4
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
3
5
|
import { hashSync } from '@exodus/crypto/hash'
|
|
4
6
|
import BN from 'bn.js'
|
|
5
7
|
import bs58 from 'bs58'
|
|
@@ -121,6 +123,7 @@ export class PublicKey {
|
|
|
121
123
|
Buffer.from(seed),
|
|
122
124
|
programId.toBuffer(),
|
|
123
125
|
])
|
|
126
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
124
127
|
const hash = hashSync('sha256', buffer)
|
|
125
128
|
return new PublicKey(hash)
|
|
126
129
|
}
|
|
@@ -138,6 +141,7 @@ export class PublicKey {
|
|
|
138
141
|
buffer = Buffer.concat([buffer, Buffer.from(seed)])
|
|
139
142
|
})
|
|
140
143
|
buffer = Buffer.concat([buffer, programId.toBuffer(), Buffer.from('ProgramDerivedAddress')])
|
|
144
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
141
145
|
const hash = hashSync('sha256', buffer, 'hex')
|
|
142
146
|
const publicKeyBytes = new BN(hash, 16).toArray(null, 32)
|
|
143
147
|
if (isOnCurve(publicKeyBytes)) {
|
|
@@ -152,6 +156,7 @@ export class PublicKey {
|
|
|
152
156
|
function isOnCurve(p) {
|
|
153
157
|
try {
|
|
154
158
|
// This tries to parse edwards public key and validates it
|
|
159
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
155
160
|
edwardsToMontgomeryPublicSync({ publicKey: Uint8Array.from(p) })
|
|
156
161
|
return true
|
|
157
162
|
} catch {}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// https://github.com/solana-labs/solana-web3.js/blob/master/src/transaction.js
|
|
2
2
|
|
|
3
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
3
4
|
import { signDetachedSync, verifyDetachedSync } from '@exodus/crypto/curve25519'
|
|
4
5
|
import bs58 from 'bs58'
|
|
5
6
|
import invariant from 'minimalistic-assert'
|
|
@@ -436,6 +437,7 @@ export class Transaction {
|
|
|
436
437
|
|
|
437
438
|
const signData = message.serialize()
|
|
438
439
|
signers.forEach((signer) => {
|
|
440
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
439
441
|
const signature = signDetachedSync({ message: signData, privateKey: signer.privateKey })
|
|
440
442
|
this.addSignature(signer.publicKey, signature)
|
|
441
443
|
})
|
|
@@ -474,6 +476,7 @@ export class Transaction {
|
|
|
474
476
|
}
|
|
475
477
|
} else {
|
|
476
478
|
if (
|
|
479
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
477
480
|
!verifyDetachedSync({ message: signData, signature, publicKey: publicKey.toBuffer() })
|
|
478
481
|
) {
|
|
479
482
|
return false
|