Jupiter V6 Swap API指南
Jupiter API 是开发者获取 Solana 流动性的最简单方法,本文可以帮助开发者在自己的应用中快速集成Jupiter 的能力。
一键发币: SOL | BNB | ETH | BASE | Blast | ARB | OP | POLYGON | AVAX | FTM | OK
Jupiter API 是开发者获取 Solana 流动性的最简单方法。 只需传入所需的货币对、金额和滑点,API 将返回执行交换所需的序列化交易,然后可以将其与所需的签名一起传递到 Solana 区块链中。
所有 Jupiter 交换都使用版本化交易和地址查找表。 但并非所有钱包都支持版本化交易,因此如果检测到不支持版本化交易的钱包,则需要使用 asLegacyTransaction
参数。
可以在这里查阅Jupiter V6 Swap API的详细文档。
1、Jupiter API快速上手
要运行此示例,至少需要 NodeJS 16。在命令行终端中,安装库:
npm i @solana/web3.js
npm i cross-fetch
npm i @project-serum/anchor
npm i bs58
1.1 从库导入并设置连接
接下来,你可以将以下代码片段复制到 javascript 文件 jupiter-api-example.js。 当准备好运行代码时,只需输入: node jupiter-api-example.js
。
import { Connection, Keypair, VersionedTransaction } from '@solana/web3.js';
import fetch from 'cross-fetch';
import { Wallet } from '@project-serum/anchor';
import bs58 from 'bs58';
// It is recommended that you use your own RPC endpoint.
// This RPC endpoint is only for demonstration purposes so that this example will run.
const connection = new Connection('https://neat-hidden-sanctuary.solana-mainnet.discover.quiknode.pro/2af5315d336f9ae920028bbb90a73b724dc1bbed/');
提示:始终确保你使用自己的 RPC 端点。 上例中连接对象使用的 RPC 端点可能不再起作用。
1.2 设置钱包
你可以粘贴私钥用于测试目的,但不建议在生产应用程序中这样做。
const wallet = new Wallet(Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || '')));
1.3 获取交换路由
在这里,我们获得了从 SOL 兑换成 USDC 的报价。
// Swapping SOL to USDC with input 0.1 SOL and 0.5% slippage
const quoteResponse = await (
await fetch('https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112\
&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\
&amount=100000000\
&slippageBps=50'
)
).json();
// console.log({ quoteResponse })
API 接受整数金额,你必须通过查找该代币的小数点来考虑每个代币的小数点。 例如,USDC 有 6 位小数,1 USDC 传入 API 时,其整数为 1000000。
1.4 获取序列化交易来执行交换
一旦有了报价,我们需要将报价序列化为可以在链上提交的兑换交易。
// get serialized transactions for the swap
const { swapTransaction } = await (
await fetch('https://quote-api.jup.ag/v6/swap', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// quoteResponse from /quote api
quoteResponse,
// user public key to be used for the swap
userPublicKey: wallet.publicKey.toString(),
// auto wrap and unwrap SOL. default is true
wrapAndUnwrapSol: true,
// feeAccount is optional. Use if you want to charge a fee. feeBps must have been passed in /quote API.
// feeAccount: "fee_account_public_key"
})
})
).json();
1.5 反序列化并签署交易
// deserialize the transaction
const swapTransactionBuf = Buffer.from(swapTransaction, 'base64');
var transaction = VersionedTransaction.deserialize(swapTransactionBuf);
console.log(transaction);
// sign the transaction
transaction.sign([wallet.payer]);
1.6 执行交易
// Execute the transaction
const rawTransaction = transaction.serialize()
const txid = await connection.sendRawTransaction(rawTransaction, {
skipPreflight: true,
maxRetries: 2
});
await connection.confirmTransaction(txid);
console.log(`https://solscan.io/tx/${txid}`);
2、高级错误处理以从 API 禁用某些 AMM
有时,AMM 在交换时会抛出错误,并且为了防止为同一报价获取失败的 AMM,你可以在获取 /quote
时使用 exceptDexes
参数。
示例 JS,在 @mercurial-finance/optimist
包的帮助下:
import { parseErrorForTransaction } from '@mercurial-finance/optimist';
// TX ID from last step if the transaction failed.
const transaction = connection.getTransaction(txid, {
maxSupportedTransactionVersion: 0,
commitment: 'confirmed'
});
const programIdToLabelHash = await (
await fetch('https://quote-api.jup.ag/v6/program-id-to-label')
).json();
const { programIds } = parseErrorForTransaction(transaction);
let excludeDexes = new Set();
if (programIds) {
for (let programId of programIds) {
let foundLabel = programIdToLabelHash[programId];
if(foundLabel) {
excludeDexes.add(foundLabel);
}
}
}
// Request another quote with `excludeDexes`.
const { data } = await (
await fetch(`https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112
&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
&amount=100000000&excludeDexes=${Array.from(excludeDexes).join(',')}
&slippageBps=50`
)
).json();
3、指令代替交易
有时,你更喜欢使用指令来编写,而不是从 /swap
端点返回的一个交易。 你可以改为发布到 /swap-instructions
,它采用与 /swap
端点相同的参数。
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 api
quoteResponse,
userPublicKey: swapUserKeypair.publicKey.toBase58(),
})
})
).json();
if (instructions.error) {
throw new Error("Failed to get swap instructions: " + instructions.error);
}
const {
tokenLedgerInstruction, // If you are using `useTokenLedger = true`.
computeBudgetInstructions, // The necessary instructions to setup the compute budget.
setupInstructions, // Setup missing ATA for the users.
swapInstruction: swapInstructionPayload, // The actual swap instruction.
cleanupInstruction, // Unwrap the SOL if `wrapAndUnwrapSol = true`.
addressLookupTableAddresses, // The lookup table addresses that you can use if you are using versioned transaction.
} = 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: string[]
): Promise<AddressLookupTableAccount[]> => {
const addressLookupTableAccountInfos =
await 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<AddressLookupTableAccount>());
};
const addressLookupTableAccounts: AddressLookupTableAccount[] = [];
addressLookupTableAccounts.push(
...(await getAddressLookupTableAccounts(addressLookupTableAddresses))
);
const blockhash = (await connection.getLatestBlockhash()).blockhash;
const messageV0 = new TransactionMessage({
payerKey: payerPublicKey,
recentBlockhash: blockhash,
instructions: [
// uncomment if needed: ...setupInstructions.map(deserializeInstruction),
deserializeInstruction(swapInstructionPayload),
// uncomment if needed: deserializeInstruction(cleanupInstruction),
],
}).compileToV0Message(addressLookupTableAccounts);
const transaction = new VersionedTransaction(messageV0);
4、使用maxAccounts
有时,如果你使用 Jupiter Swap 指令编写,可能希望为自己的程序指令腾出一些帐户(1 个 Solana 交易中最多 64 个),这时可以使用 maxAccounts
。
// If you know that your instruction will take up 10 accounts, you
// can pass in 54 as `maxAccounts` when quoting.
const { data } = await (
await fetch('https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112\
&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\
&amount=100000000\
&slippageBps=50\
&maxAccounts=54'
)
).json();
const quoteResponse = data;
// console.log(quoteResponse)
maxAccounts
是一个估计值,因为它不考虑帐户重叠,但它是控制每笔交易所需的帐户数量的良好开端。
6、使用Token Ledger指令
有时,你可能不知道 Jupiter 交换的确切输入金额,直到交换发生前的指令为止。
例如:
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 api
quoteResponse,
useTokenLedger: true,
})
).json();
const {
tokenLedgerInstruction: tokenLedgerPayload, // If you are using `useTokenLedger = true`.
swapInstruction: swapInstructionPayload, // The actual swap instruction.
addressLookupTableAddresses, // The lookup table addresses that you can use if you are using versioned transaction.
} = instructions;
// A withdraw instruction that will increase the user input token account amount.
const withdrawInstruction = ...;
// Coupled with the tokenLedgerInstruction, the swap instruction will use the
// user increased amount of the input token account after the withdrawal as input amount.
const tokenLedgerInstruction = new TransactionInstruction({
programId: new PublicKey(tokenLedgerPayload.programId),
keys: tokenLedgerPayload.accounts.map((key) => ({
pubkey: new PublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable,
})),
data: Buffer.from(tokenLedgerPayload.data, "base64"),
});
const swapInstruction = new TransactionInstruction({
programId: new PublicKey(swapInstructionPayload.programId),
keys: swapInstructionPayload.accounts.map((key) => ({
pubkey: new PublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable,
})),
data: Buffer.from(swapInstructionPayload.data, "base64"),
});
const getAdressLookupTableAccounts = async (
keys: string[]
): Promise<AddressLookupTableAccount[]> => {
const addressLookupTableAccountInfos =
await 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<AddressLookupTableAccount>());
};
const addressLookupTableAccounts: AddressLookupTableAccount[] = [];
addressLookupTableAccounts.push(
...(await getAdressLookupTableAccounts(addressLookupTableAddresses))
);
const messageV0 = new TransactionMessage({
payerKey: payerPublicKey,
recentBlockhash: blockhash,
instructions: [tokenLedgerInstruction, withdrawInstruction, swapInstruction],
}).compileToV0Message(addressLookupTableAccounts);
const transaction = new VersionedTransaction(messageV0);
如果想从 Solend 提款并立即将提款代币转换为 Jupiter 的另一种代币,这会很有用。
7、为交易设置优先费
如果交易在未经链上确认的情况下到期,这可能意味着你必须支付额外费用才能优先处理你的交易。 为此,你可以设置 computeUnitPriceMicroLamports
参数。
const transaction = await (
await fetch('https://quote-api.jup.ag/v6/swap', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// quoteResponse from /quote api
quoteResponse,
// user public key to be used for the swap
userPublicKey: wallet.publicKey.toString(),
dynamicComputeUnitLimit: true, // allow dynamic compute limit instead of max 1,400,000
// custom priority fee
prioritizationFeeLamports: 'auto' // or custom lamports: 1000
})
})
).json();
如果使用 auto
,Jupiter 将自动为交易设置优先费用,上限为 5,000,000 lamports / 0.005 SOL。
原文链接:V6 Swap API - Jupiter Station
DefiPlot翻译整理,转载请标明出处