@renec-foundation/redex-sdk
Version:
Typescript SDK to interact with Orca's Whirlpool program.
318 lines (317 loc) • 20.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PositionImpl = void 0;
const common_sdk_1 = require("@orca-so/common-sdk");
const spl_token_1 = require("@solana/spl-token");
const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
const instructions_1 = require("../instructions");
const position_builder_util_1 = require("../utils/builder/position-builder-util");
const public_1 = require("../utils/public");
const spl_token_utils_1 = require("../utils/spl-token-utils");
const whirlpool_ata_utils_1 = require("../utils/whirlpool-ata-utils");
const util_1 = require("./util");
const whirlpool_impl_1 = require("./whirlpool-impl");
class PositionImpl {
constructor(ctx, address, data, whirlpoolData, lowerTickArrayData, upperTickArrayData) {
this.ctx = ctx;
this.address = address;
this.data = data;
this.whirlpoolData = whirlpoolData;
this.lowerTickArrayData = lowerTickArrayData;
this.upperTickArrayData = upperTickArrayData;
}
getAddress() {
return this.address;
}
getData() {
return this.data;
}
getWhirlpoolData() {
return this.whirlpoolData;
}
getLowerTickData() {
return public_1.TickArrayUtil.getTickFromArray(this.lowerTickArrayData, this.data.tickLowerIndex, this.whirlpoolData.tickSpacing);
}
getUpperTickData() {
return public_1.TickArrayUtil.getTickFromArray(this.upperTickArrayData, this.data.tickUpperIndex, this.whirlpoolData.tickSpacing);
}
refreshData() {
return __awaiter(this, void 0, void 0, function* () {
yield this.refresh();
return this.data;
});
}
increaseLiquidity(liquidityInput, resolveATA = true, sourceWallet, positionWallet, ataPayer) {
return __awaiter(this, void 0, void 0, function* () {
const sourceWalletKey = sourceWallet
? common_sdk_1.AddressUtil.toPubKey(sourceWallet)
: this.ctx.wallet.publicKey;
const positionWalletKey = positionWallet
? common_sdk_1.AddressUtil.toPubKey(positionWallet)
: this.ctx.wallet.publicKey;
const ataPayerKey = ataPayer ? common_sdk_1.AddressUtil.toPubKey(ataPayer) : this.ctx.wallet.publicKey;
const whirlpool = yield this.ctx.fetcher.getPool(this.data.whirlpool, true);
if (!whirlpool) {
throw new Error("Unable to fetch whirlpool for this position.");
}
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet);
let tokenOwnerAccountA;
let tokenOwnerAccountB;
if (resolveATA) {
const [ataA, ataB] = yield (0, common_sdk_1.resolveOrCreateATAs)(this.ctx.connection, sourceWalletKey, [
{ tokenMint: whirlpool.tokenMintA, wrappedSolAmountIn: liquidityInput.tokenMaxA },
{ tokenMint: whirlpool.tokenMintB, wrappedSolAmountIn: liquidityInput.tokenMaxB },
], () => this.ctx.fetcher.getAccountRentExempt(), ataPayerKey);
const _a = ataA, { address: ataAddrA } = _a, tokenOwnerAccountAIx = __rest(_a, ["address"]);
const _b = ataB, { address: ataAddrB } = _b, tokenOwnerAccountBIx = __rest(_b, ["address"]);
tokenOwnerAccountA = ataAddrA;
tokenOwnerAccountB = ataAddrB;
txBuilder.addInstruction(tokenOwnerAccountAIx);
txBuilder.addInstruction(tokenOwnerAccountBIx);
}
else {
tokenOwnerAccountA = yield (0, common_sdk_1.deriveATA)(sourceWalletKey, whirlpool.tokenMintA);
tokenOwnerAccountB = yield (0, common_sdk_1.deriveATA)(sourceWalletKey, whirlpool.tokenMintB);
}
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint);
const increaseIx = (0, instructions_1.increaseLiquidityIx)(this.ctx.program, Object.assign(Object.assign({}, liquidityInput), { whirlpool: this.data.whirlpool, position: this.address, positionTokenAccount,
tokenOwnerAccountA,
tokenOwnerAccountB, tokenVaultA: whirlpool.tokenVaultA, tokenVaultB: whirlpool.tokenVaultB, tickArrayLower: public_1.PDAUtil.getTickArray(this.ctx.program.programId, this.data.whirlpool, public_1.TickUtil.getStartTickIndex(this.data.tickLowerIndex, whirlpool.tickSpacing)).publicKey, tickArrayUpper: public_1.PDAUtil.getTickArray(this.ctx.program.programId, this.data.whirlpool, public_1.TickUtil.getStartTickIndex(this.data.tickUpperIndex, whirlpool.tickSpacing)).publicKey, positionAuthority: positionWalletKey }));
txBuilder.addInstruction(increaseIx);
return txBuilder;
});
}
decreaseLiquidity(liquidityInput, resolveATA = true, sourceWallet, positionWallet, ataPayer) {
return __awaiter(this, void 0, void 0, function* () {
const sourceWalletKey = sourceWallet
? common_sdk_1.AddressUtil.toPubKey(sourceWallet)
: this.ctx.wallet.publicKey;
const positionWalletKey = positionWallet
? common_sdk_1.AddressUtil.toPubKey(positionWallet)
: this.ctx.wallet.publicKey;
const ataPayerKey = ataPayer ? common_sdk_1.AddressUtil.toPubKey(ataPayer) : this.ctx.wallet.publicKey;
const whirlpool = yield this.ctx.fetcher.getPool(this.data.whirlpool, true);
if (!whirlpool) {
throw new Error("Unable to fetch whirlpool for this position.");
}
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet);
let tokenOwnerAccountA;
let tokenOwnerAccountB;
if (resolveATA) {
const [ataA, ataB] = yield (0, common_sdk_1.resolveOrCreateATAs)(this.ctx.connection, sourceWalletKey, [{ tokenMint: whirlpool.tokenMintA }, { tokenMint: whirlpool.tokenMintB }], () => this.ctx.fetcher.getAccountRentExempt(), ataPayerKey);
const _a = ataA, { address: ataAddrA } = _a, tokenOwnerAccountAIx = __rest(_a, ["address"]);
const _b = ataB, { address: ataAddrB } = _b, tokenOwnerAccountBIx = __rest(_b, ["address"]);
tokenOwnerAccountA = ataAddrA;
tokenOwnerAccountB = ataAddrB;
txBuilder.addInstruction(tokenOwnerAccountAIx);
txBuilder.addInstruction(tokenOwnerAccountBIx);
}
else {
tokenOwnerAccountA = yield (0, common_sdk_1.deriveATA)(sourceWalletKey, whirlpool.tokenMintA);
tokenOwnerAccountB = yield (0, common_sdk_1.deriveATA)(sourceWalletKey, whirlpool.tokenMintB);
}
const decreaseIx = (0, instructions_1.decreaseLiquidityIx)(this.ctx.program, Object.assign(Object.assign({}, liquidityInput), { whirlpool: this.data.whirlpool, position: this.address, positionTokenAccount: yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint), tokenOwnerAccountA,
tokenOwnerAccountB, tokenVaultA: whirlpool.tokenVaultA, tokenVaultB: whirlpool.tokenVaultB, tickArrayLower: public_1.PDAUtil.getTickArray(this.ctx.program.programId, this.data.whirlpool, public_1.TickUtil.getStartTickIndex(this.data.tickLowerIndex, whirlpool.tickSpacing)).publicKey, tickArrayUpper: public_1.PDAUtil.getTickArray(this.ctx.program.programId, this.data.whirlpool, public_1.TickUtil.getStartTickIndex(this.data.tickUpperIndex, whirlpool.tickSpacing)).publicKey, positionAuthority: positionWalletKey }));
txBuilder.addInstruction(decreaseIx);
return txBuilder;
});
}
collectFees(updateFeesAndRewards = true, ownerTokenAccountMap, destinationWallet, positionWallet, ataPayer, refresh = false) {
return __awaiter(this, void 0, void 0, function* () {
const [destinationWalletKey, positionWalletKey, ataPayerKey] = common_sdk_1.AddressUtil.toPubKeys([
destinationWallet !== null && destinationWallet !== void 0 ? destinationWallet : this.ctx.wallet.publicKey,
positionWallet !== null && positionWallet !== void 0 ? positionWallet : this.ctx.wallet.publicKey,
ataPayer !== null && ataPayer !== void 0 ? ataPayer : this.ctx.wallet.publicKey,
]);
const whirlpool = yield this.ctx.fetcher.getPool(this.data.whirlpool, refresh);
if (!whirlpool) {
throw new Error(`Unable to fetch whirlpool (${this.data.whirlpool}) for this position (${this.address}).`);
}
let txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet);
const accountExemption = yield this.ctx.fetcher.getAccountRentExempt();
let ataMap = Object.assign({}, ownerTokenAccountMap);
if (!ownerTokenAccountMap) {
const affliatedMints = (0, whirlpool_ata_utils_1.getTokenMintsFromWhirlpools)([whirlpool], whirlpool_ata_utils_1.TokenMintTypes.POOL_ONLY);
const { ataTokenAddresses: affliatedTokenAtaMap, resolveAtaIxs } = yield (0, whirlpool_ata_utils_1.resolveAtaForMints)(this.ctx, {
mints: affliatedMints.mintMap,
accountExemption,
receiver: destinationWalletKey,
payer: ataPayerKey,
});
txBuilder.addInstructions(resolveAtaIxs);
if (affliatedMints.hasNativeMint) {
let _a = (0, spl_token_utils_1.createWSOLAccountInstructions)(destinationWalletKey, common_sdk_1.ZERO, accountExemption, ataPayerKey, destinationWalletKey), { address: wSOLAta } = _a, resolveWSolIx = __rest(_a, ["address"]);
affliatedTokenAtaMap[spl_token_1.NATIVE_MINT.toBase58()] = wSOLAta;
txBuilder.addInstruction(resolveWSolIx);
}
ataMap = Object.assign({}, affliatedTokenAtaMap);
}
const tokenOwnerAccountA = ataMap[whirlpool.tokenMintA.toBase58()];
(0, tiny_invariant_1.default)(!!tokenOwnerAccountA, `No owner token account provided for wallet ${destinationWalletKey.toBase58()} for token A ${whirlpool.tokenMintA.toBase58()} `);
const tokenOwnerAccountB = ataMap[whirlpool.tokenMintB.toBase58()];
(0, tiny_invariant_1.default)(!!tokenOwnerAccountB, `No owner token account provided for wallet ${destinationWalletKey.toBase58()} for token B ${whirlpool.tokenMintB.toBase58()} `);
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint);
if (updateFeesAndRewards && !this.data.liquidity.isZero()) {
const updateIx = yield this.updateFeesAndRewards();
txBuilder.addInstruction(updateIx);
}
const ix = (0, instructions_1.collectFeesIx)(this.ctx.program, {
whirlpool: this.data.whirlpool,
position: this.address,
positionTokenAccount,
tokenOwnerAccountA: common_sdk_1.AddressUtil.toPubKey(tokenOwnerAccountA),
tokenOwnerAccountB: common_sdk_1.AddressUtil.toPubKey(tokenOwnerAccountB),
tokenVaultA: whirlpool.tokenVaultA,
tokenVaultB: whirlpool.tokenVaultB,
positionAuthority: positionWalletKey,
});
txBuilder.addInstruction(ix);
return txBuilder;
});
}
collectRewards(rewardsToCollect, updateFeesAndRewards = true, ownerTokenAccountMap, destinationWallet, positionWallet, ataPayer, refresh = true) {
return __awaiter(this, void 0, void 0, function* () {
const [destinationWalletKey, positionWalletKey, ataPayerKey] = common_sdk_1.AddressUtil.toPubKeys([
destinationWallet !== null && destinationWallet !== void 0 ? destinationWallet : this.ctx.wallet.publicKey,
positionWallet !== null && positionWallet !== void 0 ? positionWallet : this.ctx.wallet.publicKey,
ataPayer !== null && ataPayer !== void 0 ? ataPayer : this.ctx.wallet.publicKey,
]);
const whirlpool = yield this.ctx.fetcher.getPool(this.data.whirlpool, refresh);
if (!whirlpool) {
throw new Error(`Unable to fetch whirlpool(${this.data.whirlpool}) for this position(${this.address}).`);
}
const initializedRewards = whirlpool.rewardInfos.filter((info) => public_1.PoolUtil.isRewardInitialized(info));
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet);
const accountExemption = yield this.ctx.fetcher.getAccountRentExempt();
let ataMap = Object.assign({}, ownerTokenAccountMap);
if (!ownerTokenAccountMap) {
const rewardMints = (0, whirlpool_ata_utils_1.getTokenMintsFromWhirlpools)([whirlpool], whirlpool_ata_utils_1.TokenMintTypes.REWARD_ONLY);
const { ataTokenAddresses: affliatedTokenAtaMap, resolveAtaIxs } = yield (0, whirlpool_ata_utils_1.resolveAtaForMints)(this.ctx, {
mints: rewardMints.mintMap,
accountExemption,
receiver: destinationWalletKey,
payer: ataPayerKey,
});
if (rewardMints.hasNativeMint) {
let _a = (0, spl_token_utils_1.createWSOLAccountInstructions)(destinationWalletKey, common_sdk_1.ZERO, accountExemption), { address: wSOLAta } = _a, resolveWSolIx = __rest(_a, ["address"]);
affliatedTokenAtaMap[spl_token_1.NATIVE_MINT.toBase58()] = wSOLAta;
txBuilder.addInstruction(resolveWSolIx);
}
txBuilder.addInstructions(resolveAtaIxs);
ataMap = Object.assign({}, affliatedTokenAtaMap);
}
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint);
if (updateFeesAndRewards && !this.data.liquidity.isZero()) {
const updateIx = yield this.updateFeesAndRewards();
txBuilder.addInstruction(updateIx);
}
initializedRewards.forEach((info, index) => {
if (rewardsToCollect &&
!rewardsToCollect.some((r) => r.toString() === info.mint.toBase58())) {
// If rewardsToCollect is specified and this reward is not in it,
// don't include collectIX for that in TX
return;
}
const rewardOwnerAccount = ataMap[info.mint.toBase58()];
(0, tiny_invariant_1.default)(!!rewardOwnerAccount, `No owner token account provided for wallet ${destinationWalletKey.toBase58()} for reward ${index} token ${info.mint.toBase58()} `);
const ix = (0, instructions_1.collectRewardIx)(this.ctx.program, {
whirlpool: this.data.whirlpool,
position: this.address,
positionTokenAccount,
rewardIndex: index,
rewardOwnerAccount: common_sdk_1.AddressUtil.toPubKey(rewardOwnerAccount),
rewardVault: info.vault,
positionAuthority: positionWalletKey,
});
txBuilder.addInstruction(ix);
});
return txBuilder;
});
}
closePosition(slippageTolerance, destinationWallet, positionWallet, payer) {
return __awaiter(this, void 0, void 0, function* () {
const pool = yield this.getPool();
const txs = yield pool.closePosition(this.address, slippageTolerance, destinationWallet, positionWallet, payer);
if (txs.length <= 1) {
return txs[0];
}
const tokenAccountsIx = txs[0].compressIx(true);
return txs[1].addInstruction(tokenAccountsIx);
});
}
getPool(refresh = false) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = this.data.whirlpool;
const account = yield this.ctx.fetcher.getPool(poolAddress, refresh);
if (!account) {
throw new Error(`Unable to fetch Whirlpool at address at ${poolAddress}`);
}
const tokenInfos = yield (0, util_1.getTokenMintInfos)(this.ctx.fetcher, account, refresh);
const vaultInfos = yield (0, util_1.getTokenVaultAccountInfos)(this.ctx.fetcher, account, refresh);
const rewardInfos = yield (0, util_1.getRewardInfos)(this.ctx.fetcher, account, refresh);
return new whirlpool_impl_1.WhirlpoolImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(poolAddress), tokenInfos[0], tokenInfos[1], vaultInfos[0], vaultInfos[1], rewardInfos, account);
});
}
refresh() {
return __awaiter(this, void 0, void 0, function* () {
const positionAccount = yield this.ctx.fetcher.getPosition(this.address, true);
if (!!positionAccount) {
this.data = positionAccount;
}
const whirlpoolAccount = yield this.ctx.fetcher.getPool(this.data.whirlpool, true);
if (!!whirlpoolAccount) {
this.whirlpoolData = whirlpoolAccount;
}
const [lowerTickArray, upperTickArray] = yield (0, position_builder_util_1.getTickArrayDataForPosition)(this.ctx, this.data, this.whirlpoolData, true);
if (lowerTickArray) {
this.lowerTickArrayData = lowerTickArray;
}
if (upperTickArray) {
this.upperTickArrayData = upperTickArray;
}
});
}
updateFeesAndRewards() {
return __awaiter(this, void 0, void 0, function* () {
const whirlpool = yield this.ctx.fetcher.getPool(this.data.whirlpool);
if (!whirlpool) {
throw new Error(`Unable to fetch whirlpool(${this.data.whirlpool}) for this position(${this.address}).`);
}
const [tickArrayLowerPda, tickArrayUpperPda] = [
this.data.tickLowerIndex,
this.data.tickUpperIndex,
].map((tickIndex) => public_1.PDAUtil.getTickArrayFromTickIndex(tickIndex, whirlpool.tickSpacing, this.data.whirlpool, this.ctx.program.programId));
const updateIx = (0, instructions_1.updateFeesAndRewardsIx)(this.ctx.program, {
whirlpool: this.data.whirlpool,
position: this.address,
tickArrayLower: tickArrayLowerPda.publicKey,
tickArrayUpper: tickArrayUpperPda.publicKey,
});
return updateIx;
});
}
}
exports.PositionImpl = PositionImpl;