Orca Whirlpool兑换API教程

本文介绍如何在你的应用中调用Orca Whirlpool的兑换API,以及如何设置相关的参数。

Orca Whirlpool兑换API教程
一键发币: SOLANA | BNB | ETH | POLYGON | HECO | OKEX

在我们开始之前,你应该了解ticks是什么以及它们如何存储。 如果需要,可以参考此文档

1、使用WhirlpoolClient进行交易

你可以使用兑换报价和 WhirlpoolClient 轻松执行交易。

1.1 通过输入或输出代币生成兑换报价

在下面的部分中了解有关 amountSpecifiedIsInputaToB 的更多信息。

使用报价函数之一生成报价:

生成的 SwapQuote对象包含对 输入代币、 输出代币、费用、预计结束 sqrtPrice 的预期金额的估计。 准备好后,将报价对象直接插入 swapIx 中以执行交易。

const whirlpoolPda = PDAUtil.getWhirlpool(...);
const whirlpoolClient = buildWhirlpoolClient(ctx);
const whirlpool = await whirlpoolClient.getPool(whirlpoolPda.publicKey, true);
// use getData or refreshData, depending on whether you think your data is stale.
const whirlpoolData = await whirlpool.getData(); 

const inputTokenQuote = await swapQuoteByInputToken(
  whirlpool,
  whirlpoolData.tokenMintB,
  new u64(190000000),
  Percentage.fromFraction(1, 1000), // 0.1%
  ctx.program.programId,
  fetcher,
  true
);

// Send out the transaction
const txId = await (await whirlpool.swap(inputTokenQuote)).buildAndExecute();

1.2 在兑换中添加开发者费用

SDK 提供了备用报价和互换功能,让开发者可以按输入资产的百分比收取费用。 此功能是计算百分比、构建费用转移指令以及在交换指令中使用剩余输入资产的便捷方法。

// Wallet used to collect developer fees
const DEV_WALLET = new PublicKey(...)

const whirlpoolPda = PDAUtil.getWhirlpool(...);
const whirlpoolClient = buildWhirlpoolClient(ctx);
const whirlpool = await whirlpoolClient.getPool(whirlpoolPda.publicKey, true);
// use getData or refreshData, depending on whether you think your data is stale.
const whirlpoolData = await whirlpool.getData(); 

const inputTokenQuote = await swapQuoteByInputTokenWithDevFees(
  whirlpool,
  whirlpoolData.tokenMintB,
  new u64(190000000),
  Percentage.fromFraction(1, 1000), // 0.1%
  ctx.program.programId,
  fetcher,
  Percentage.fromFraction(2, 1000), // 0.2% of the input asset will be sent to DEV_WALLET
  true
);

// Send out the transaction
const txId = await (await whirlpool.swapWithDevFees(inputTokenQuote, DEV_WALLET)).buildAndExecute();
注意:开发者费用转移由 SPL 代币程序或系统程序执行,而不是 Whirlpools 程序。

最佳实践是为每种代币类型预先创建关联代币账户 (ATA),并将其发送到开发者钱包。 另外,如果费用以 SOL 支付,请确保开发者钱包至少有 0.001 SOL,以确保钱包账户满足租金减免。

2、手动方式

手动构建自己的参数可以让你更灵活地定义交易边界。

2.1 交易参数

兑换指令需要以下输入(和其他常用账户)来执行交易。

export type SwapInput = {
  amount: u64;
  otherAmountThreshold: u64;
  sqrtPriceLimit: BN;
  amountSpecifiedIsInput: boolean;
  aToB: boolean;
  tickArray0: PublicKey;
  tickArray1: PublicKey;
  tickArray2: PublicKey;
};

aToB决定交易方向

  • 如果为 true,则从代币 A 交易到代币 B
  • 如果为 false,则从代币 B 交易到代币 A

决定想要使用 amountSpecifiedIsInput 限制的代币

  • 如果为 true,则金额是代表交易代币的值。 该金额在交易计算前需缴纳交易费用。
  • 如果为 false,则 amount 是代表所交易代币的值。 该金额是扣除费用后交易所需的代币输出金额。

决定是否要使用 otherAmountThreshold 限制交易的其他代币

  • 如果 amountSpecifiedIsInputtrue,则该金额表示本次交易预期输出代币的最小金额。 如果不想限制,请使用 0。
  • 如果 amountSpecifiedIsInputfalse,则该金额表示可用于交易到预期输出代币数量的输入代币的最大数量。 如果不想限制,请使用钱包中的最大代币数量。

使用 sqrtPriceLimit 确定你想要限制此交易的价格限制。

  • 如果 aToB 为真,则交易将推低价格。 该金额是在输入代币金额足够的情况下交易将进行的最低开方价格。
  • 如果 aToB 为假,交易将推高价格。 该金额是输入代币金额足够时交易将达到的最大开方价格。

如果没有上限,并且想要按照你定义的 amountotherAmountThreshold 进行交易,请使用 bToA 的报价范围的最低价格和 aToB 的报价范围的最高价格。 如果不介意出现 ticks数组错误,或者你知道兑换不会使价格波动太大,则可以使用 MIN_SQRT_PRICEMAX_SQRT_PRICE

sqrt-price 是一个 x64 数字。 所以你的数字需要乘以 2^64。 此处使用 PriceMath 工具 来帮助你进行转换。

amountotherAmountThreshold 是 u64 数字。 因此,请确保将预期的代币数值移动代币的小数点。

3、tick数组

