UNPKG

@renec-foundation/redex-sdk

Version:

Typescript SDK to interact with Orca's Whirlpool program.

318 lines (317 loc) 20.3 kB
"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;