@orca-so/whirlpools-sdk
Advanced tools
Comparing version 0.6.1 to 0.7.1
@@ -6,3 +6,3 @@ import { TransactionBuilder } from "@orca-so/common-sdk"; | ||
import { DecreaseLiquidityInput, IncreaseLiquidityInput } from "../instructions"; | ||
import { PositionData } from "../types/public"; | ||
import { PositionData, TickArrayData, TickData, WhirlpoolData } from "../types/public"; | ||
import { Position } from "../whirlpool-client"; | ||
@@ -13,9 +13,18 @@ export declare class PositionImpl implements Position { | ||
private data; | ||
constructor(ctx: WhirlpoolContext, address: PublicKey, data: PositionData); | ||
private whirlpoolData; | ||
private lowerTickArrayData; | ||
private upperTickArrayData; | ||
constructor(ctx: WhirlpoolContext, address: PublicKey, data: PositionData, whirlpoolData: WhirlpoolData, lowerTickArrayData: TickArrayData, upperTickArrayData: TickArrayData); | ||
getAddress(): PublicKey; | ||
getData(): PositionData; | ||
getWhirlpoolData(): WhirlpoolData; | ||
getLowerTickData(): TickData; | ||
getUpperTickData(): TickData; | ||
refreshData(): Promise<PositionData>; | ||
increaseLiquidity(liquidityInput: IncreaseLiquidityInput, resolveATA?: boolean, sourceWallet?: Address, positionWallet?: Address, ataPayer?: Address): Promise<TransactionBuilder>; | ||
decreaseLiquidity(liquidityInput: DecreaseLiquidityInput, resolveATA?: boolean, sourceWallet?: Address, positionWallet?: Address, ataPayer?: Address): Promise<TransactionBuilder>; | ||
collectFees(updateFeesAndRewards?: boolean, resolveATA?: boolean, destinationWallet?: Address, positionWallet?: Address, ataPayer?: Address, refresh?: boolean): Promise<TransactionBuilder>; | ||
collectRewards(rewardsToCollect?: Address[], updateFeesAndRewards?: boolean, resolveATA?: boolean, destinationWallet?: Address, positionWallet?: Address, ataPayer?: Address, refresh?: boolean): Promise<TransactionBuilder>; | ||
private refresh; | ||
private updateFeesAndRewards; | ||
} |
@@ -26,8 +26,13 @@ "use strict"; | ||
const instructions_1 = require("../instructions"); | ||
const position_builder_util_1 = require("../utils/builder/position-builder-util"); | ||
const public_1 = require("../utils/public"); | ||
const whirlpool_ata_utils_1 = require("../utils/whirlpool-ata-utils"); | ||
class PositionImpl { | ||
constructor(ctx, address, data) { | ||
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; | ||
} | ||
@@ -40,2 +45,11 @@ getAddress() { | ||
} | ||
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() { | ||
@@ -122,11 +136,133 @@ return __awaiter(this, void 0, void 0, function* () { | ||
} | ||
collectFees(updateFeesAndRewards = true, resolveATA = true, 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(); | ||
const { ataTokenAddresses, resolveAtaIxs } = yield (0, whirlpool_ata_utils_1.resolveAtaForMints)(this.ctx, { | ||
mints: [whirlpool.tokenMintA, whirlpool.tokenMintB], | ||
accountExemption, | ||
receiver: destinationWalletKey, | ||
payer: ataPayerKey, | ||
}); | ||
const tokenOwnerAccountA = ataTokenAddresses[whirlpool.tokenMintA.toBase58()]; | ||
const tokenOwnerAccountB = ataTokenAddresses[whirlpool.tokenMintB.toBase58()]; | ||
if (resolveATA) { | ||
txBuilder.addInstructions(resolveAtaIxs); | ||
} | ||
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint); | ||
if (updateFeesAndRewards) { | ||
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, | ||
tokenOwnerAccountB, | ||
tokenVaultA: whirlpool.tokenVaultA, | ||
tokenVaultB: whirlpool.tokenVaultB, | ||
positionAuthority: positionWalletKey, | ||
}); | ||
txBuilder.addInstruction(ix); | ||
return txBuilder; | ||
}); | ||
} | ||
collectRewards(rewardsToCollect, updateFeesAndRewards = true, resolveATA = true, 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(); | ||
const { ataTokenAddresses, resolveAtaIxs } = yield (0, whirlpool_ata_utils_1.resolveAtaForMints)(this.ctx, { | ||
mints: initializedRewards.map((r) => r.mint), | ||
accountExemption, | ||
receiver: destinationWalletKey, | ||
payer: ataPayerKey, | ||
}); | ||
if (resolveATA) { | ||
txBuilder.addInstructions(resolveAtaIxs); | ||
} | ||
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWalletKey, this.data.positionMint); | ||
if (updateFeesAndRewards) { | ||
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 ix = (0, instructions_1.collectRewardIx)(this.ctx.program, { | ||
whirlpool: this.data.whirlpool, | ||
position: this.address, | ||
positionTokenAccount, | ||
rewardIndex: index, | ||
rewardOwnerAccount: ataTokenAddresses[info.mint.toBase58()], | ||
rewardVault: info.vault, | ||
positionAuthority: positionWalletKey, | ||
}); | ||
txBuilder.addInstruction(ix); | ||
}); | ||
return txBuilder; | ||
}); | ||
} | ||
refresh() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const account = yield this.ctx.fetcher.getPosition(this.address, true); | ||
if (!!account) { | ||
this.data = account; | ||
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; |
@@ -22,2 +22,3 @@ "use strict"; | ||
const ix_1 = require("../ix"); | ||
const position_builder_util_1 = require("../utils/builder/position-builder-util"); | ||
const public_1 = require("../utils/public"); | ||
@@ -88,3 +89,11 @@ const position_impl_1 = require("./position-impl"); | ||
} | ||
return new position_impl_1.PositionImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(positionAddress), account); | ||
const whirlAccount = yield this.ctx.fetcher.getPool(account.whirlpool, refresh); | ||
if (!whirlAccount) { | ||
throw new Error(`Unable to fetch Whirlpool for Position at address at ${positionAddress}`); | ||
} | ||
const [lowerTickArray, upperTickArray] = yield (0, position_builder_util_1.getTickArrayDataForPosition)(this.ctx, account, whirlAccount, refresh); | ||
if (!lowerTickArray || !upperTickArray) { | ||
throw new Error(`Unable to fetch TickArrays for Position at address at ${positionAddress}`); | ||
} | ||
return new position_impl_1.PositionImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(positionAddress), account, whirlAccount, lowerTickArray, upperTickArray); | ||
}); | ||
@@ -94,10 +103,31 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const accounts = yield this.ctx.fetcher.listPositions(positionAddresses, refresh); | ||
const results = accounts.map((positionAccount, index) => { | ||
const address = positionAddresses[index]; | ||
if (!positionAccount) { | ||
return [address, null]; | ||
// TODO: Prefetch and use fetcher as a cache - Think of a cleaner way to prefetch | ||
const positions = yield this.ctx.fetcher.listPositions(positionAddresses, refresh); | ||
const whirlpoolAddrs = positions | ||
.map((position) => position === null || position === void 0 ? void 0 : position.whirlpool.toBase58()) | ||
.flatMap((x) => (!!x ? x : [])); | ||
yield this.ctx.fetcher.listPools(whirlpoolAddrs, refresh); | ||
const tickArrayAddresses = new Set(); | ||
yield Promise.all(positions.map((pos) => __awaiter(this, void 0, void 0, function* () { | ||
if (pos) { | ||
const pool = yield this.ctx.fetcher.getPool(pos.whirlpool, false); | ||
if (pool) { | ||
const lowerTickArrayPda = public_1.PDAUtil.getTickArrayFromTickIndex(pos.tickLowerIndex, pool.tickSpacing, pos.whirlpool, this.ctx.program.programId).publicKey; | ||
const upperTickArrayPda = public_1.PDAUtil.getTickArrayFromTickIndex(pos.tickUpperIndex, pool.tickSpacing, pos.whirlpool, this.ctx.program.programId).publicKey; | ||
tickArrayAddresses.add(lowerTickArrayPda); | ||
tickArrayAddresses.add(upperTickArrayPda); | ||
} | ||
} | ||
return [address, new position_impl_1.PositionImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(address), positionAccount)]; | ||
}); | ||
}))); | ||
yield this.ctx.fetcher.listTickArrays(Array.from(tickArrayAddresses), true); | ||
// Use getPosition and the prefetched values to generate the Positions | ||
const results = yield Promise.all(positionAddresses.map((pos) => __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
const position = yield this.getPosition(pos, false); | ||
return [pos, position]; | ||
} | ||
catch (_a) { | ||
return [pos, null]; | ||
} | ||
}))); | ||
return Object.fromEntries(results); | ||
@@ -104,0 +134,0 @@ }); |
@@ -35,3 +35,3 @@ import { Percentage, TransactionBuilder } from "@orca-so/common-sdk"; | ||
initTickArrayForTicks(ticks: number[], funder?: Address, refresh?: boolean): Promise<TransactionBuilder | null>; | ||
closePosition(positionAddress: Address, slippageTolerance: Percentage, destinationWallet?: Address, positionWallet?: Address, payer?: Address): Promise<TransactionBuilder>; | ||
closePosition(positionAddress: Address, slippageTolerance: Percentage, destinationWallet?: Address, positionWallet?: Address, payer?: Address): Promise<TransactionBuilder[]>; | ||
swap(quote: SwapInput, sourceWallet?: Address): Promise<TransactionBuilder>; | ||
@@ -46,5 +46,5 @@ swapWithDevFees(quote: DevFeeSwapInput, devFeeWallet: PublicKey, wallet?: PublicKey | undefined, payer?: PublicKey | undefined): Promise<TransactionBuilder>; | ||
}>; | ||
getClosePositionIx(positionAddress: PublicKey, slippageTolerance: Percentage, destinationWallet: PublicKey, positionWallet: PublicKey, payerKey: PublicKey): Promise<TransactionBuilder>; | ||
getClosePositionIx(positionAddress: PublicKey, slippageTolerance: Percentage, destinationWallet: PublicKey, positionWallet: PublicKey, payerKey: PublicKey): Promise<TransactionBuilder[]>; | ||
private getSwapTx; | ||
private refresh; | ||
} |
@@ -33,3 +33,6 @@ "use strict"; | ||
const public_1 = require("../quotes/public"); | ||
const position_builder_util_1 = require("../utils/builder/position-builder-util"); | ||
const public_2 = require("../utils/public"); | ||
const whirlpool_ata_utils_1 = require("../utils/whirlpool-ata-utils"); | ||
const position_impl_1 = require("./position-impl"); | ||
const util_1 = require("./util"); | ||
@@ -203,36 +206,57 @@ class WhirlpoolImpl { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const position = yield this.ctx.fetcher.getPosition(positionAddress, true); | ||
if (!position) { | ||
const positionData = yield this.ctx.fetcher.getPosition(positionAddress, true); | ||
if (!positionData) { | ||
throw new Error(`Position not found: ${positionAddress.toBase58()}`); | ||
} | ||
const whirlpool = this.data; | ||
(0, tiny_invariant_1.default)(position.whirlpool.equals(this.address), `Position ${positionAddress.toBase58()} is not a position for Whirlpool ${this.address.toBase58()}`); | ||
const tickArrayLower = public_2.PDAUtil.getTickArrayFromTickIndex(position.tickLowerIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const tickArrayUpper = public_2.PDAUtil.getTickArrayFromTickIndex(position.tickUpperIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWallet, position.positionMint); | ||
(0, tiny_invariant_1.default)(positionData.whirlpool.equals(this.address), `Position ${positionAddress.toBase58()} is not a position for Whirlpool ${this.address.toBase58()}`); | ||
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWallet, positionData.positionMint); | ||
const ataTxBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
const affiliatedMints = (0, whirlpool_ata_utils_1.getTokenMintsFromWhirlpools)([whirlpool]); | ||
const accountExemption = yield this.ctx.fetcher.getAccountRentExempt(); | ||
const { ataTokenAddresses, resolveAtaIxs } = yield (0, whirlpool_ata_utils_1.resolveAtaForMints)(this.ctx, { | ||
mints: affiliatedMints, | ||
accountExemption, | ||
receiver: destinationWallet, | ||
payer: payerKey, | ||
}); | ||
ataTxBuilder.addInstructions(resolveAtaIxs); | ||
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
const resolvedAssociatedTokenAddresses = {}; | ||
const [ataA, ataB] = yield (0, common_sdk_1.resolveOrCreateATAs)(this.ctx.connection, destinationWallet, [{ tokenMint: whirlpool.tokenMintA }, { tokenMint: whirlpool.tokenMintB }], () => this.ctx.fetcher.getAccountRentExempt(), payerKey); | ||
const { address: tokenOwnerAccountA } = ataA, createTokenOwnerAccountAIx = __rest(ataA, ["address"]); | ||
const { address: tokenOwnerAccountB } = ataB, createTokenOwnerAccountBIx = __rest(ataB, ["address"]); | ||
txBuilder.addInstruction(createTokenOwnerAccountAIx).addInstruction(createTokenOwnerAccountBIx); | ||
resolvedAssociatedTokenAddresses[whirlpool.tokenMintA.toBase58()] = tokenOwnerAccountA; | ||
resolvedAssociatedTokenAddresses[whirlpool.tokenMintB.toBase58()] = tokenOwnerAccountB; | ||
// TODO: Collect all Fees and rewards for the position. | ||
// TODO: Optimize - no need to call updateFee if we call decreaseLiquidity first. | ||
// const collectTx = await buildCollectFeesAndRewardsTx(this.dal, { | ||
// provider, | ||
// positionAddress: positionAddress, | ||
// resolvedAssociatedTokenAddresses, | ||
// }); | ||
// txBuilder.addInstruction(collectTx.compressIx(false)); | ||
/* Remove all liquidity remaining in the position */ | ||
if (position.liquidity.gt(new anchor_1.BN(0))) { | ||
const tickArrayLower = public_2.PDAUtil.getTickArrayFromTickIndex(positionData.tickLowerIndex, whirlpool.tickSpacing, positionData.whirlpool, this.ctx.program.programId).publicKey; | ||
const tickArrayUpper = public_2.PDAUtil.getTickArrayFromTickIndex(positionData.tickUpperIndex, whirlpool.tickSpacing, positionData.whirlpool, this.ctx.program.programId).publicKey; | ||
const [tickArrayLowerData, tickArrayUpperData] = yield (0, position_builder_util_1.getTickArrayDataForPosition)(this.ctx, positionData, whirlpool, true); | ||
(0, tiny_invariant_1.default)(!!tickArrayLowerData, `Tick array ${tickArrayLower} expected to be initialized for whirlpool ${this.address}`); | ||
(0, tiny_invariant_1.default)(!!tickArrayUpperData, `Tick array ${tickArrayUpper} expected to be initialized for whirlpool ${this.address}`); | ||
const position = new position_impl_1.PositionImpl(this.ctx, positionAddress, positionData, whirlpool, tickArrayLowerData, tickArrayUpperData); | ||
const tickLower = position.getLowerTickData(); | ||
const tickUpper = position.getUpperTickData(); | ||
const feesQuote = (0, public_1.collectFeesQuote)({ | ||
position: positionData, | ||
whirlpool, | ||
tickLower, | ||
tickUpper, | ||
}); | ||
const rewardsQuote = (0, public_1.collectRewardsQuote)({ | ||
position: positionData, | ||
whirlpool, | ||
tickLower, | ||
tickUpper, | ||
}); | ||
const shouldCollectFees = feesQuote.feeOwedA.gtn(0) || feesQuote.feeOwedB.gtn(0); | ||
(0, tiny_invariant_1.default)(this.data.rewardInfos.length === rewardsQuote.length, "Rewards quote does not match reward infos length"); | ||
const rewardsToCollect = this.data.rewardInfos | ||
.filter((_, i) => { var _a; return ((_a = rewardsQuote[i]) !== null && _a !== void 0 ? _a : common_sdk_1.ZERO).gtn(0); }) | ||
.map((info) => info.mint); | ||
const shouldCollectRewards = rewardsToCollect.length > 0; | ||
if (positionData.liquidity.gtn(0)) { | ||
/* Remove all liquidity remaining in the position */ | ||
const tokenOwnerAccountA = ataTokenAddresses[whirlpool.tokenMintA.toBase58()]; | ||
const tokenOwnerAccountB = ataTokenAddresses[whirlpool.tokenMintB.toBase58()]; | ||
const decreaseLiqQuote = (0, public_1.decreaseLiquidityQuoteByLiquidityWithParams)({ | ||
liquidity: position.liquidity, | ||
liquidity: positionData.liquidity, | ||
slippageTolerance, | ||
sqrtPrice: whirlpool.sqrtPrice, | ||
tickCurrentIndex: whirlpool.tickCurrentIndex, | ||
tickLowerIndex: position.tickLowerIndex, | ||
tickUpperIndex: position.tickUpperIndex, | ||
tickLowerIndex: positionData.tickLowerIndex, | ||
tickUpperIndex: positionData.tickUpperIndex, | ||
}); | ||
@@ -243,3 +267,3 @@ const liquidityIx = (0, instructions_1.decreaseLiquidityIx)(this.ctx.program, { | ||
tokenMinB: decreaseLiqQuote.tokenMinB, | ||
whirlpool: position.whirlpool, | ||
whirlpool: positionData.whirlpool, | ||
positionAuthority: positionWallet, | ||
@@ -257,2 +281,21 @@ position: positionAddress, | ||
} | ||
else { | ||
if (shouldCollectFees || shouldCollectRewards) { | ||
// We need to manually udpate the fees/rewards since there is no liquidity IX to do so | ||
txBuilder.addInstruction((0, instructions_1.updateFeesAndRewardsIx)(this.ctx.program, { | ||
whirlpool: positionData.whirlpool, | ||
position: positionAddress, | ||
tickArrayLower, | ||
tickArrayUpper, | ||
})); | ||
} | ||
} | ||
if (shouldCollectFees) { | ||
const collectFeexTx = yield position.collectFees(false, false, destinationWallet, positionWallet, payerKey); | ||
txBuilder.addInstruction(collectFeexTx.compressIx(false)); | ||
} | ||
if (shouldCollectRewards) { | ||
const collectRewardsTx = yield position.collectRewards(rewardsToCollect, false, false, destinationWallet, positionWallet, payerKey); | ||
txBuilder.addInstruction(collectRewardsTx.compressIx(false)); | ||
} | ||
/* Close position */ | ||
@@ -264,6 +307,11 @@ const positionIx = (0, instructions_1.closePositionIx)(this.ctx.program, { | ||
position: positionAddress, | ||
positionMint: position.positionMint, | ||
positionMint: positionData.positionMint, | ||
}); | ||
txBuilder.addInstruction(positionIx); | ||
return txBuilder; | ||
const txBuilders = []; | ||
if (!ataTxBuilder.isEmpty()) { | ||
txBuilders.push(ataTxBuilder); | ||
} | ||
txBuilders.push(txBuilder); | ||
return txBuilders; | ||
}); | ||
@@ -270,0 +318,0 @@ } |
import { TransactionBuilder } from "@orca-so/common-sdk"; | ||
import { Address } from "@project-serum/anchor"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { WhirlpoolContext } from "../.."; | ||
import { Position } from "../../whirlpool-client"; | ||
import { PositionData, WhirlpoolContext } from "../.."; | ||
/** | ||
@@ -30,3 +29,3 @@ * Parameters to collect all fees and rewards from a list of positions. | ||
export type CollectAllPositionParams = { | ||
positions: Position[]; | ||
positions: Record<string, PositionData>; | ||
} & CollectAllParams; | ||
@@ -33,0 +32,0 @@ type CollectAllParams = { |
@@ -28,3 +28,2 @@ "use strict"; | ||
const web3_js_1 = require("@solana/web3.js"); | ||
const position_impl_1 = require("../../impl/position-impl"); | ||
const ix_1 = require("../../ix"); | ||
@@ -50,10 +49,10 @@ const public_1 = require("../../utils/public"); | ||
const { positions } = params, rest = __rest(params, ["positions"]); | ||
const posDatas = yield ctx.fetcher.listPositions(positions, refresh); | ||
const positionsObjs = posDatas.reduce((accu, curr, index) => { | ||
if (curr) { | ||
accu.push(new position_impl_1.PositionImpl(ctx, common_sdk_1.AddressUtil.toPubKey(positions[index]), curr)); | ||
const posData = (0, txn_utils_1.convertListToMap)(yield ctx.fetcher.listPositions(positions, refresh), positions.map((pos) => pos.toString())); | ||
const positionMap = {}; | ||
Object.entries(posData).forEach(([addr, pos]) => { | ||
if (pos) { | ||
positionMap[addr] = pos; | ||
} | ||
return accu; | ||
}, []); | ||
return collectAllForPositionsTxns(ctx, Object.assign({ positions: positionsObjs }, rest)); | ||
}); | ||
return collectAllForPositionsTxns(ctx, Object.assign({ positions: positionMap }, rest)); | ||
}); | ||
@@ -77,6 +76,7 @@ } | ||
const payerKey = payer !== null && payer !== void 0 ? payer : ctx.wallet.publicKey; | ||
if (positions.length === 0) { | ||
const positionList = Object.entries(positions); | ||
if (positionList.length === 0) { | ||
return []; | ||
} | ||
const whirlpoolAddrs = positions.map((pos) => pos.getData().whirlpool.toBase58()); | ||
const whirlpoolAddrs = positionList.map(([, pos]) => pos.whirlpool.toBase58()); | ||
const whirlpoolDatas = yield ctx.fetcher.listPools(whirlpoolAddrs, false); | ||
@@ -97,6 +97,6 @@ const whirlpools = (0, txn_utils_1.convertListToMap)(whirlpoolDatas, whirlpoolAddrs); | ||
let reattempt = false; | ||
while (posIndex < positions.length) { | ||
const position = positions[posIndex]; | ||
while (posIndex < positionList.length) { | ||
const [positionAddr, position] = positionList[posIndex]; | ||
let positionTxBuilder = new common_sdk_1.TransactionBuilder(ctx.connection, ctx.wallet); | ||
const { whirlpool: whirlpoolKey, positionMint } = position.getData(); | ||
const { whirlpool: whirlpoolKey, positionMint } = position; | ||
const whirlpool = whirlpools[whirlpoolKey.toBase58()]; | ||
@@ -113,3 +113,3 @@ if (!whirlpool) { | ||
// Build position instructions | ||
const collectIxForPosition = constructCollectPositionIx(ctx, position, whirlpools, positionOwnerKey, positionAuthorityKey, affliatedTokenAtaMap); | ||
const collectIxForPosition = constructCollectPositionIx(ctx, new web3_js_1.PublicKey(positionAddr), position, whirlpools, positionOwnerKey, positionAuthorityKey, affliatedTokenAtaMap); | ||
positionTxBuilder.addInstructions(collectIxForPosition); | ||
@@ -128,3 +128,3 @@ // Attempt to push the new instructions into the pending builder | ||
if (reattempt) { | ||
throw new Error(`Unable to fit collection ix for ${position.getAddress().toBase58()} in a Transaction.`); | ||
throw new Error(`Unable to fit collection ix for ${position.positionMint.toBase58()} in a Transaction.`); | ||
} | ||
@@ -152,6 +152,5 @@ txBuilders.push(pendingTxBuilder); | ||
// TODO: Once individual collect ix for positions is implemented, maybe migrate over if it can take custom ATA? | ||
const constructCollectPositionIx = (ctx, position, whirlpools, positionOwner, positionAuthority, affliatedTokenAtaMap) => { | ||
const constructCollectPositionIx = (ctx, positionKey, position, whirlpools, positionOwner, positionAuthority, affliatedTokenAtaMap) => { | ||
const ixForPosition = []; | ||
const { whirlpool: whirlpoolKey, liquidity, tickLowerIndex, tickUpperIndex, positionMint, rewardInfos: positionRewardInfos, } = position.getData(); | ||
const positionKey = common_sdk_1.AddressUtil.toPubKey(position.getAddress()); | ||
const { whirlpool: whirlpoolKey, liquidity, tickLowerIndex, tickUpperIndex, positionMint, rewardInfos: positionRewardInfos, } = position; | ||
const whirlpool = whirlpools[whirlpoolKey.toBase58()]; | ||
@@ -158,0 +157,0 @@ if (!whirlpool) { |
@@ -26,3 +26,3 @@ "use strict"; | ||
address: tickArray.address, | ||
data: tickArray.data | ||
data: tickArray.data, | ||
}); | ||
@@ -29,0 +29,0 @@ } |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WHIRLPOOL_ACCOUNT_SIZE = exports.WHIRLPOOL_CODER = exports.AccountName = void 0; | ||
const anchor_1 = require("@project-serum/anchor"); | ||
const whirlpool_1 = require("../../artifacts/whirlpool"); | ||
const whirlpool_json_1 = __importDefault(require("../../artifacts/whirlpool.json")); | ||
/** | ||
@@ -25,6 +28,7 @@ * This file contains the types that has the same structure as the types anchor functions returns. | ||
})(AccountName = exports.AccountName || (exports.AccountName = {})); | ||
exports.WHIRLPOOL_CODER = new anchor_1.BorshAccountsCoder(whirlpool_1.IDL); | ||
const IDL = whirlpool_json_1.default; | ||
exports.WHIRLPOOL_CODER = new anchor_1.BorshAccountsCoder(IDL); | ||
/** | ||
* Size of the Whirlpool account in bytes. | ||
*/ | ||
exports.WHIRLPOOL_ACCOUNT_SIZE = exports.WHIRLPOOL_CODER.size(whirlpool_1.IDL.accounts[4]); | ||
exports.WHIRLPOOL_ACCOUNT_SIZE = exports.WHIRLPOOL_CODER.size(IDL.accounts[4]); |
import { PublicKey } from "@solana/web3.js"; | ||
import { AccountFetcher } from "../../network/public"; | ||
import { TickArrayData, TickData } from "../../types/public"; | ||
import { AccountFetcher } from "../../network/public"; | ||
/** | ||
@@ -11,2 +11,11 @@ * A collection of utility functions when interacting with Ticks. | ||
/** | ||
* Get the offset index to access a tick at a given tick-index in a tick-array | ||
* | ||
* @param tickIndex The tick index for the tick that this offset would access | ||
* @param arrayStartIndex The starting tick for the array that this tick-index resides in | ||
* @param tickSpacing The tickSpacing for the Whirlpool that this tickArray belongs to | ||
* @returns The offset index that can access the desired tick at the given tick-array | ||
*/ | ||
static getOffsetIndex(tickIndex: number, arrayStartIndex: number, tickSpacing: number): number; | ||
/** | ||
* Get the startIndex of the tick array containing tickIndex. | ||
@@ -13,0 +22,0 @@ * |
@@ -31,2 +31,13 @@ "use strict"; | ||
/** | ||
* Get the offset index to access a tick at a given tick-index in a tick-array | ||
* | ||
* @param tickIndex The tick index for the tick that this offset would access | ||
* @param arrayStartIndex The starting tick for the array that this tick-index resides in | ||
* @param tickSpacing The tickSpacing for the Whirlpool that this tickArray belongs to | ||
* @returns The offset index that can access the desired tick at the given tick-array | ||
*/ | ||
static getOffsetIndex(tickIndex, arrayStartIndex, tickSpacing) { | ||
return Math.floor((tickIndex - arrayStartIndex) / tickSpacing); | ||
} | ||
/** | ||
* Get the startIndex of the tick array containing tickIndex. | ||
@@ -33,0 +44,0 @@ * |
@@ -7,3 +7,3 @@ import { Percentage, TransactionBuilder } from "@orca-so/common-sdk"; | ||
import { AccountFetcher } from "./network/public"; | ||
import { DecreaseLiquidityInput, IncreaseLiquidityInput, PositionData, WhirlpoolData } from "./types/public"; | ||
import { DecreaseLiquidityInput, IncreaseLiquidityInput, PositionData, TickData, WhirlpoolData } from "./types/public"; | ||
import { TokenAccountInfo, TokenInfo, WhirlpoolRewardInfo } from "./types/public/client-types"; | ||
@@ -193,3 +193,3 @@ /** | ||
*/ | ||
closePosition: (positionAddress: Address, slippageTolerance: Percentage, destinationWallet?: Address, positionWallet?: Address, payer?: Address) => Promise<TransactionBuilder>; | ||
closePosition: (positionAddress: Address, slippageTolerance: Percentage, destinationWallet?: Address, positionWallet?: Address, payer?: Address) => Promise<TransactionBuilder[]>; | ||
/** | ||
@@ -230,2 +230,17 @@ * Perform a swap between tokenA and tokenB on this pool. | ||
/** | ||
* Return the most recently fetched Whirlpool account data for this position. | ||
* @return most recently fetched WhirlpoolData for this position. | ||
*/ | ||
getWhirlpoolData: () => WhirlpoolData; | ||
/** | ||
* Return the most recently fetched TickData account data for this position's lower tick. | ||
* @return most recently fetched TickData for this position's lower tick. | ||
*/ | ||
getLowerTickData: () => TickData; | ||
/** | ||
* Return the most recently fetched TickData account data for this position's upper tick. | ||
* @return most recently fetched TickData for this position's upper tick. | ||
*/ | ||
getUpperTickData: () => TickData; | ||
/** | ||
* Fetch and return the most recently fetched Position account data. | ||
@@ -261,2 +276,31 @@ * @return the most up to date PositionData for this address. | ||
decreaseLiquidity: (liquidityInput: DecreaseLiquidityInput, resolveATA?: boolean, destinationWallet?: Address, positionWallet?: Address, ataPayer?: Address) => Promise<TransactionBuilder>; | ||
/** | ||
* Collect fees from this position | ||
* | ||
* If `positionWallet` is provided, the wallet owners have to sign this transaction. | ||
* | ||
* @param resolveATA - if true, add instructions to create associated token accounts for tokenA,B for the destinationWallet if necessary. (RPC call required) | ||
* @param updateFeesAndRewards - if true, add instructions to refresh the accumulated fees and rewards data (default to true unless you know that the collect fees quote and on-chain data match for the "feeOwedA" and "feeOwedB" fields in the Position account) | ||
* @param destinationWallet - the wallet to deposit tokens into when withdrawing from the position. If null, the WhirlpoolContext wallet is used. | ||
* @param positionWallet - the wallet to that houses the position token. If null, the WhirlpoolContext wallet is used. | ||
* @param ataPayer - wallet that will fund the creation of the new associated token accounts | ||
* @param refresh - set to true to bypass cached on-chain data | ||
* @return the transaction that will collect fees from the position | ||
*/ | ||
collectFees: (updateFeesAndRewards?: boolean, resolveATA?: boolean, destinationWallet?: Address, positionWallet?: Address, ataPayer?: Address, refresh?: boolean) => Promise<TransactionBuilder>; | ||
/** | ||
* Collect rewards from this position | ||
* | ||
* If `positionWallet` is provided, the wallet owners have to sign this transaction. | ||
* | ||
* @param rewardsToCollect - reward mints to collect (omitting this parameter means all rewards will be collected) | ||
* @param updateFeesAndRewards - if true, add instructions to refresh the accumulated fees and rewards data (default to true unless you know that the collect fees quote and on-chain data match for the "feeOwedA" and "feeOwedB" fields in the Position account) | ||
* @param resolveATA - if true, add instructions to create associated token accounts for the reward mints for the destinationWallet if necessary. (RPC call required) | ||
* @param destinationWallet - the wallet to deposit tokens into when withdrawing from the position. If null, the WhirlpoolContext wallet is used. | ||
* @param positionWallet - the wallet to that houses the position token. If null, the WhirlpoolContext wallet is used. | ||
* @param ataPayer - wallet that will fund the creation of the new associated token accounts | ||
* @param refresh - set to true to bypass cached on-chain data | ||
* @return the transaction that will collect fees from the position | ||
*/ | ||
collectRewards: (rewardsToCollect?: Address[], updateFeesAndRewards?: boolean, resolveATA?: boolean, destinationWallet?: Address, positionWallet?: Address, ataPayer?: Address, refresh?: boolean) => Promise<TransactionBuilder>; | ||
} |
{ | ||
"name": "@orca-so/whirlpools-sdk", | ||
"version": "0.6.1", | ||
"version": "0.7.1", | ||
"description": "Typescript SDK to interact with Orca's Whirlpool program.", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
655067
158
14226