2025-02-17 15:21:20 +07:00

161 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
AddressLookupTableAccount,
PublicKey,
TransactionInstruction
} from "@solana/web3.js"
import {
DEFAULT_OPTIONS,
JUP_API,
JUP_REFERRAL_ADDRESS,
TOKENS
} from "../../constants"
import { getMint } from "@solana/spl-token"
import { sendTx } from "../../utils/send_tx"
/**
* Swap tokens using Jupiter Exchange
* @param agent SolanaAgentKit instance
* @param outputMint Target token mint address
* @param inputAmount Amount to swap (in token decimals)
* @param inputMint Source token mint address (defaults to USDC)
* @param slippageBps Slippage tolerance in basis points (default: 300 = 3%)
* @returns Transaction signature
*/
export async function trade(
agent,
outputMint,
inputAmount,
inputMint = TOKENS.USDC,
slippageBps = DEFAULT_OPTIONS.SLIPPAGE_BPS
) {
try {
// Check if input token is native SOL
const isNativeSol = inputMint.equals(TOKENS.SOL)
// For native SOL, we use LAMPORTS_PER_SOL, otherwise fetch mint info
const inputDecimals = isNativeSol
? 9 // SOL always has 9 decimals
: (await getMint(agent.connection, inputMint)).decimals
// Calculate the correct amount based on actual decimals
const scaledAmount = inputAmount * Math.pow(10, inputDecimals)
const quoteResponse = await (
await fetch(
`${JUP_API}/quote?` +
`inputMint=${
isNativeSol ? TOKENS.SOL.toString() : inputMint.toString()
}` +
`&outputMint=${outputMint.toString()}` +
`&amount=${scaledAmount}` +
`&slippageBps=${slippageBps}` +
`&onlyDirectRoutes=true` +
`&maxAccounts=40` +
`${
agent.config.JUPITER_FEE_BPS
? `&platformFeeBps=${agent.config.JUPITER_FEE_BPS}`
: ""
}`
)
).json()
// Get serialized transaction
let feeAccount
if (agent.config.JUPITER_REFERRAL_ACCOUNT) {
;[feeAccount] = PublicKey.findProgramAddressSync(
[
Buffer.from("referral_ata"),
new PublicKey(agent.config.JUPITER_REFERRAL_ACCOUNT).toBuffer(),
TOKENS.SOL.toBuffer()
],
new PublicKey(JUP_REFERRAL_ADDRESS)
)
}
const instructions = await (
await fetch("https://quote-api.jup.ag/v6/swap-instructions", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
// quoteResponse from /quote or /swap api
quoteResponse,
userPublicKey: agent.wallet.publicKey.toBase58()
// other Jupiter request fields if needed
})
})
).json()
// Deserialize transaction
if (instructions.error) {
throw new Error("Failed to get swap instructions: " + instructions.error)
}
const {
tokenLedgerInstruction, // If using `useTokenLedger = true`
computeBudgetInstructions, // Jupiters default compute budget instructions (we will NOT use these)
setupInstructions, // Setup ATAs if needed
swapInstruction: swapInstructionPayload,
cleanupInstruction, // Unwrap SOL, if you used wrapAndUnwrapSol
addressLookupTableAddresses
} = instructions
const deserializeInstruction = instruction => {
return new TransactionInstruction({
programId: new PublicKey(instruction.programId),
keys: instruction.accounts.map(key => ({
pubkey: new PublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable
})),
data: Buffer.from(instruction.data, "base64")
})
}
const getAddressLookupTableAccounts = async keys => {
const addressLookupTableAccountInfos = await agent.connection.getMultipleAccountsInfo(
keys.map(key => new PublicKey(key))
)
return addressLookupTableAccountInfos.reduce(
(acc, accountInfo, index) => {
const addressLookupTableAddress = keys[index]
if (accountInfo) {
const addressLookupTableAccount = new AddressLookupTableAccount({
key: new PublicKey(addressLookupTableAddress),
state: AddressLookupTableAccount.deserialize(accountInfo.data)
})
acc.push(addressLookupTableAccount)
}
return acc
},
new Array()
)
}
const addressLookupTableAccounts = []
addressLookupTableAccounts.push(
...(await getAddressLookupTableAccounts(addressLookupTableAddresses))
)
const signature = await sendTx(
agent,
[
...setupInstructions.map(deserializeInstruction),
deserializeInstruction(swapInstructionPayload),
deserializeInstruction(cleanupInstruction)
],
undefined,
addressLookupTableAccounts
)
return signature
} catch (error) {
throw new Error(`Swap failed: ${error.message}`)
}
}