@exodus/solana-lib 3.22.2 → 3.22.4
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 +18 -0
- package/package.json +2 -2
- package/src/helpers/memo.js +18 -0
- package/src/helpers/metaplex-transfer.js +2 -2
- package/src/helpers/spl-token.js +0 -5
- package/src/index.js +1 -0
- package/src/transaction.js +20 -28
- package/src/tx/create-unsigned-tx.js +5 -0
- package/src/tx/decode-tx-instructions.js +5 -4
- package/src/tx/instruction-utils.js +7 -2
- package/src/tx/parse-tx-buffer.js +2 -2
- package/src/tx/prepare-for-signing.js +6 -2
- package/src/tx/simulate-and-sign-tx.js +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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.4](https://github.com/ExodusMovement/assets/compare/@exodus/solana-lib@3.22.2...@exodus/solana-lib@3.22.4) (2026-03-30)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix: SOL memo program (#7696)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [3.22.3](https://github.com/ExodusMovement/assets/compare/@exodus/solana-lib@3.22.2...@exodus/solana-lib@3.22.3) (2026-03-30)
|
|
17
|
+
|
|
18
|
+
**Note:** Version bump only for package @exodus/solana-lib
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
## [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
25
|
|
|
8
26
|
**Note:** Version bump only for package @exodus/solana-lib
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-lib",
|
|
3
|
-
"version": "3.22.
|
|
3
|
+
"version": "3.22.4",
|
|
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": "0d8639ef50e192df1f302f0ac7ff378d919cc536"
|
|
52
52
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
import { MEMO_PROGRAM_ID } from '../constants.js'
|
|
4
|
+
import { TransactionInstruction } from '../vendor/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds a legacy Memo program instruction (spl-memo).
|
|
8
|
+
* @see https://spl.solana.com/memo
|
|
9
|
+
*/
|
|
10
|
+
export function createMemoInstruction(memo) {
|
|
11
|
+
assert(typeof memo === 'string', 'memo must be a string')
|
|
12
|
+
|
|
13
|
+
return new TransactionInstruction({
|
|
14
|
+
keys: [],
|
|
15
|
+
programId: MEMO_PROGRAM_ID,
|
|
16
|
+
data: Buffer.from(memo, 'utf8'),
|
|
17
|
+
})
|
|
18
|
+
}
|
|
@@ -2,6 +2,7 @@ import * as BufferLayout from '@exodus/buffer-layout'
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
MPL_TOKEN_METADATA_PROGRAM_ID,
|
|
5
|
+
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
|
5
6
|
SYSTEM_PROGRAM_ID,
|
|
6
7
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
7
8
|
TOKEN_PROGRAM_ID,
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
getTokenRecordPDA,
|
|
14
15
|
} from '../encode.js'
|
|
15
16
|
import { PublicKey, Transaction, TransactionInstruction } from '../vendor/index.js'
|
|
16
|
-
import { ASSOCIATED_TOKEN_PROGRAM_ID } from './spl-token.js'
|
|
17
17
|
|
|
18
18
|
const TRANSFER_INSTRUCTION_DISCRIMINATOR = 49
|
|
19
19
|
const TOKEN_AUTH_RULES_ID = new PublicKey('auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg')
|
|
@@ -127,7 +127,7 @@ export const prepareMetaplexTransferTx = ({
|
|
|
127
127
|
isSigner: false,
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
|
-
pubkey:
|
|
130
|
+
pubkey: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
|
131
131
|
isWritable: false,
|
|
132
132
|
isSigner: false,
|
|
133
133
|
},
|
package/src/helpers/spl-token.js
CHANGED
|
@@ -12,11 +12,6 @@ import * as Layout from '../vendor/utils/layout.js'
|
|
|
12
12
|
|
|
13
13
|
// Extracted from https://github.com/ExodusMovement/solana-spl-token/blob/master/src/index.js#L263
|
|
14
14
|
|
|
15
|
-
export const ASSOCIATED_TOKEN_PROGRAM_ID = new PublicKey(
|
|
16
|
-
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'
|
|
17
|
-
)
|
|
18
|
-
export const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA')
|
|
19
|
-
|
|
20
15
|
/**
|
|
21
16
|
* 64-bit value
|
|
22
17
|
*/
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export {
|
|
|
13
13
|
SystemProgram,
|
|
14
14
|
} from './vendor/index.js'
|
|
15
15
|
export { default as Transaction } from './transaction.js'
|
|
16
|
+
export { createMemoInstruction } from './helpers/memo.js'
|
|
16
17
|
export { U64, Token } from './helpers/spl-token.js'
|
|
17
18
|
export { createGetKeyIdentifier, getSupportedPurposes } from './key-identifier.js'
|
|
18
19
|
export { createAgentTokenInitTx } from './helpers/create-agent-token-init-tx.js'
|
package/src/transaction.js
CHANGED
|
@@ -2,7 +2,7 @@ import BN from 'bn.js'
|
|
|
2
2
|
import bs58 from 'bs58'
|
|
3
3
|
import assert from 'minimalistic-assert'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { SEED, STAKE_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from './constants.js'
|
|
6
6
|
import { createStakeAddress, findAssociatedTokenAddress } from './encode.js'
|
|
7
7
|
import { createTransferCheckedWithFeeInstruction } from './helpers/spl-token-2022.js'
|
|
8
8
|
import {
|
|
@@ -20,9 +20,21 @@ import {
|
|
|
20
20
|
StakeProgram,
|
|
21
21
|
SystemProgram,
|
|
22
22
|
Transaction,
|
|
23
|
-
TransactionInstruction,
|
|
24
23
|
} from './vendor/index.js'
|
|
25
24
|
|
|
25
|
+
function normalizeReferencePublicKeys(reference) {
|
|
26
|
+
if (reference == null) return null
|
|
27
|
+
|
|
28
|
+
const entries = Array.isArray(reference) ? reference : [reference]
|
|
29
|
+
if (entries.length === 0) return null
|
|
30
|
+
|
|
31
|
+
return entries.map((key) => {
|
|
32
|
+
if (key instanceof PublicKey) return key
|
|
33
|
+
assert(typeof key === 'string', 'reference must be base58 string(s) or PublicKey(s)')
|
|
34
|
+
return new PublicKey(key)
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
class Tx {
|
|
27
39
|
constructor(
|
|
28
40
|
{
|
|
@@ -38,7 +50,6 @@ class Tx {
|
|
|
38
50
|
fromTokenAddresses, // sender token addresses
|
|
39
51
|
instructions,
|
|
40
52
|
feePayer,
|
|
41
|
-
memo,
|
|
42
53
|
reference,
|
|
43
54
|
} = {},
|
|
44
55
|
options = {}
|
|
@@ -87,17 +98,6 @@ class Tx {
|
|
|
87
98
|
// SOL tx
|
|
88
99
|
this.buildSOLtransaction(this.txObj)
|
|
89
100
|
}
|
|
90
|
-
|
|
91
|
-
// If a memo is provided, add it to the transaction before adding the transfer instruction
|
|
92
|
-
if (memo) {
|
|
93
|
-
this.transaction.add(
|
|
94
|
-
new TransactionInstruction({
|
|
95
|
-
programId: MEMO_PROGRAM_ID,
|
|
96
|
-
keys: [],
|
|
97
|
-
data: Buffer.from(memo, 'utf8'),
|
|
98
|
-
})
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
buildSOLtransaction({ from, to, amount, recentBlockhash, feePayer, reference }) {
|
|
@@ -107,13 +107,9 @@ class Tx {
|
|
|
107
107
|
lamports: new BN(amount),
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
reference = [reference]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
for (const pubkey of reference) {
|
|
110
|
+
const refKeys = normalizeReferencePublicKeys(reference)
|
|
111
|
+
if (refKeys) {
|
|
112
|
+
for (const pubkey of refKeys) {
|
|
117
113
|
txInstruction.keys.push({ pubkey, isWritable: false, isSigner: false })
|
|
118
114
|
}
|
|
119
115
|
}
|
|
@@ -231,13 +227,9 @@ class Tx {
|
|
|
231
227
|
)
|
|
232
228
|
}
|
|
233
229
|
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
reference = [reference]
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
for (const pubkey of reference) {
|
|
230
|
+
const refKeys = normalizeReferencePublicKeys(reference)
|
|
231
|
+
if (refKeys) {
|
|
232
|
+
for (const pubkey of refKeys) {
|
|
241
233
|
tokenTransferInstruction.keys.push({ pubkey, isWritable: false, isSigner: false })
|
|
242
234
|
}
|
|
243
235
|
}
|
|
@@ -36,6 +36,9 @@ export function createUnsignedTx({
|
|
|
36
36
|
// Wallet Connect
|
|
37
37
|
instructions,
|
|
38
38
|
feePayer,
|
|
39
|
+
memo,
|
|
40
|
+
/** Base58 string(s) and/or PublicKey; normalized when building transactions. */
|
|
41
|
+
reference,
|
|
39
42
|
}) {
|
|
40
43
|
return {
|
|
41
44
|
txData: {
|
|
@@ -72,6 +75,8 @@ export function createUnsignedTx({
|
|
|
72
75
|
// Wallet Connect
|
|
73
76
|
instructions,
|
|
74
77
|
feePayer,
|
|
78
|
+
memo,
|
|
79
|
+
reference,
|
|
75
80
|
},
|
|
76
81
|
txMeta: {
|
|
77
82
|
assetName: asset.name,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as BufferLayout from '@exodus/buffer-layout'
|
|
2
2
|
import bs58 from 'bs58'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { SystemInstruction
|
|
4
|
+
import { TOKEN_PROGRAM_ID } from '../constants.js'
|
|
5
|
+
import { SystemInstruction } from './../vendor/index.js'
|
|
6
|
+
import { isATAProgram, isSystemProgram } from './instruction-utils.js'
|
|
6
7
|
import * as TokenInstructions from './token-instructions.js'
|
|
7
8
|
|
|
8
9
|
const INSTRUCTION_LAYOUT = BufferLayout.union(BufferLayout.u8('instruction'))
|
|
@@ -184,7 +185,7 @@ function decodeTokenInstruction(instruction) {
|
|
|
184
185
|
return decodeTokenProgramInstruction(instruction)
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
if (programId
|
|
188
|
+
if (isATAProgram(programId)) {
|
|
188
189
|
return decodeAssociatedTokenProgramInstruction(instruction)
|
|
189
190
|
}
|
|
190
191
|
} catch (err) {
|
|
@@ -216,7 +217,7 @@ export function decodeTransactionInstructions(transactionMessages) {
|
|
|
216
217
|
return [...prevInstructions, ...instructions]
|
|
217
218
|
}, [])
|
|
218
219
|
return transactionInstructions.map((instruction) => {
|
|
219
|
-
if (instruction.programId
|
|
220
|
+
if (isSystemProgram(instruction.programId)) {
|
|
220
221
|
return decodeSystemInstruction(instruction)
|
|
221
222
|
}
|
|
222
223
|
|
|
@@ -74,14 +74,19 @@ export function isATAProgram(programId) {
|
|
|
74
74
|
return programId.toString() === SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID.toString()
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export function isSystemProgram(programId) {
|
|
78
|
+
if (!programId) return false
|
|
79
|
+
return programId.toString() === SYSTEM_PROGRAM_ID.toString()
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
/** @deprecated Use `isTokenProgram(instruction.programId)` instead. */
|
|
78
83
|
export function isTokenProgramInstruction(instruction) {
|
|
79
84
|
return isTokenProgram(instruction?.programId)
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
export function isSystemTransferInstruction(instruction) {
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
if (!isSystemProgram(instruction?.programId)) return false
|
|
89
|
+
|
|
85
90
|
const reqs = SYSTEM_INSTRUCTION_REQUIREMENTS[SYSTEM_INSTRUCTION_LAYOUTS.Transfer.index]
|
|
86
91
|
if (!hasValidShape(instruction, reqs)) return false
|
|
87
92
|
try {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SYSTEM_PROGRAM_ID } from '../constants.js'
|
|
2
1
|
import {
|
|
3
2
|
StakeInstruction,
|
|
4
3
|
SYSTEM_INSTRUCTION_LAYOUTS,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
import { toBuffer } from '../vendor/utils/to-buffer.js'
|
|
9
8
|
import { deserializeTransaction } from './common.js'
|
|
10
9
|
import { getAccountKeys, getNormalizedInstructions } from './instruction-normalizer.js'
|
|
10
|
+
import { isSystemProgram } from './instruction-utils.js'
|
|
11
11
|
|
|
12
12
|
const InstructionKind = {
|
|
13
13
|
TOKEN: 'token',
|
|
@@ -19,7 +19,7 @@ const InstructionKind = {
|
|
|
19
19
|
function getStakedAmountFromCreateWithSeed(stakeAddress, instructions, accountKeys) {
|
|
20
20
|
for (const instruction of instructions) {
|
|
21
21
|
const programId = accountKeys[instruction.programIdIndex]
|
|
22
|
-
if (!programId
|
|
22
|
+
if (!isSystemProgram(programId)) continue
|
|
23
23
|
|
|
24
24
|
const buffer = toBuffer(instruction.data)
|
|
25
25
|
if (
|
|
@@ -4,6 +4,7 @@ import { VersionedTransaction } from '@exodus/solana-web3.js'
|
|
|
4
4
|
import BN from 'bn.js'
|
|
5
5
|
import assert from 'minimalistic-assert'
|
|
6
6
|
|
|
7
|
+
import { createMemoInstruction } from '../helpers/memo.js'
|
|
7
8
|
import { createMetaplexTransferTransaction } from '../helpers/metaplex-transfer.js'
|
|
8
9
|
import Transaction from '../transaction.js'
|
|
9
10
|
import { ComputeBudgetProgram, PublicKey } from '../vendor/index.js'
|
|
@@ -125,6 +126,11 @@ const createTx = ({ txData, method, from }, options) => {
|
|
|
125
126
|
addComputeBudgetToTransaction({ transaction: tx, computeUnits: txData.computeUnits })
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
if (txData.memo) {
|
|
130
|
+
const prependCount = (txData.computeUnits ? 1 : 0) + (txData.priorityFee ? 1 : 0)
|
|
131
|
+
tx.instructions.splice(prependCount, 0, createMemoInstruction(txData.memo))
|
|
132
|
+
}
|
|
133
|
+
|
|
128
134
|
const publicKey = new PublicKey(from)
|
|
129
135
|
if (!tx.feePayer) {
|
|
130
136
|
tx.feePayer = publicKey
|
|
@@ -227,7 +233,6 @@ const createTokenTransaction = (
|
|
|
227
233
|
recentBlockhash,
|
|
228
234
|
to,
|
|
229
235
|
tokenMintAddress,
|
|
230
|
-
memo,
|
|
231
236
|
reference,
|
|
232
237
|
},
|
|
233
238
|
options
|
|
@@ -245,7 +250,6 @@ const createTokenTransaction = (
|
|
|
245
250
|
recentBlockhash,
|
|
246
251
|
to,
|
|
247
252
|
tokenMintAddress,
|
|
248
|
-
memo,
|
|
249
253
|
reference,
|
|
250
254
|
},
|
|
251
255
|
options
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Token, U64 } from '../helpers/spl-token.js'
|
|
2
2
|
import { PublicKey } from '../vendor/index.js'
|
|
3
|
+
import { isSystemProgram } from './instruction-utils.js'
|
|
3
4
|
|
|
4
5
|
export function computeBalance(futureAccountBalance, currentAccountBalance) {
|
|
5
6
|
return new U64(futureAccountBalance).sub(new U64(currentAccountBalance))
|
|
@@ -59,8 +60,6 @@ export function getTransactionSimulationParams(transactionMessage) {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
const isSolAccount = (account) => account === '11111111111111111111111111111111'
|
|
63
|
-
|
|
64
63
|
export function filterAccountsByOwner(futureAccountsState, accountAddresses, publicKey) {
|
|
65
64
|
const solAccounts = []
|
|
66
65
|
const tokenAccounts = []
|
|
@@ -75,7 +74,7 @@ export function filterAccountsByOwner(futureAccountsState, accountAddresses, pub
|
|
|
75
74
|
const accountAddress = accountAddresses[index]
|
|
76
75
|
|
|
77
76
|
// Check if it's SOL account (not token)
|
|
78
|
-
if (
|
|
77
|
+
if (isSystemProgram(futureAccount.owner)) {
|
|
79
78
|
if (accountAddress.toString() === publicKey.toString()) {
|
|
80
79
|
solAccounts.push({
|
|
81
80
|
amount: futureAccount.lamports,
|