UNPKG

@orca-so/whirlpools-sdk

Version:

Typescript SDK to interact with Orca's Whirlpool program.

263 lines 16.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WhirlpoolClientImpl = void 0; const common_sdk_1 = require("@orca-so/common-sdk"); const web3_js_1 = require("@solana/web3.js"); const tiny_invariant_1 = __importDefault(require("tiny-invariant")); const instructions_1 = require("../instructions"); const composites_1 = require("../instructions/composites"); const ix_1 = require("../ix"); const fetcher_1 = require("../network/public/fetcher"); const public_1 = require("../types/public"); const position_builder_util_1 = require("../utils/builder/position-builder-util"); const public_2 = require("../utils/public"); const position_impl_1 = require("./position-impl"); const util_1 = require("./util"); const whirlpool_impl_1 = require("./whirlpool-impl"); const token_extension_util_1 = require("../utils/public/token-extension-util"); const decimal_js_1 = __importDefault(require("decimal.js")); class WhirlpoolClientImpl { ctx; constructor(ctx) { this.ctx = ctx; } getContext() { return this.ctx; } getFetcher() { return this.ctx.fetcher; } async getPool(poolAddress, opts = fetcher_1.PREFER_CACHE) { const account = await this.ctx.fetcher.getPool(poolAddress, opts); if (!account) { throw new Error(`Unable to fetch Whirlpool at address at ${poolAddress}`); } const tokenInfos = await (0, util_1.getTokenMintInfos)(this.ctx.fetcher, account, opts); const vaultInfos = await (0, util_1.getTokenVaultAccountInfos)(this.ctx.fetcher, account, opts); const rewardInfos = await (0, util_1.getRewardInfos)(this.ctx.fetcher, account, opts); return new whirlpool_impl_1.WhirlpoolImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(poolAddress), tokenInfos[0], tokenInfos[1], vaultInfos[0], vaultInfos[1], rewardInfos, account); } async getPools(poolAddresses, opts = fetcher_1.PREFER_CACHE) { const accounts = Array.from((await this.ctx.fetcher.getPools(poolAddresses, opts)).values()).filter((account) => !!account); if (accounts.length !== poolAddresses.length) { throw new Error(`Unable to fetch all Whirlpools at addresses ${poolAddresses}`); } const tokenMints = new Set(); const tokenAccounts = new Set(); accounts.forEach((account) => { tokenMints.add(account.tokenMintA.toBase58()); tokenMints.add(account.tokenMintB.toBase58()); tokenAccounts.add(account.tokenVaultA.toBase58()); tokenAccounts.add(account.tokenVaultB.toBase58()); account.rewardInfos.forEach((rewardInfo) => { if (public_2.PoolUtil.isRewardInitialized(rewardInfo)) { tokenAccounts.add(rewardInfo.vault.toBase58()); } }); }); await this.ctx.fetcher.getMintInfos(Array.from(tokenMints), opts); await this.ctx.fetcher.getTokenInfos(Array.from(tokenAccounts), opts); const whirlpools = []; for (let i = 0; i < accounts.length; i++) { const account = accounts[i]; const poolAddress = poolAddresses[i]; const tokenInfos = await (0, util_1.getTokenMintInfos)(this.ctx.fetcher, account, fetcher_1.PREFER_CACHE); const vaultInfos = await (0, util_1.getTokenVaultAccountInfos)(this.ctx.fetcher, account, fetcher_1.PREFER_CACHE); const rewardInfos = await (0, util_1.getRewardInfos)(this.ctx.fetcher, account, fetcher_1.PREFER_CACHE); whirlpools.push(new whirlpool_impl_1.WhirlpoolImpl(this.ctx, common_sdk_1.AddressUtil.toPubKey(poolAddress), tokenInfos[0], tokenInfos[1], vaultInfos[0], vaultInfos[1], rewardInfos, account)); } return whirlpools; } async getPosition(positionAddress, opts = fetcher_1.PREFER_CACHE) { const account = await this.ctx.fetcher.getPosition(positionAddress, opts); if (!account) { throw new Error(`Unable to fetch Position at address at ${positionAddress}`); } const whirlAccount = await this.ctx.fetcher.getPool(account.whirlpool, opts); if (!whirlAccount) { throw new Error(`Unable to fetch Whirlpool for Position at address at ${positionAddress}`); } const positionMint = await this.ctx.fetcher.getMintInfo(account.positionMint, opts); if (!positionMint) { throw new Error(`Unable to fetch Mint for Position at address at ${positionAddress}`); } const [lowerTickArray, upperTickArray] = await (0, position_builder_util_1.getTickArrayDataForPosition)(this.ctx, account, whirlAccount, opts); 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, positionMint.tokenProgram); } async getPositions(positionAddresses, opts = fetcher_1.PREFER_CACHE) { const positions = Array.from((await this.ctx.fetcher.getPositions(positionAddresses, opts)).values()); const whirlpoolAddrs = positions .map((position) => position?.whirlpool.toBase58()) .flatMap((x) => (!!x ? x : [])); await this.ctx.fetcher.getPools(whirlpoolAddrs, opts); const positionMintAddrs = positions .map((position) => position?.positionMint.toBase58()) .flatMap((x) => (!!x ? x : [])); await this.ctx.fetcher.getMintInfos(positionMintAddrs, opts); const tickArrayAddresses = new Set(); await Promise.all(positions.map(async (pos) => { if (pos) { const pool = await this.ctx.fetcher.getPool(pos.whirlpool, fetcher_1.PREFER_CACHE); if (pool) { const lowerTickArrayPda = public_2.PDAUtil.getTickArrayFromTickIndex(pos.tickLowerIndex, pool.tickSpacing, pos.whirlpool, this.ctx.program.programId).publicKey; const upperTickArrayPda = public_2.PDAUtil.getTickArrayFromTickIndex(pos.tickUpperIndex, pool.tickSpacing, pos.whirlpool, this.ctx.program.programId).publicKey; tickArrayAddresses.add(lowerTickArrayPda.toBase58()); tickArrayAddresses.add(upperTickArrayPda.toBase58()); } } })); await this.ctx.fetcher.getTickArrays(Array.from(tickArrayAddresses), fetcher_1.IGNORE_CACHE); const results = await Promise.all(positionAddresses.map(async (pos) => { try { const position = await this.getPosition(pos, fetcher_1.PREFER_CACHE); return [pos, position]; } catch { return [pos, null]; } })); return Object.fromEntries(results); } async createSplashPool(whirlpoolsConfig, tokenMintA, tokenMintB, initialPrice = new decimal_js_1.default(1), funder, opts = fetcher_1.PREFER_CACHE) { const correctTokenOrder = public_2.PoolUtil.orderMints(tokenMintA, tokenMintB).map((addr) => addr.toString()); (0, tiny_invariant_1.default)(correctTokenOrder[0] === tokenMintA.toString(), "Token order needs to be flipped to match the canonical ordering (i.e. sorted on the byte repr. of the mint pubkeys)"); const mintInfos = await this.getFetcher().getMintInfos([tokenMintA, tokenMintB], opts); (0, tiny_invariant_1.default)(mintInfos.size === 2, "At least one of the token mints cannot be found."); const tokenExtensionCtx = { ...token_extension_util_1.NO_TOKEN_EXTENSION_CONTEXT, tokenMintWithProgramA: mintInfos.get(tokenMintA.toString()), tokenMintWithProgramB: mintInfos.get(tokenMintB.toString()), }; whirlpoolsConfig = common_sdk_1.AddressUtil.toPubKey(whirlpoolsConfig); const feeTierKey = public_2.PDAUtil.getFeeTier(this.ctx.program.programId, whirlpoolsConfig, public_1.SPLASH_POOL_TICK_SPACING).publicKey; const whirlpoolPda = public_2.PDAUtil.getWhirlpool(this.ctx.program.programId, whirlpoolsConfig, new web3_js_1.PublicKey(tokenMintA), new web3_js_1.PublicKey(tokenMintB), public_1.SPLASH_POOL_TICK_SPACING); const tokenDecimalsA = mintInfos.get(tokenMintA.toString())?.decimals ?? 0; const tokenDecimalsB = mintInfos.get(tokenMintB.toString())?.decimals ?? 0; const initSqrtPrice = public_2.PriceMath.priceToSqrtPriceX64(initialPrice, tokenDecimalsA, tokenDecimalsB); const tokenVaultAKeypair = web3_js_1.Keypair.generate(); const tokenVaultBKeypair = web3_js_1.Keypair.generate(); const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet, this.ctx.txBuilderOpts); const tokenBadgeA = public_2.PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, common_sdk_1.AddressUtil.toPubKey(tokenMintA)).publicKey; const tokenBadgeB = public_2.PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, common_sdk_1.AddressUtil.toPubKey(tokenMintB)).publicKey; const baseParams = { initSqrtPrice, whirlpoolsConfig, whirlpoolPda, tokenMintA: new web3_js_1.PublicKey(tokenMintA), tokenMintB: new web3_js_1.PublicKey(tokenMintB), tokenVaultAKeypair, tokenVaultBKeypair, feeTierKey, tickSpacing: public_1.SPLASH_POOL_TICK_SPACING, funder: new web3_js_1.PublicKey(funder), }; const initPoolIx = !token_extension_util_1.TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx) ? ix_1.WhirlpoolIx.initializePoolIx(this.ctx.program, baseParams) : ix_1.WhirlpoolIx.initializePoolV2Ix(this.ctx.program, { ...baseParams, tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, tokenBadgeA, tokenBadgeB, }); txBuilder.addInstruction(initPoolIx); const [startTickIndex, endTickIndex] = public_2.TickUtil.getFullRangeTickIndex(public_1.SPLASH_POOL_TICK_SPACING); const startInitializableTickIndex = public_2.TickUtil.getStartTickIndex(startTickIndex, public_1.SPLASH_POOL_TICK_SPACING); const endInitializableTickIndex = public_2.TickUtil.getStartTickIndex(endTickIndex, public_1.SPLASH_POOL_TICK_SPACING); const startTickArrayPda = public_2.PDAUtil.getTickArray(this.ctx.program.programId, whirlpoolPda.publicKey, startInitializableTickIndex); const endTickArrayPda = public_2.PDAUtil.getTickArray(this.ctx.program.programId, whirlpoolPda.publicKey, endInitializableTickIndex); txBuilder.addInstruction((0, instructions_1.initDynamicTickArrayIx)(this.ctx.program, { startTick: startInitializableTickIndex, tickArrayPda: startTickArrayPda, whirlpool: whirlpoolPda.publicKey, funder: common_sdk_1.AddressUtil.toPubKey(funder), })); txBuilder.addInstruction((0, instructions_1.initDynamicTickArrayIx)(this.ctx.program, { startTick: endInitializableTickIndex, tickArrayPda: endTickArrayPda, whirlpool: whirlpoolPda.publicKey, funder: common_sdk_1.AddressUtil.toPubKey(funder), })); return { poolKey: whirlpoolPda.publicKey, tx: txBuilder, }; } async createPool(whirlpoolsConfig, tokenMintA, tokenMintB, tickSpacing, initialTick, funder, opts = fetcher_1.PREFER_CACHE) { (0, tiny_invariant_1.default)(public_2.TickUtil.checkTickInBounds(initialTick), "initialTick is out of bounds."); const correctTokenOrder = public_2.PoolUtil.orderMints(tokenMintA, tokenMintB).map((addr) => addr.toString()); (0, tiny_invariant_1.default)(correctTokenOrder[0] === tokenMintA.toString(), "Token order needs to be flipped to match the canonical ordering (i.e. sorted on the byte repr. of the mint pubkeys)"); const mintInfos = await this.ctx.fetcher.getMintInfos([tokenMintA, tokenMintB], opts); const tokenExtensionCtx = { ...token_extension_util_1.NO_TOKEN_EXTENSION_CONTEXT, tokenMintWithProgramA: mintInfos.get(tokenMintA.toString()), tokenMintWithProgramB: mintInfos.get(tokenMintB.toString()), }; whirlpoolsConfig = common_sdk_1.AddressUtil.toPubKey(whirlpoolsConfig); const feeTierKey = public_2.PDAUtil.getFeeTier(this.ctx.program.programId, whirlpoolsConfig, tickSpacing).publicKey; const initSqrtPrice = public_2.PriceMath.tickIndexToSqrtPriceX64(initialTick); const tokenVaultAKeypair = web3_js_1.Keypair.generate(); const tokenVaultBKeypair = web3_js_1.Keypair.generate(); const whirlpoolPda = public_2.PDAUtil.getWhirlpool(this.ctx.program.programId, whirlpoolsConfig, new web3_js_1.PublicKey(tokenMintA), new web3_js_1.PublicKey(tokenMintB), tickSpacing); const feeTier = await this.ctx.fetcher.getFeeTier(feeTierKey, opts); (0, tiny_invariant_1.default)(!!feeTier, `Fee tier for ${tickSpacing} doesn't exist`); const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet, this.ctx.txBuilderOpts); const tokenBadgeA = public_2.PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, common_sdk_1.AddressUtil.toPubKey(tokenMintA)).publicKey; const tokenBadgeB = public_2.PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, common_sdk_1.AddressUtil.toPubKey(tokenMintB)).publicKey; const baseParams = { initSqrtPrice, whirlpoolsConfig, whirlpoolPda, tokenMintA: new web3_js_1.PublicKey(tokenMintA), tokenMintB: new web3_js_1.PublicKey(tokenMintB), tokenVaultAKeypair, tokenVaultBKeypair, feeTierKey, tickSpacing, funder: new web3_js_1.PublicKey(funder), }; const initPoolIx = !token_extension_util_1.TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx) ? ix_1.WhirlpoolIx.initializePoolIx(this.ctx.program, baseParams) : ix_1.WhirlpoolIx.initializePoolV2Ix(this.ctx.program, { ...baseParams, tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, tokenBadgeA, tokenBadgeB, }); const initialTickArrayStartTick = public_2.TickUtil.getStartTickIndex(initialTick, tickSpacing); const initialTickArrayPda = public_2.PDAUtil.getTickArray(this.ctx.program.programId, whirlpoolPda.publicKey, initialTickArrayStartTick); txBuilder.addInstruction(initPoolIx); txBuilder.addInstruction((0, instructions_1.initDynamicTickArrayIx)(this.ctx.program, { startTick: initialTickArrayStartTick, tickArrayPda: initialTickArrayPda, whirlpool: whirlpoolPda.publicKey, funder: common_sdk_1.AddressUtil.toPubKey(funder), })); return { poolKey: whirlpoolPda.publicKey, tx: txBuilder, }; } async collectFeesAndRewardsForPositions(positionAddresses, opts) { const walletKey = this.ctx.wallet.publicKey; return (0, composites_1.collectAllForPositionAddressesTxns)(this.ctx, { positions: positionAddresses, receiver: walletKey, positionAuthority: walletKey, positionOwner: walletKey, payer: walletKey, }, opts); } async collectProtocolFeesForPools(poolAddresses) { return (0, composites_1.collectProtocolFees)(this.ctx, poolAddresses); } } exports.WhirlpoolClientImpl = WhirlpoolClientImpl; //# sourceMappingURL=whirlpool-client-impl.js.map