tick数组参数是交换可能遍历的一系列刻度数组。 tickArray0 将始终是包含当前刻度索引的 TickArray 的 PublicKey。

在几乎所有情况下,都可以使用 SwapUtils.getTickArrays 生成所需的刻度数组序列。

如果选择自己构建它,并且知道你的兑换空间足够小,不太可能遍历数组,只需为所有 3 个帐户提供相同的 tickArray0 帐户即可。 一旦获得了刻度数组公钥序列,您就可以使用 AccountFetcher 来检查刻度数组是否已初始化。

要了解有关刻度数组及其遍历工作原理的更多信息,请阅读此处的文档。

4、常见用法示例

假设以下所有标记的小数点均为 6

用 100 个代币 A 换取一定数量的代币 B:

  • aToB - true,amount - 100*10^6,amountSpecifiedIsInput - true
  • otherAmountThreshold - 0,sqrt_price_limit - MIN_SQRT_PRICE

最多可以用 50 个代币 B 换取 100 个代币 A:

  • aToB - false,amount - 100*10^6 , amountSpecifiedIsInput - false
  • otherAmountThreshold - 50,sqrt_price_limit - MAX_SQRT_PRICE

交易将价格从当前 sqrt-price 50_x64 移动到 sqrt_price 250_x64 所需的任意金额

  • aToB - false,amount - 钱包中的最大金额,amountSpecifiedIsInput - true
  • otherAmountThreshold - 0,sqrt_price_limit - 250_x64

5、示例代码

const whirlpoolAddress = PDAUtil.getWhirlpool(...).publicKey;
const whirlpoolData = await fetcher.getPool(whirlpoolAddress);

// Option 1 - Get the current tick-array PDA based on your desired sequence
const startTick = TickUtil.getStartTickIndex(whirlpoolData.tickCurrentIndex, whirlpoolData.tickSpacing);
const tickArrayKey = PDAUtil.getTickArray(ORCA_WHIRLPOOL_PROGRAM_ID, whirlpoolAddress, startTick);

// Option 2 - Get the sequence of tick-arrays to trade in based on your trade direction. 
const tickArrays = SwapUtils.getTickArrays(
  whirlpoolData.tickCurrentIndex,
  whirlpoolData.tickSpacing,
  aToB,
  ctx.program.programId,
  whirlpoolAddress,
  fetcher,
  true
);

// This swap assumes the swap will not cross the current tick-array's boundaries
// Swap 10 tokenA for tokenB. Or up until the price hits $4.95.
const amountIn = DecimalUtil.fromNumber(10, tokenADecimal);
const swapInput: SwapInput = {
  amount: amountIn,
  otherAmountThreshold: ZERO,
  sqrtPriceLimit: MathUtil.toX64(new Decimal(4.95)),
  amountSpecifiedIsInput: true,
  aToB: true,
  tickArray0: tickArrays[0],
  tickArray1: tickArrays[1],
  tickArray2: tickArrays[2]
}

// Send the transaction
const tokenAccountA, tokenAccountB = ...
const oraclePda = PDAUtil.getOracle(ctx.program.programId, whirlpoolPda.publicKey);
const tx = toTx(
  ctx,
  WhirlpoolIx.swapIx(ctx.program, {
    whirlpool: whirlpoolAddress,
    tokenAuthority: ctx.wallet.publicKey,
    tokenOwnerAccountA: tokenAccountA,
    tokenVaultA: whirlpoolData.tokenVaultA,
    tokenOwnerAccountB: tokenAccountB,
    tokenVaultB: whirlpoolData.tokenVaultB,
    oracle: oraclePda.publicKey,
  })
  
  await tx.buildAndExecute();

注意:必须提供 Price Oracle,但目前尚未使用。

6、计算预算限制

交换指令可能是计算成本最高的操作,具体取决于价格对交换的影响有多大。 当你在流动性较低且存在大量缺口的情况下运营时,这可能是你必须考虑的问题。 如果无法在当前计算预算下完成交换,则必须将你的交易拆分为多个交易。

交换的基准计算预算约为 70k。

目前 200k 的最大预算可以容纳的交换循环迭代的最大数量约为 10 个循环。

单次兑换对价格的影响程度取决于以下几个因素:

  • 初始化刻度的间距。 如果已经初始化了很多仓位,使得序列中的所有可初始化报价都已初始化,则需要更多的迭代来跨越价格范围。
  • 池的刻度间距。 刻度间距越大,可初始化刻度之间的价格差异越大,每次交换迭代的价格跳跃越大。

7、常见错误

  • 零交易量(0x1793) - 用户提供的参数量为 0。
  • 无效 SqrtPriceLimitDirection(0x1792) - 用户提供的参数 sqrt_price_limit 与交易方向不匹配。
  • 平方价格越界(0x177b) - 用户提供的参数 sqrt_price_limit 不在 [ MIN_SQRT_PRICE, MAX_SQRT_PRICE] 的范围内
  • 无效的TickArray序列(0x1787) - 交换遍历已到达无序或未初始化的刻度数组。 仔细检查传入的刻度数组序列。
  • 勾选数组索引越界(0x1773) - 交换循环尝试在价格变动期间访问无效的数组索引。
  • 流动性溢出(0x177e)- 流动性值在价格变动期间溢出 128 位。
  • 无效刻度间距(0x1774) -交换池初始化时刻度间距为 0。

原文链接:Performing Swaps | Orca Whirlpool

DefiPlot翻译整理,转载请标明出处